Proxy介绍
Proxy 用于修改某些操作的默认行为;可以理解成,在目标对象之前架设一层"拦截",外界对对象的访问,都必须先通过这层拦截,因此提供了一层机制,可以对外界的访问进行过滤和改写。
const proxy = new Proxy(target,handler); target表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
const 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
console.log(++obj.count)//2
proxy实例的常见方法
get(target,prop,receiver ):拦截某个属性的读取,三个参数依次为目标对象、属性名和 proxy 实例本身 receiver, 其中 receiver 为可选;
function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
console.log(propKey)
let index = Number(propKey);
if(index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver)
}
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
let arr = createArray('a','b','c');
console.log(arr[-1]);// c
console.log(arr[0]);// b
console.log(arr[1])// a
这个例子用 get 拦截,实现根据数组索引读取数组;当索引为负时,表示数组遍历为从后到前;所以,当数组的位置是 -1 的时候,就会输出数组的倒数第一个成员 c
set(target,prop, value, receiver):用来拦截某个属性的赋值操作; prop 为属性名,value 为属性值,receiver 依旧可选;
用set()作为数据验证:
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;
console.log(person.age)// 100
person.age = 300;
console.log(person.age);// 报错
任何不符合要求的age属性赋值,都会报错。
apply(target,ctx, args):拦截函数的调用,call 和 apply 操作;参数 ctx 指的是目标对象的上下文对象 (this) 和目标对象的参数数组。
const twice = {
apply(target, ctx, args) {
console.log(args);// 1,2
return Reflect.apply(...arguments)*2;
}
};
function sum(left, right) {
return left + right;
};
const proxy = new Proxy(sum,twice);
console.log(proxy(1,2));//6
当执行proxy()时,会被apply()拦截;
has(target,key):用来拦截 hasProperty 操作,即判断对象是否具有某个属性;典型的操作就是 in 运算符。
const handler = {
has(target,key) {
if(key[0] === '_') {
return false;
}
return key in target;
}
};
const target = {_prop: 'foo', prop: 'foo'};
const proxy = new Proxy(target,handler);
console.log('_prop' in proxy);// false;
这里的代码中,如果原对象的属性名的第一个字符时下划线,proxy.has() 就会返回 false,从而不被 in 运算符发现。注意:has() 拦截的是hasProperty 操作,而不是 hasOwnProperty 操作,即它不判断属性来源。
this问题
虽然 proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理。不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在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 而不是 target 了。
来源:oschina
链接:https://my.oschina.net/u/2669765/blog/3034542