Proxy 对象可以捕获源对象(对象、数组、函数)的操作,包括增删改查(获取、赋值、枚举、删除)以及函数调用,可以更加全面地控制源对象。
代理对象
// 源对象 必须是复杂数据类型,如 对象、数组、函数
var target = {age: 27};
var handler = {
get: function(target, key) {
// 代理 target[key]
console.log('get');
if (key in target) {
return target[key];
}
},
set: function(target, key, value) {
// 代理 target[key] = value
console.log('set');
target[key] = value;
},
deleteProperty: function(target, key) {
// 代理 delete target[key]
console.log('deleteProperty');
return key in target ? delete target[key] : false;
},
enumerate: function(target, key) {
console.log('enumerate');
return Object.keys(target);
},
ownKeys: function(target, key) {
// 代理 Object.getOwnPropertyNames(target)
// 代理 Object.getOwnPropertySymbols(target)
console.log('ownKeys');
return Object.keys(target);
},
has: function(target, key) {
// 代理 key in target
console.log('has');
return key in target;
},
defineProperty: function(target, key, desc) {
// 代理 Object.defineProperty(proxy, key, desc)
console.log('defineProperty');
Object.defineProperty(target, key, desc);
return target;
},
getOwnPropertyDescriptor: function(target, key) {
console.log('getOwnPropertyDescriptor');
return key in target ? {
value: target[key],
writable: true,
enumerable: false,
configurable: true
} : undefined;
}
};
// 新建代理
var proxy = new Proxy(target, handler);
proxy.age;
proxy.age = 28;
Object.defineProperty(proxy, 'name', {value: 'seven'});
proxy.name;
target.name;
Object.keys(proxy),会分别调用 handler.ownKeys 和 getOwnPropertyDescriptor,前者用于获取 target 的 key 列表,后者用于依次判断每个 key 是否可以迭代。
若 getOwnPropertyDescriptor 返回值得 enumerate 为 false,则该 key 不会出现在 Object.keys(proxy) 返回结果中。
Object.getOwnPropertyDescriptors(proxy) 和 Object.keys(proxy) 一样,会分别调用 handler.ownKeys 和 getOwnPropertyDescriptor,但不会根据 getOwnPropertyDescriptor 返回结果过滤 key。
代理数组
var target = [1, 2, 3];
var handler = {
set: function(target, key, value) {
// 代理 target[key] = value
console.log('set');
target[key] = value;
// 代理数组 set,需要 返回源对象
// 代理对象不需要
return target;
}
};
var proxy = new Proxy(target, handler);
proxy.push(4);
proxy.pop();
proxy.unshift(5);
proxy.shift();
代理数组 handler.set 需要返回源对象,其余与代理对象一致。
代理函数
var target = function (name) {
this.name = name;
this.sayHello = function() {
return 'Hi, I am ' + this.name;
};
return 'Hi, I am ' + name;
};
var handler = {
construct: function(target, args) {
console.log('construct');
var inst = Object.create(target.prototype);
target.apply(inst, args);
return inst;
},
apply: function(target, args) {
console.log('apply');
return target.apply(target, args);
}
};
var proxy = new Proxy(target, handler);
proxy();
var inst = new proxy('seven');
inst.sayHello();
代理函数可以设置 handler.construct 和 handler.apply,其余与代理对象一致。
创建可以撤销的代理
var target = {age: 27};
var handler = {
get: function(target, key) {
// 代理 target[key]
console.log('get');
if (key in target) {
return target[key];
}
}
};
var revocable = Proxy.revocable(target, handler);
var proxy = revocable.proxy;
proxy.age;
// 执行撤销
revocable.revoke();
// 撤销之后再执行任何代理操作都会报错
// TypeError
proxy.age;