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;