代理
在本课程中,你将学习如何使用 JavaScript 代理来拦截和重写对象的操作。
JavaScript 代理(Proxy)于 2015 年随 ECMAScript 6 引入。它允许我们拦截和重写对象属性的访问与赋值等操作。一个 Proxy 对象包裹另一个对象,并充当中间人。
语法
通过 new Proxy 构造函数创建代理,需要两个必需参数:目标对象 和 处理器对象。
1let proxy = new Proxy(target, handler)
target– 被包裹的对象handler– 定义控制目标对象行为的方法(也称为 陷阱)
Proxy 在目标对象周围创建了一个不可察觉的屏障,将所有操作重定向到处理器对象。如果传入空的 handler,代理就只是一个原始对象的空包装。
1let user = { 2 name: 'Max', 3 age: 42 4}; 5 6let proxyUser = new Proxy(user, {}); 7 8console.log(user.name); // -> Max 9console.log(proxyUser.name); // -> Max
为了让代理具有实际意义,我们需要在处理器中添加一些功能。
陷阱(Traps)
每次与对象交互时,实际上是在调用内部方法。代理允许我们使用陷阱来拦截特定内部方法的执行。
例如,当我们运行 user.name 时,是告诉 JavaScript 引擎调用内部的 [[GET]] 方法来获取 name 属性。
而当我们运行 proxyUser.name 时,get 陷阱 会先调用 handler 中定义的 get() 函数,然后再将请求传递给原始对象。
get
get() 方法有两个必需参数:
target– 传入代理的目标对象property– 被访问的属性名
为了自定义代理行为,我们可以在 handler 对象上定义函数。这里我们定义 get 方法用于记录访问:
1const handler = { 2 get(target, property) { 3 console.log(`GET ${property}`); 4 return target[property]; 5 } 6};
为了使调用正常进行,我们返回 target[property]。
现在,如果我们用这个处理器创建代理并访问原始对象,就会输出访问日志:
1const proxyUser = new Proxy(user, handler); 2 3console.log(proxyUser.age); 4// -> GET age 5// -> 42
可以看到,当通过 proxyUser 访问 user 对象的属性时,handler 中的 get() 方法被触发。
set
set 陷阱 控制对目标对象属性赋值时的行为。
我们可以对 age 的输入值进行验证:
1const handler = { 2 set(target, property, value) { 3 if(property === 'age' && typeof(value) !== 'number') { 4 throw new TypeError('Age is just a number.'); 5 } 6 7 target[property] = value; 8 return true; 9 } 10}
如果尝试为 age 赋予错误类型的数据,会抛出错误:
1proxyUser.age = 'old'; 2// -> TypeError: Age is just a number.
在 target[property] = value 这一行中,我们修改了 user 对象的 age 属性。
set() 方法必须返回布尔值 true,表示赋值成功。如果在严格模式下返回的是假值或未返回任何值,将会抛出错误:
1Uncaught TypeError: 'set' on proxy: trap returned falsish for property 'age'
除了拦截属性的读取和修改外,
Proxy还可以拦截总共十三种操作。
总结
我们学习了如何使用代理来监控对象的行为。现在你应该能够通过在处理器对象中使用陷阱方法,为对象添加自定义行为。虽然我们仅通过几个基础示例初步接触了代理,但这已足够让你入门并激发探索更多可能性的兴趣!
关于代理的测验
Quiz
这是代理的语法,其中 target 是我们包装的对象。那么 handler 是什么?
1let proxy = new Proxy(target, handler)