PROXY (代理器)
用处:
修改某些操作的默认行为,等同于在语言层面做出修改,属于一种"元编程",即对编程语言进行的编程。
就是在目标对象之前架设一层拦截,外界对该对象的访问,都必须通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
语法:
let proxy = new Proxy(target,handler) //生成proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为
handler是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。
例子:
let person = {name:'张三'}
let proxy = new Proxy(person,{
get:function(target,propKey){//target指向person这个对象,propKey是指target指向的对象的属性
//get方法,修改对象object获取属性的逻辑
}
})
上面代码中的配置对象中有一个get方法,用来拦截对目标对象属性的访问请求。get方法中的两个参数,分别是目标对象和所要访问的属性,
对目标对象设置后拦截器之后,对产生的实例进行操作,对原目标对象操作就不起作用。
如果没有设置任何拦截,就等同于直接通向原对象。
proxy实例也可以作为其他对象的原型对象。
同一个拦截器可以设置多个操作。
对于可以设置,但没有设置拦截操作,则直接落在目标对象上,按照原先的方式产生结果。
let prox = new Proxy({z:3},{
get:function(target,propKey){
console.log(target,propKey)
if(propKey === 'z'){
return target[propKey]
}
return 35
}})
prox.z // {z:3} 'z'
prox.a // {z:3} 'a'
拦截器中可以设置的handler中的操作有13种:
get(target,propKey,receiver) :拦截对象属性的读取,
set(target,propKey,value,receiver) :拦截对象属性的设置
has(target,propKey) : 拦截propKey in proxy的操作,返回一个布尔值
deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值
ownKeys(target): 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而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(...)。当实例作为函数调用时,就会被apply方法拦截。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
实例:get
使用proxy,将读取属性的操作get,转变为执行某个函数,从而实现属性的链式操作:
var pipe = function (value) {
var funcStack = [];
var oproxy = new Proxy({} , {
get : function (pipeObject, fnName) {
if (fnName === 'get') {
return funcStack.reduce(function (val, fn) {
return fn(val);
},value);
}
funcStack.push(window[fnName]);
return oproxy;
}
});
return oproxy;}
var double = n => n * 2;var pow = n => n * n;var reverseInt = n => n.toString().split("").reverse().join("") | 0;
pipe(3).double.pow.reverseInt.get; // 63
实例:set
设置存值函数set,任何不符合要求的age属性赋值,都会抛出一个错误。这是数据验证的一种方法。
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
}};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错