zoukankan      html  css  js  c++  java
  • ES6学习笔记(11)----Proxy

    参考书《ECMAScript 6入门》
    http://es6.ruanyifeng.com/

    Proxy
    1.概述
        Proxy可以用来修改对象的默认操作
        let obj = {name : "test"};

        obj.name = "test";
        obj['name'] = "test";
        这两种取值操作相当于调用了obj的内部默认get操作
        let obj = {
                    name : "test",
                    get(){
                      return "123"
                    }
            };
        let obj = {
                    name : "test",
                    get : function(){
                      return "123"
                    }
            };
        obj.name = "test";
        obj['name'] = "test";
        obj.get();//"123"
        //由此可见,通过在obj内部定义一个名字为get的方法并不能改变obj内部默认的get行为,而proxy可以做到这一点
        //new Proxy(target,handler);
        //target是目标处理对象,handler中定义要处理的操作
        let obj = {
                    name : "test",
                    get(){
                      return "456"
                    }
            };
        let obj2 = new Proxy(obj,{
           get(){
             return "789";
           }
        });
        obj2.name = "789";
        obj2['name'] = "789";
        //如果handle没有设置任何拦截行为,new Proxy(target)就通向原对象
        let obj = {name : "test"};
        let proxy = new Proxy(obj,{});
        proxy.name = "proxy test";
        obj.name // "proxy test" 相当于浅拷贝
        obj.name = "obj test";
        proxy.name //"obj test" 相当于浅拷贝

        let handler = {
          get : function(){
             return "this called get";
          },
          apply : function(){
             return "this called apply";
          },
          construct : function(){
             return {construct : "this called construct"};
          }
        }

        let obj = new Proxy(function(){
          return "this is target";
        },handler);

        obj();// "this called apply" obj()的默认行为是调用apply操作
        new obj();//{construct: "this called construct"} new obj()的默认行为是调用construct操作
        obj.prototype //"this called get" 点运算符的默认行为是调用get操作
        obj['name'] // "this called get" 方括号运算的默认行为是调用get操作

    2. proxy支持的拦截操作
    (1)get(target,propkey,receiver):拦截对象属性的读取。target是目标对象,propkey是属性名,receiver指当前的proxy实例
        数组reduce的用法
        array.reduce(function(previousValue,currentValue,currentIndex,array){});
        如果一个属性是不可配置的(configurable)和不可写的(written),则不能使用proxy获取该属性的值
        let obj = Object.defineProperty({},'color',{value:123,writable:false,configurable:false});
        obj.color // 123
        let proxy = new Proxy(obj,{
           get : function(target,propkey){
             return "color";
           }
        });
        proxy.color //Uncaught TypeError: 'get' on proxy: property 'color' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '123' but got 'color')
    (2)set(target,propkey,propValue,receiver):拦截对象属性的设置。
        target是目标对象,propkey是属性名,propValue是属性值,receiver是当前操作行为所指的对象,一般是Proxy实例本身
        利用set拦截方式达到双向数据绑定的效果
        HTML代码举例
        <!DOCTYPE html>
        <html>
            <head>
                <meta charset="UTF-8">
                <title></title>
                <script src="../js/jquery-1.11.3.min.js"></script>
            </head>
            <body>
                <input type="text"/>
                <div id="test"></div>
            </body>
            <script>
                var proxy = new Proxy({
                    data: "red"
                }, {
                    set: function(target, propkey, propV, proxy) {
                        target[propkey] = propV;
                        $("#test").text(propV);
                        $('input').val(propV);
                        return true;
                    }
                });
                $('input').on('keyup',function(){
                    proxy.data = $('input').val();
                });
            </script>
        </html>
        当一个属性设置为不可写和不可设置时,set方法将不起作用
        let obj = Object.defineProperty({},'color',{value:"red",configurable:false,writable:false});
        let proxy = new Proxy(obj,{
            set : function(target,pk,pv,receiver){
               target[pk] = "test";
               return true;
            }
        });
        proxy.color = "balck";//VM583:1 Uncaught TypeError: 'set' on proxy: trap returned truish for property 'color' which exists in the proxy target as a non-configurable and non-writable data property with a different value
        proxy.name = "123"; //"123"
    (3).has(target,propkey):拦截propkey in proxy的遍历操作,返回布尔值
        let obj = {color:"red",testN : "name"}
        let proxy = new Proxy(obj,{
           has(target,pk){
             console.log(pk);
             if(pk === 'testN'){
                return false;
             }
             return pk in target;
           }
        });
        'color' in obj //true
        'testN' in obj //true

        'color' in proxy //true
        'testN' in proxy //false

        has不能拦截设置为禁止扩展或者设置为禁止配置的对象的hasProperty操作
        let obj = {color:"test"};
        Object.preventExtensions(obj);
        let proxy = new Proxy(obj,{
           has(){
              return false;
           }
        });
        color in proxy //Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'color' but the proxy target is not extensible
        let obj = Object.defineProperty({},'color',{value:123,configurable:false});
        let proxy = new Proxy(obj,{
          has(){
            return false;
          }
        });
        'color' in proxy //Uncaught TypeError: 'has' on proxy: trap returned falsish for property 'color' which exists in the proxy target as non-configurable
        has不会拦截for循环遍历中的in运算
        let obj = {name : "test",color : "red"}
        let handler = {
            has(target,pk){
              return "this called has"
            }
        }
        let proxy = new Proxy(obj,handler);
        for(let o in proxy){
          console.log(o + " : " + proxy[o]);
        }
        //name : test
        //color : red

    (4).deleteProperty(target,propkey):拦截delete proxy[propkey]的操作,返回一个boolean值。target指目标对象,propkey指属性名
        let obj = Object.defineProperties({},{
           "name" : {
             value : "obj"
           },
           "speed" : {
             value : 23,
             writable : false
           },
           "type" : {
             value : "abc"
           },
           "color" : {
             value : "test",
             configurable : false
           },
           "size" : {
             value : 11,
             configurable : true,
             writable : false
           }
        });
        Object.getOwnPropertyDescriptors(obj);
        color:{value: "test", writable: false, enumerable: false, configurable: false}
        name:{value: "obj", writable: false, enumerable: false, configurable: false}
        size:{value: 11, writable: false, enumerable: false, configurable: true}
        speed:{value: 23, writable: false, enumerable: false, configurable: false}
        type:{value: "abc", writable: false, enumerable: false, configurable: false}

        let handler = {
          deleteProperty(target, pk){
            if("name" === pk){
              return false;
            }
            return true;
          }
        }

        let proxy = new Proxy(obj,handler);
        拦截删除name属性
        delete proxy.name //false
        color属性是不可配置属性,不能删除
        delete proxy.color //VM520:1 Uncaught TypeError: 'deleteProperty' on proxy: trap returned truish for property 'color' which is non-configurable in the proxy target
        size是可配置但是不可写属性,能够删除
        delete proxy.size //true
        speed是不可写属性,不能删除
        delete proxy.speed //VM638:1 Uncaught TypeError: 'deleteProperty' on proxy: trap returned truish for property 'speed' which is non-configurable in the proxy target
        type属性定义时默认是不可配置,不可写,不能删除
        delete proxy.type
        proxy //Proxy {name: "obj", speed: 23, type: "abc", color: "test", size: 11}

    (5).ownKeys(target):拦截对象自身读取属性的操作,如:
        Object.getOwnPropertyNames(obj):返回一个数组,包含对象自身的所有属性的键名,这个所有属性包含不可枚举属性但是不包含symbol属性的键名
        Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有Symbol属性的键名
        Object.keys(obj):返回对象自身的所有可枚举属性的键名
        ownKeys拦截操作返回一个数组,且此数组的键名必须是字符串或者Symbol类型,否则会报错;
        当目标对象有不能配置的属性时,ownKeys()拦截操作所返回的数组则必须包含目标对象的此属性,否则会报错;
        当目标对象不可扩展时,ownKeys()拦截操作必须返回目标对象所有的属性,否则会报错;
        let s = Symbol("for s");
        let obj = {
           "name":"asd",
           "number" : 7,
           "cat" : {
              color : "black",
              size : 10
           },
           [s] : "symbol s"//Symbol键名的属性
        }
        Object.defineProperties(obj,{
               "nonEm":{//不可枚举属性
                  value : "test nonEm",
                  enumerable : false,
                  configurable : true,
                  writable : true
               },
               "nonCn":{//不可配置属性
                  value : "test nonCn",
                  enumerable : true,
                  configurable : false,
                  writable : true
               }
            }
        );
        object.keys(obj);//["name", "number", "cat", "nonCn"] 拿不到Symbol类型为键名和不可枚举的
        Object.getOwnPropertyNames(obj);// ["name", "number", "cat", "nonEm", "nonCn"] 拿不到Symbol类型为键名的
        Object.getOwnPropertySymbols(obj);// [Symbol(for s)] 只获取Symbol类型的键名
        
        let handler = {
            ownKeys(target){
               return ['nonCn','cat','abc'];//必须包含'nonCn'此不可配置属性的键名
            }
        }
        
        let proxy = new Proxy(obj,handler);
        Object.keys(proxy);//["nonCn", "cat"] 'abc' 对应的属性不存在,所以不返回
        Object.getOwnPropertyNames(proxy);//["nonCn", "cat", "abc"]
        Object.getOwnPropertySymbols(proxy);//[]
        
        Object.preventExtensions(obj);//目标对象设置了不可扩展,则ownKeys拦截必须返回所有键名,否则报错
        let proxy1 = new Proxy(obj,handler);
        Object.keys(proxy1);//Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'name'
        
        let handler2 = {
            ownKeys(){
              return ["name", "number", "cat", "nonCn","nonEm",s]//因为目标对象不可扩展,因此不能返回目标对象不存在的键名
            }
        }
        let proxy2 = new Proxy(obj,handler2);
        Object.keys(proxy2);//["name", "number", "cat", "nonCn"]
        Object.getOwnPropertyNames(proxy4);//["name", "number", "cat", "nonCn", "nonEm"]
        Object.getOwnPropertySymbols(proxy4);//[Symbol(for s)]

    (6).getOwnPropertyDescriptor(target,propkey):拦截Object.getOwnPropertyDescriptor(proxy,propkey),返回属性的描述对象或者undefined。
        let obj = {color : "red"};
        let handler = {
          getOwnPropertyDescriptor(target,key){
            return false;
          }
        }
        let proxy = new Proxy(obj,handler);
        proxy.color //'red'
        Object.getOwnPropertyDescriptor(proxy,'color');//VM2595:1 Uncaught TypeError: 'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined for property 'color'

    (7)defineProperty(target,propkey,descriptor):拦截Object.defineProperty,Object.defineProperties,返回一个boolean值。
        let proxy = new Proxy({},{
          defineProperty(target,pk,descriptor){
            console.log(target);//{}
            console.log(pk);//name
            console.log(descriptor);//{value: "123", writable: true, enumerable: true, configurable: true}
            return false;
          }
        });
        proxy.name = "123";//"123" 没有报错
        proxy //Proxy {}
        proxy.name //undefined 没有成功的定义属性值,拦截行为生效
        如果目标对象的某个属性不可写(writable = false)或者不可配置(configurable = false),则defineProperty方法不能这两个设置
        let obj = Object.defineProperty({},'color',{
          value : "red",
          writable : false,
          configurable : false
        });
        Object.preventExtensions(obj);
        let proxy = new Proxy(obj,{
          defineProperty(target,pk,descriptor){
            target[pk]['confirgurable'] = true;
            return true;
          }
        });

        proxy.color //'red'
        Object.getOwnPropertyDescriptor(proxy,'color')['configurable'] //false
        Object.getOwnPropertyDescriptor(proxy,'color')['configurable'] = true //true
        Object.getOwnPropertyDescriptor(proxy,'color')['configurable'] //false
        //如果目标对象设置为禁止扩展,则不允许为proxy实例添加新的属性
        proxy.name = "123" //Uncaught TypeError: Cannot set property 'confirgurable' of undefined

    (8)preventExtensions(target):拦截Object.preventExtensions(proxy),返回值必须在实际意义上与Object.isExtensible(target)一致
        let obj = {}
        Object.isExtensible(obj);//true
        let handler = {
          preventExtensions:function(target){
             console.log("other action");
             Object.preventExtensions(target);
             return true;
          }
        }
        let proxy = new Proxy(obj,handler);
        Object.preventExtensions(proxy);
        //"other action"
        //proxy{} 最新浏览器返回的是对象不是boolean值


        var p = new Proxy({}, {
          preventExtensions: function(target) {
            return true;
          }
        });
        Object.preventExtensions(p);//报错 拦截后Object.preventExtensions与Object.isExtensible(target)--true不一致,所以报错

    (9)getPrototypeOf(target):拦截获取对象原型的操作。如:Object.prototype.__proto__,Object.prototype.isPrototypeOf(),Object.getPrototypeOf(),Reflect.getPrototypeOf(),instanceOf
                           返回值必须是对象或者null.
        let arr = [1,2,3,4,5];
        let handler = {
          getPrototypeOf(target){
            return {name : "test"};
          }
        }
        let proxy = new Proxy(arr,handler);
        Object.getPrototypeOf(proxy)   //{name: "test"}
        Reflect.getPrototypeOf(proxy) //{name: "test"}
        proxy.__proto__ //{name: "test"}


        let arr = {name : "asd"};
        Object.preventExtensions(arr);
        let handler = {
          getPrototypeOf(target){
            return {color : "black"};
          }
        }
        let proxy = new Proxy(arr,handler);
        Object.getPrototypeOf(proxy)   //Uncaught TypeError: 'getPrototypeOf' on proxy: proxy target is non-extensible but the trap did not return its actual prototype
        Reflect.getPrototypeOf(proxy) //Uncaught TypeError: 'getPrototypeOf' on proxy: proxy target is non-extensible but the trap did not return its actual prototype
        proxy.__proto__ //Uncaught TypeError: 'getPrototypeOf' on proxy: proxy target is non-extensible but the trap did not return its actual prototype

    (10)isExtensible(target):拦截Object.isExtensible(proxy),必须返回一个boolean值且与目标对象调用Object.isExtensible(target)一致。
        let obj = {};
        Object.isExtensible(obj) //true
        let handler = {
          isExtensible(target){
            Object.preventExtensions(target);//拦截操作
            return Object.isExtensible(target);
          }
        }
        let proxy = new Proxy(obj,handler);
        Object.isExtensible(proxy);//false
        Object.isExtensible(obj);//false

    (11)setPrototypeOf(target,propkey):拦截Object.setPrototypeOf(target,proto),返回一个布尔值。
        let obj = {};
        let obj1 = {name : "test"};
        Object.getPrototypeOf(obj); //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
        let handler = {
          setPrototypeOf(target){
             Object.setPrototypeOf(target,obj1);
             return true;
          }
        }
        let proxy = new Proxy(obj,handler);
        Object.setPrototypeOf(proxy,null);
        Object.getPrototypeOf(proxy);//{name: "test"}

    (12)apply(target,objects,arguments):拦截proxy实例作为函数调用的操作,
        target代表目标对象,objects代表目标对象的上下文对象,arguments代表参数数组
        如Proxy(...arguments),Proxy.apply(obejct,...arguments),Proxy.call(...),直接使用Reflect.call()也会被拦截
        let f1 = function add(a,b){
            console.log(a+b);
        }
        let proxy = new Proxy(f1,{
           apply : function(target,context,arguments){
              return arguments + " this is apply";
           }
        });
        proxy(1,2) //"1,2 this is apply";
        proxy.apply(null,[1,2]) //"1,2 this is apply"
        proxy.call(null,1,2) //"1,2 this is apply"
        Reflect.apply(proxy,null,[1,2]) //"1,2 this is apply"

    (13)construct(target,arguments):拦截Proxy实例作为构造器调用的操作,如new Proxy(...arguments)。
                                target是目标对象,arguments是构建函数的参数对象。
        construct返回的必须是一个对象,否则会报错。
        let handler1 = {
          construct : function(target,arguments){
            return {value : arguments*22};
          }
        }
        let handler2 = {
          construct : function(target,arguments){
            return arguments*22;
          }
        }
        let p1 = new Proxy(function(){},handler1);
        let p2 = new Proxy(function(){},handler2);
        new p1(12);//{value: 264}
        new p2(12);//VM460:1 Uncaught TypeError: 'construct' on proxy: trap returned non-object ('264')
    3.Proxy.revocable():用于取消Proxy实例,执行此函数后,再访问被取消的Proxy实例的属性就会报错。
        使用场景:目标对象不能直接访问,必须通过代理访问,访问结束就收回代理权,不能再访问
        let {proxy,revoke} = Proxy.revocable({},{
          get(){
            return {color : "red"};
          }
        });
        proxy.name //{color : "red"}
        Proxy.revoke(proxy);
        proxy //Proxy {}
        proxy.name //Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked

    4.this问题:在Proxy代理的情况下,目标对象this的关键字会指向Proxy 代理
        let n = new Number();
        n.toFixed(2)//'0.00'
        let proxy = new Proxy(n,{});
        proxy.toFixed(2); //Uncaught TypeError: Number.prototype.toFixed requires that 'this' be a Number
    5.实现web服务的客户端
        function createWebService(baseUrl) {
          return new Proxy({}, {
            get(target, propKey, receiver) {
              return () => httpGet(baseUrl+'/' + propKey);
            }
          });
        }

  • 相关阅读:
    Spring事务
    org.apache.catalina.webresources.Cache.getResource Unable to add the resource
    CentOS7下zip解压和unzip压缩文件
    通过Maven插件发布JaveEE项目到tomcat下
    MYSQL5.7版本sql_mode=only_full_group_by问题
    CentOS下安装Tomcat
    MYSQL57密码策略修改
    CentOS下安装mysql5.7和mysql8.x
    Linux下使用systemctl命令
    076-PHP数组修改元素值
  • 原文地址:https://www.cnblogs.com/carolddz/p/8880569.html
Copyright © 2011-2022 走看看