写本《JavaScript简餐》系列文章的目的是记录在阅读学习《JavaScript高级程序设计(第4版)》一书时出现的各个知识点。虽是对读书的笔记和总结,但是希望它轻量、简洁、犀利,不会引起阅读疲劳,可以在碎片化时间和闲暇之余轻巧地沐浴一下知识点。每篇文章只针对一个小部分进行讲解式的梳理,来达到个人复习总结和分享知识的目的。
ECMAScript6中新增了代理与反射,让我们可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。
const person = { name: 'Lucy' }; const handler = {}; const proxy = new Proxy(person, handler); // (目标对象, 处理程序对象) console.log(person.name); // Lucy console.log(proxy.name); // Lucy person.name = 'Jack'; // 改变person对象(目标对象)上的name属性那么在proxy上也会改变 console.log(person.name); // Jack console.log(proxy.name); // Jack console.log(person === proxy) // false
const person = { name: 'Lucy' }; const handler = { get() { return '捕获器触发!' } }; const proxy = new Proxy(person, handler); // (目标对象, 处理程序对象)这样,当通过代理对象执行get()操作时,就会触发定义的get()捕获器。所有这些操作只要发生在代理对象上,就会触发get()捕获器。注意,只有在代理对象上执行这些操作才会触发捕获器。在目标对象执行这些操作仍然会产生正常的行为。我们做一下试验:
console.log(person.name); // Lucy console.log(proxy.name); // 捕获器触发!果不其然,在代理对象上执行get操作时捕获器触发了!
const person = { name: 'Lucy' }; const handler = { get(trapTarget, property, receiver) { console.log(trapTarget === person); console.log(property); console.log(receiver === proxy); } }; const proxy = new Proxy(person, handler); // (目标对象, 处理程序对象) proxy.name; // true // name // true有了这些参数,就可以重建被捕获方法的原始行为:
const person = { name: 'Lucy' }; const handler = { get(trapTarget, property, receiver) { return trapTarget[property]; } }; const proxy = new Proxy(person, handler); // (目标对象, 处理程序对象) console.log(person.name); // Lucy console.log(proxy.name); // Lucy虽然我们可以自己手动重建原始行为,但是完全可以没这个必要,因为比get复杂的操作还有很多。为此,我们可以通过调用全局Reflect对象上的同名方法来轻松重建。处理程序对象中所有可以捕获的方法都有对应的反射API方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。因此,使用反射API也可以像下面这样定义出空代理对象:
const person = { name: 'Lucy' }; const handler = { get() { return Reflect.get(...arguments); } }; const proxy = new Proxy(person, handler); // (目标对象, 处理程序对象) console.log(person.name); // Lucy console.log(proxy.name); // Lucy如果真的想创建一个可以捕获所有方法,然后将每个方法转发给对应反射API的空代理,那么甚至不需要定义处理程序对象:
const person = { name: 'Lucy' }; const proxy = new Proxy(person, Reflect); // (目标对象, 处理程序对象) console.log(person.name); // Lucy console.log(proxy.name); // Lucy
const person = {}; Object.defineProperty(person, 'name', { configurable: false, writable: false, value: 'Lucy' }); const handler = { get() { return 'Jack'; } }; const proxy = new Proxy(person, handler); console.log(proxy.name); // TypeError: 'get' on proxy: property 'name' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'Lucy' but got 'Jack')