Object.defindProperty
缺陷:
- 无法监听数组变化
- 只能劫持对象的属性
var obj = {name: 'name'}
Object.defineProperty(obj, 'name', {
get: function(){
return '123';
}
})
obj.name // 123
Proxy
基础用法:
new Proxy(target, handler);
targer 要拦截的对象,handler 拦截行为对象
var obj = new Proxy({}, {
get: function(){
return '35';
}
})
obj.name;// 35
obj.age;// 35
obj2 = {}
obj2.name;// 无
obj2.age;// 无
Proxy针对的是对象实例obj,而不是上面代码中的空对象{}
get(target, propKey[, proxy])
用于拦截某个属性的读取操作,接受三个参数,target为目标对象,propKey为属性名,proxy为实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
var obj = new Proxy({}, {
get: function(target, propKey, proxy){
console.dir(target); // Object
console.dir(propkey); // name、age
console.dir(proxy); // Proxy
if (propKey in target) {
return target[propKey];
} else {
console.log('没有这个属性');
return null;
}
}
})
obj.name = 'new name';
obj.name;// new name
obj.age;// null
var obj = new Proxy({}, {
get: function(target, propKey, proxy){
return proxy;
}
})
console.log(obj.a === obj)// true
set(target, propKey, propValue[, proxy])
用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选
var obj = new Proxy({}, {
set: function(target, key, val, proxy){
console.log(key)
if (key == 'name') {
console.log('name 不可修改')
} else {
target[key] = val;
}
}
})
obj.name = 'new name';// undefined
obj.age = '99';// 99
Proxy可以拦截的其他方法:
- 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(...)
。 - construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
Proxy.revocable()
let {proxy, revoke} = Proxy.revocable({}, {});
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // 报错
console.dir(Proxy.revocable({}, {})); //Object {proxy, revoke}
返回一个可以取消的Proxy实例
this问题
在 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
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);
proxy.getDate();
// TypeError: this is not a Date object.
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate() // 1