参考:http://es6.ruanyifeng.com/#docs/proxy
Proxy 的13种拦截操作。
- get(target, propKey, receiver):拦截对象属性的读取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):拦截对象属性的设置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。 - has(target, propKey):拦截
propKey in proxy
的操作,返回一个布尔值。 - deleteProperty(target, propKey):拦截
delete proxy[propKey]
的操作,返回一个布尔值。 - ownKeys(target):拦截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。 - getOwnPropertyDescriptor(target, propKey):拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。 - defineProperty(target, propKey, propDesc):拦截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。 - preventExtensions(target):拦截
Object.preventExtensions(proxy)
,返回一个布尔值。 - getPrototypeOf(target):拦截
Object.getPrototypeOf(proxy)
,返回一个对象。 - isExtensible(target):拦截
Object.isExtensible(proxy)
,返回一个布尔值。 - setPrototypeOf(target, proto):拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
get:
/** * 1.get(target, propKey, receiver) * target:目标对象 * propKey:属性名 * receiver:操作行为所针对的对象(可选) * * get方法用于拦截某个属性的读取操作 */ var student = { name : "Bill", age : 20 }; var handlerStudent = { get: function(target, property, receiver){ if(property in target){ //这里的property = "name",所以不能以target.property的方式调用 return target[property]; }else{ // return "No this property!"; throw "Error"; } } }; try{ var getProxy = new Proxy(student, handlerStudent); console.log(getProxy.name); //Bill console.log(getProxy.age); //20 console.log(getProxy.sex); //抛出异常 }catch(error){ console.log(error); }
set:
/** * 2.set(target, propKey, value, receiver) * target:目标对象 * propKey:属性名 * value:属性值 * receiver:操作行为所针对的对象(可选) * * set方法用于拦截某个属性的赋值操作 */ var handlerStudent = { set: function(target, property, value){ if(property === "age"){ if(!Number.isInteger(value)){ throw "TypeError"; } if(value >= 20){ throw "Value is invalid"; } }
//这里的property = "age",所以不能以target.property的方式调用 target[property] = value; } }; try{ var setProxy = new Proxy({}, handlerStudent); setProxy.age = 10; console.log(setProxy.age); setProxy.age = 'Ac'; //TypeError setProxy.age = 1234; //Value is invalid }catch(error){ console.log(error); }
apply:
/** * 3.apply(target, object, args) * target:目标对象 * object:目标对象的上下文 * args:目标对象的数组 * * apply方法拦截函数的调用,call和apply操作。 */ function sum(a, b) { return a + b; } var handler = { apply(target, obj, args){ let a = args.length; let b = 1; for(let i = 0; i < a; i++){ b *= args[i]; } return b; } /** * 你可能会遇到这种写法: * apply: function(target, obj, args){} * 不必担心,这与apply(target, obj, args){}是等价的。 */ } var proxy = new Proxy(sum, handler); console.log(proxy(1, 2)); //2 /** * 为什么call()和apply传参的方式不同?请查阅相关的方法。 */ console.log(proxy.call(null, 5, 7)); //35 console.log(proxy.apply(null, [10, 20]));//200
has:
/** * 4.has(target, propKey) * target:目标对象 * propKey:属性名 * * has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法就会生效。典型的操作符就是in操作符。 */ var stu1 = { name : 'Alice', score: 59 }; var stu2 = { name : 'Bill', score: 86 }; var handler = { has(target, prop){ if(prop === 'score' && target[prop] < 60){ console.log(target.name + " 不及格"); return false; } return prop in target;//这返回的是一个布尔值 } } var proxy1 = new Proxy(stu1, handler); var proxy2 = new Proxy(stu2, handler); 'score' in proxy1; //Alice 不及格 'score' in proxy2; //<啥也没输出>不过你可以添加一个console.log();查看结果 /** * 即使for(...in...)中有in操作符,但是,has对其不生效。 */ for(let i in proxy1){ console.log(proxy1[i]);// alice <这里有一个换行> 59 } for(let i in proxy2){ console.log(proxy2[i]);//Bill <这里有一个换行> 86 }
constract:
/** * 5.construct(target, ages) * target:目标对象 * args:构建函数的参数对象 * * construct方法用于拦截new命令,返回的必须是一个对象 */ var handler = { construct(target, args){ args.map(x => console.log(x));//3<这里有一个换行>4<这里有一个换行>5 return { num : args[0] * 10}; } }; var proxy = new Proxy(function(){}, handler); /** * 这里用new proxy()的原因是,返回的是一个对象,需要实例化才能调用其中的num(属性名)的属性值 */ console.log((new proxy(3, 4, 5)).num);//30
deleteProperty:
/** * 6.deleteProperty(target, propKey) * target:目标对象 * propKey:属性名 * * deleteProperty方法用于拦截delete操作 */ var handler = { /** * deleteProperty无法删除不可配置的属性,否则会报错 */ deleteProperty (target, propKey){ if(propKey[0] === '_'){ throw new Error("can't delete the property!"); } return true; } } var target = {_prop: 'a'}; var proxy = new Proxy(target, handler); try{ delete proxy._prop; }catch(error){ console.log(error); }
defineProperty:
/** * 7.defineProperty(target, propKey, propDesc): * target:目标对象 * propKey:属性名 * propDesc:属性描述符 * * defineProperty方法用于拦截Object.defineProperty操作 */ var handler = { defineProperty (target, propKey, propDesc){ return false; } };
//如果目标对象不可扩展,则defineProperty
不能增加目标对象上不存在的属性,否则会报错。
//另外,如果目标对象的某个属性不可写或不可配置,
//则defineProperty
方法不得改变这两个设置。 var proxy = new Proxy({foo1 : 2}, handler); proxy.foo = 'A'; console.log(proxy.foo + " " + proxy.foo1);//undefined 2
getOwnPropertyDescriptor:
/** * 8.getOwnPropertyDescriptor(target, propKey) * target:目标对象 * propKey:属性名 * * getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述 * 对象或者undefined. */ var handler = { getOwnPropertyDescriptor(target, propKey){ if(propKey[0] === '_'){ return; } return Object.getOwnPropertyDescriptor(target, propKey); } }; var target = { _foo: 'bar', baz: 'tar'}; var proxy = new Proxy(target, handler); console.log(Object.getOwnPropertyDescriptor(proxy, 'wat')); //undefine console.log(Object.getOwnPropertyDescriptor(proxy, '_foo')); //undefine(对属性首字符为下划线的直接返回undefine) console.log(Object.getOwnPropertyDescriptor(proxy, 'baz')); //{value: "tar", writable: true, enumerable: true, configurable: true}
getPrototypeOf(target):
/** * 9.getPrototypeOf(target): * target:目标对象 * * getPrototypeOf方法用于拦截获取对象原型。可以用于拦截以下操作: * -Object.prototype.__proto__ * -Object.prototypr.isPrototypeOf() * -Object.getPrototyprOf() * -Reflect.getPrototyprOf() * -instanceof() */ var a = {foo : 1} var proxy = new Proxy({},{ getPrototypeOf(target) { //返回值必须是对象或者是null。如果目标对象不可扩展,getPrototype方法必须返回目标对象的原型对象 return a; } }) console.log(Object.getPrototypeOf(proxy) === a);//true
isExtensible:
/** * 10.isExtensible(target) * target:目标对象 * * isExtensible方法拦截Object.isExtensible操作 */ var proxy = new Proxy({}, { isExtensible: function(target) { //只能返回布尔值,否则将被自动转换为布尔值 return true; } }); //返回值必须与目标对象的isExtensible属性保持一致,否则报错 console.log(Object.isExtensible(proxy));
ownKeys:
/** * 11.ownKeys(target) * target:目标对象 * * ownKeys方法用于拦截对象自身属性的读取操作 * -Object.getOwnPropertyNames() * -Object.getOwnPropertySybols() * -Object.keys() */ var target = { a : 1, b : 2, c : 3 }; var handler = { ownKeys(target){ return ['a']; } } //这里只举例一个操作,其余的请见最上方的参考 console.log(Object.keys(target));//["a", "b", "c"] console.log(Object.keys(new Proxy(target, handler)));//["a"]
preventExtensions:
/** * 12.preventExtensions(target) * target:目标对象 * * preventExtensions方法拦截Object.preventExtensions() * 该方法只有一个限制,即目标对象不可扩展时(即Object.isExtensions为false), * 其余该方法将返回true,否则报错 */ var proxy = new Proxy({}, { preventExtensions(target){ //通过调用一次Object.preventExtensions()来防止Object.isExtensible()返回true Object.preventExtensions(target); return true; } }); console.log(Object.preventExtensions(proxy));//proxy{}
setPrototypeOf:
/** * 13.setPrototypeOf(target, proto) * target:目标对象 * proto:原型对象 * * setPrototypeOf方法用于拦截Object.setPrototypeOf */ var handler = { setPrototypeOf(target, proto){ throw new Error('error'); } } var proto = {}; var target = {}; var proxy = new Proxy(target, handler); try{ console.log(Object.setPrototypeOf(proxy, proto)); }catch(error){ console.log(error); }
注:任何细节请前往最上方的网站查看。