ES6 Proxy入门

Proxy

 

基础知识

  1. 概念:用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种元编程,即对编程语言进行编程.可以理解为,在目标对象之前架设一层拦截,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写.Proxy的原义是代理,在这里表示由它来代理某些操作,可以译为代理器
  2. 一个简单的例子:
var obj = new Proxy({}, {
	get: function (target, key, receiver) {
		console.log(`getting ${key}!`);
		return Reflect.get(target, key, receiver);
	},
	set: function (target, key, value, receiver) {
		console.log(`setting ${key}!`);
		return Reflect.set(target, key, value, receiver);
	}
});
obj.count = 1
// setting count!
++obj.count
// getting count!
// setting count!
// 2
//上述代码说明,proxy实际上重载了点运算符,即用自己的定义覆盖了语言的原始定义

   3. ES6原生提供proxy构造函数,用来生成proxy实例:var proxy=new Proxy(target,handler); target参数表示所要代理的目标对象,handler参数也是一个对象,用来定制拦截行为.注意:要使proxy起作用,必须针对proxy实例进行操作,而不是目标对象.如果handlerm没有设置任何拦截,就等于直接通向原对象

Proxy支持的拦截操作

  1. get(target,propKey,receiver):用于拦截对象属性的读取,比如proxy.foo和proxy['foo'],最后一个receiver是可选参数,详情参见Reflect.get的部分.这个target参数指的是new Proxy(target,handler)中的第一个参数
  2. set(target,propKey,value,receiver):拦截对象属性的设置,比如proxy.foo=v或proxy['foo']=v,返回一个布尔值
    //假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用proxy保证age属性值符合要求
    let validator={
    	set:function(target,key,value){
    		if(key=='age'){
    			if(!Number.isInteger(value)){
    				throw new TypeError('The age is not a integer');
    			}
    			if(value>200){
    			    throw new RangeError('The age seems invalid');
    			}
    			target[key]=value;
    		}
    	}
    }
    let person=new Proxy({},validator);
    person.age=100;
    person.age        ==>100
    person.age='abc'  ==>报错
    person.age=300    ==>报错

     

  3. has(target,propKey):拦截propKey in proxy 的操作,返回一个布尔值.即判断对象是否具有某个属性时,这个方法会生效,典型的操作就是in操作符.如果原对象不可配置或禁止扩展,has拦截会报错
    var handler={
    	has:function(target,key){
    		if(key[0]==='_'){
    			return false;
    		}
    		return key in target;
    	}
    };
    var target={_prop:'foo',prop:'foo'};
    var proxy=new Proxy(target,handler);
    '_prop' in proxy // false

     

  4. deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值.目标对象自身的不可配置属性,不能被deleteProperty方法删除,否则报错
  5. ownKeys(target):拦截`Object.getOwnPropertyNames(proxy)`、 `Object.getOwnPropertySymbols(proxy)` `Object.keys(proxy)` ,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而 Object.keys() 的返回结果仅包括目标对象自身的可遍历属性(也就是不会返回目标对象上不存在的属性,属性名为Symbol值,不可遍历的属性)
    var obj = {};
    Object.defineProperty(obj, 'a', {
    	configurable: false,
    	enumerable: true,
    	value: 10 }
    );
    var p = new Proxy(obj, {
    	ownKeys: function(target) {
    	return ['b'];	//因为obj 的a属性是不可配置的,因此这里的返回值必须包含a,否则会报错
    						}
    });
    Object.getOwnPropertyNames(p)
    // Uncaught TypeError: 'ownKeys' on proxy: trap result did not include 'a'

     

  6. getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象或undefined
  7. defineProperty(target, propKey, propDesc)拦截 `Object.defineProperty(proxy, propKey,propDesc)` 、 `Object.defineProperties(proxy, propDescs) `,返回一个布尔值.如果目标对象不可扩展,则`defineProperty`不能增加目标对象上不存在的属性,否则会报错.此外,如果目标对象的某个属性不可写或不可配置,则defineProperty方法不得改变这两个设置
    var handler = {
    	defineProperty (target, key, descriptor) {
    		return false;	//返回false,因此对象添加新属性会抛出错误
    	}
    };
    var target = {};
    var proxy = new Proxy(target, handler);
    proxy.foo = 'bar'
    // TypeError: proxy defineProperty handler returned false for property '"foo"'

     

  8. preventExtensions(target):拦截 `Object.preventExtensions(proxy)` ,返回一个布尔值,否则会被自动转为布尔值
  9. getPrototypeOf(target):拦截 `Object.getPrototypeOf(proxy)` ,返回一个对象
  10. isExtensible(target):拦截 `Object.isExtensible(proxy)` ,返回一个布尔值.该方法只能返回布尔值,否则返回值会被自动转为布尔值.这个方法有个强限制,它的返回值必须与目标对象的isExtensible属性保持一致,否则抛出错误
  11. setPrototypeOf(target, proto):拦截` Object.setPrototypeOf(proxy, proto)` ,返回一个布尔值
  12. 如果目标对象是函数,那么还有两种额外操作可以拦截
  13. apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如 `proxy(...args)` 、` proxy.call(object,...args)` 、 `proxy.apply(...)` 注意这里只要proxy中拦截了apply,call和apply都会被拦截
  14. construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如 `new proxy(...args)`.也就是用于拦截new命令,construct方法返回的必须是一个对象,否则会报错

特征

  1. 如果一个属性不可配置(configurable)和不可写(writable),则该属性不能被代理,通过proxy对象访问该属性会报错
  2. Proxy.revocable()方法返回一个可取消的Proxy实例
    let target={};
    let handler={};
    let {proxy,revoke}=Proxy.revocable(target,handler);
    proxy.foo=123;
    proxy.foo  ==>123
    revoke();
    proxy.foo  ==>TypeError:Revoked
    //Proxy.revocable 方法返回一个对象,该对象的 proxy 属性是 Proxy 实例,revoke 属性是一个函数,可以取消 Proxy 实例

    3. Proxy代理的情况下,目标对象内部的this关键字会指向Proxy代理

    const target = {
    	m: function () {
    		console.log(this === proxy);
    	}
    };
    const handler = {};
    const proxy = new Proxy(target, handler);
    target.m() // false	
    proxy.m() // true	一旦proxy代理target.m,后者内部的this就指向proxy
    				
    //有些原生对象的内部属性,只有通过正确的this才能拿到,所以Proxy也无法代理这些原生对象的属性
    const target = new Date();
    const handler = {};
    const proxy = new Proxy(target, handler);
    proxy.getDate();
    // TypeError: this is not a Date object.
    //getDate方法只能在Date对象实例上拿到,如果this不是Date对象实例就会报错
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值