跟踪属性访问
通过捕获 get、set 和 has 等操作,可以知道什么时候访问和查询过对象属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const user = { name: 'Jake' };
const proxy = new Proxy(user, { get(target, property, receiver) { console.log(`Getting ${property}`);
return Reflect.get(...arguments); },
set(target, property, value, receiver) { console.log(`Setting ${property}=${value}`);
return Reflect.set(...arguments); } });
proxy.name; proxy.age = 27;
|
隐藏属性
代理的内部实现对外部代码不可见,能方便地隐藏目标对象上的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| const hiddenProperties = ['foo', 'bar'];
const targetObject = { foo: 1, bar: 2, baz: 3 };
const proxy = new Proxy(targetObject, { get(target, property) { if(hiddenProperties.includes(property)) { return undefined; } else { return Reflect.get(...arguments); } },
has(target, property) { if(hiddenProperties.includes(property)) { return false; } else { return Reflect.has(...arguments); } } });
console.log(proxy.foo); console.log(proxy.bar); console.log(proxy.baz);
console.log('foo' in proxy); console.log('bar' in proxy); console.log('baz' in proxy);
|
属性验证
所有赋值操作都会触发 set() 捕获器,可以根据所赋的值决定允许还是拒绝赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const target = { onlyNumbersGoHere: 0 };
const proxy = new Proxy(target, { set(target, property, value) { if(typeof value !== 'number') { return false; } else { return Reflect.set(...arguments); } } });
proxy.onlyNumbersGoere = 1; console.log(proxy.onlyNumbersGoere);
proxy.onlyNumbersGoere = '2'; console.log(proxy.onlyNumbersGoere);
|
函数与构造函数参数验证
可以审查函数和构造函数参数,让函数只接收某种类型的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function median(...nums) { return nums.sort()[Math.floor(nums.length / 2)]; }
const proxy = new Proxy(median, {
apply(target, thisArg, argumentsList) { for(const arg of argumentsList) { if(typeof arg !== 'number') { throw 'Non-number argument provided'; } }
return Reflect.apply(...arguments); } });
console.log(proxy(4, 7, 1)); console.log(proxy(4, '7', 1));
|
可以要求实例化时必须给构造函数传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class User { constructor(id) { this.id_ = id; } }
const proxy = new Proxy(User, {
construct(target, argumentsList, newTarget) { if(argumentsList[0] === undefined) { throw 'User cannot be instantiated without id'; } else { return Reflect.construct(...arguments); } } });
new proxy(1);
new proxy();
|
数据绑定与可观察对象
通过代理可以联系运行时原本不相关的部分,实现各种模式,让不同的代码互操作
比如,可以将被代理的类绑定到一个全局实例集合,让所有创建的实例都被添加到这个集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const userList = [];
class User { constructor(name) { this.name_ = name; } }
const proxy = new Proxy(User, { construct() { const newUser = Reflect.construct(...arguments); userList.push(newUser);
return newUser; } });
new proxy('John'); new proxy('Jacob'); new proxy('Jingleheimerschmidt');
console.log(userList);
|
也可以把集合绑定到一个事件分派程序,每次插入新实例都会发送消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const userList = [];
function emit(newValue) { console.log(newValue); }
const proxy = new Proxy(userList, {
set(target, property, value, receiver) { const result = Reflect.set(...arguments); if(result) {
emit(Reflect.get(target, property, receiver)); }
return result; } });
proxy.push('John');
proxy.push('Jacob');
|