参考书《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);
        }
      });
    }

ES6学习笔记(11)----Proxy的更多相关文章

  1. es6学习笔记-class之一概念

    前段时间复习了面向对象这一部分,其中提到在es6之前,Javasript是没有类的概念的,只从es6之后出现了类的概念和继承.于是乎,花时间学习一下class. 简介 JavaScript 语言中,生 ...

  2. ES6学习笔记之变量的解构赋值

    变量的解构赋值 ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构. 数组的解构赋值 以前,为变量赋值,只能直接指定值: 1 2 3 var a = 1; var b = 2; ...

  3. Ext.Net学习笔记11:Ext.Net GridPanel的用法

    Ext.Net学习笔记11:Ext.Net GridPanel的用法 GridPanel是用来显示数据的表格,与ASP.NET中的GridView类似. GridPanel用法 直接看代码: < ...

  4. SQL反模式学习笔记11 限定列的有效值

    目标:限定列的有效值,将一列的有效字段值约束在一个固定的集合中.类似于数据字典. 反模式:在列定义上指定可选值 1. 对某一列定义一个检查约束项,这个约束不允许往列中插入或者更新任何会导致约束失败的值 ...

  5. golang学习笔记11 golang要用jetbrain的golang这个IDE工具开发才好

    golang学习笔记11   golang要用jetbrain的golang这个IDE工具开发才好  jetbrain家的全套ide都很好用,一定要dark背景风格才装B   从File-->s ...

  6. ES6学习笔记<五> Module的操作——import、export、as

    import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...

  7. ES6学习笔记<四> default、rest、Multi-line Strings

    default 参数默认值 在实际开发 有时需要给一些参数默认值. 在ES6之前一般都这么处理参数默认值 function add(val_1,val_2){ val_1 = val_1 || 10; ...

  8. ES6学习笔记<三> 生成器函数与yield

    为什么要把这个内容拿出来单独做一篇学习笔记? 生成器函数比较重要,相对不是很容易理解,单独做一篇笔记详细聊一聊生成器函数. 标题为什么是生成器函数与yield? 生成器函数类似其他服务器端语音中的接口 ...

  9. ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring

    接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...

随机推荐

  1. Pyhton:List build-in function

    列表是Python中的可迭代对象之一,在讲列表的内建函数之前我们可以自己在IDE上看看都有那些内建函数,我们可以在pycharm中使用代码及其运行结果如下: print(dir(list)) ['__ ...

  2. HUST - 1010 The Minimum Length(最小循环节)

    1.赤裸裸的最小循环节 2. 3. #include<iostream> #include<stdio.h> #include<string.h> using na ...

  3. unity-Fatal Error GC-GetThreadContext Failed

    这几次在使用unity5.3打windows包后,运行x.exe不久总是会弹出"fatal error GC: GetThreadContext Failed"的错误.到网上查了, ...

  4. codeforces 672B B. Different is Good(水题)

    题目链接: B. Different is Good time limit per test 2 seconds memory limit per test 256 megabytes input s ...

  5. [USACO 2016Dec] Team Building

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=4742 [算法] 动态规划 用Fi,j,k表示约翰的前i头牛和保罗的前j头牛匹配 , ...

  6. fastText(三):微博短文本下fastText的应用(二)

    上一篇讲到,fastText在训练数据中过拟合的问题.接下来将介绍一些提高fastText泛化能力的尝试. 模型泛化使用过fastText的人,往往会被它的很多特性征服,例如训练速度.兼具word e ...

  7. nodejs 打造 多人对战游戏服务器(初级入门)

    使用socket.set 和 socket.get 在存取玩家信息 百牛信息技术bainiu.ltd整理发布于博客园 socket.get('playerinfo', function (err, p ...

  8. mac上python3安装HTMLTestRunner

    下载支持python3版本的HTMLTestRunner.py文件后,拷贝到python3的lib目录下 在终端输入如下命令: 将HTMLTestRunner.py文件拷贝到如下目录后,pycharm ...

  9. 多线程-threading模块2

    从上面例子中发现线程的创建是颇为麻烦的,每创建一个线程都需要创建一个 t(t1.t2....),如果创建的线程较多时这样极其不方便.下面对通过例子进行改进:   #coding:utf-8 impor ...

  10. 用 SDL2 加载PNG平铺背景并显示前景

    上一篇中加载的是BMP,这次可以引用 SDL2_image.lib,加载更多格式的图像. LoadImage函数做了改动,区别在于不用将surface转换成texture了. 环境:SDL2 + VC ...