读书笔记:深入理解ES6 (十二)
第十二章 代理(Proxy)和反射(Reflection)API
ES6添加了一些内建对象,赋予开发者更多访问JavaScript引擎的能力。代理(Proxy)是一种可以拦截并改变底层 JavaScript 引擎操作的包装器,在新语言中,通过它暴露内部运作的对象。
第1节 数组问题
在ES6出现以前,开发者不能通过自己定义的对象模仿 JavaScript 数组对象的行为方式。但现在通过代理就可以了。
第2节 代理和反射
1. 调用new Proxy()可以创建代理。代理可以拦截 JavaScript 引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数。
2. 反射以 Reflect 对象的形式出现,每个代理陷阱对应一个命名和参数都相同的 Reflect 方法。
第3节 创建一个简单的代理
用 Proxy 构造函数创建代理需要传入两个参数:目标(target)和处理程序(handler)。不使用任何陷阱的处理程序等价于简单的转发代理。例如:
1 let target = {}; 2 let proxy = new Proxy(target, {}); 3 4 proxy.name = "proxy"; 5 console.log(proxy.name); // "proxy" 6 console.log(target.name); // "proxy" 7 8 target.name = "target"; 9 console.log(proxy.name); //"target" 10 console.log(target.name); //"target"
第4节 使用 Set 陷阱验证属性
在该节中,通过使用set陷阱来实现了一个当给对象的属性添加的值必须为数字值时的一个验证的例子。如下:
1 let target = { 2 name: "target" 3 } 4 5 let proxy = new Proxy(target, { 6 set(trapTarget, key, value, receiver) { 7 8 //忽略不希望收到影响的已有属性 9 if (!trapTarget.hasOwnProperty(key)) 10 { 11 if (isNaN(value)) 12 { 13 throw new TypeError("属性必须是数字"); 14 } 15 } 16 17 //添加属性 18 return Reflect.set(trapTarget, key, value, receiver); 19 } 20 }); 21 22 //添加一个新属性 23 proxy.count = 1; 24 console.log(proxy.count); // 1 25 console.log(target.count); // 1 26 27 //由于目标已有name属性,因此可以给它赋值 28 proxy.name = "proxy"; 29 console.log(proxy.name); // "proxy" 30 console.log(target.name ); // "proxy" 31 32 //给不存在的属性赋值会抛出错误 33 proxy.anotherName = "proxy";
第5节 用 get 陷阱验证对象结构(Object shape)
对象结构是指对象中所有可用属性和方法的集合。本节中使用get陷阱来验证了某个对象上不存在某个属性的例子。
详细代码见P.275
第6节 使用 has 陷阱隐藏已有属性
每当使用in操作符时都会调用 has 陷阱。
详细代码见P.276
第7节 用 deleteProperty 陷阱防止删除属性
每当通过 delete 操作符删除对象属性时,deleteProperty 陷阱都会被调用。如果你希望保护属性不被删除,而且在严格模式下不抛出错误,那么这个方法很有用。
详细代码见P.278
第8节 原型代理陷阱
1. 由于 Reflect.getPropertyOf() 方法和 Reflect.setPropertyOf() 方法与 object 上的同名方法存在一些重要差异,因此区别使用它们是很重要的。
2. Object.getPropertyOf() 方法和 Object.setPropertyOf() 是高级操作,创建伊始便是给开发者用的;而 Reflect.getPropertyOf() 和 Reflect.setPropertyOf() 方法则是底层操作,其赋予开发者可以访问之前只在内部操作的 [[GetPropertyOf] ]和 [[SetPropertyOf]] 的权限。
第9节 对象可扩展性陷阱
1. ES6可以通过代理中的 preventExtensions 和 isExtensible 陷阱拦截这两个方法,并调用底层对象。
2. 相比高级功能方法而言,底层的具有更严格的错误检查。
第10节 属性描述符陷阱
在代理中可以分别用 defineProperty 陷阱和 getOwnPropertyDescriptor 陷阱拦截 Object.defineProperty() 方法和 Object.getOwnPropertyDescriptor() 方法的调用。
第11节 ownKeys陷阱
1. ownKeys 陷阱通过 Reflect.ownKeys() 方法实现默认的行为,返回的数组中包含所有自有属性的键名,字符串类型和 Symbol 类型的都包含在内。
2. ownKeys 陷阱也会影响 for-in 循环,当确定循环内部使用的键时会调用陷阱。
第12节 函数代理中的apply和construt陷阱
1. 所有代理陷阱中,只有 apply 和 construct 的代理目标是一个函数。
2. 当需要用到“验证函数参数”、“不用new调用构造函数”、“覆写抽象基类构造函数”、“可调用的类构造函数”的时候,可以回到这一节来看。
第13节 可撤销代理
1. 无论是出于安全目的通过API提供一个对象,还是在任意时间点切断访问,你将发现撤销代理是非常有用的。
2. 可以通过 Proxy.revocable() 方法创建可撤销的代理。
3. 当调用 revoke() 函数时,不能通过 Proxy 执行进一步操作。任何与代理对象交互的尝试都会触发代理陷阱抛出错误。
第14节 解决数组问题
在ES6以前,开发者不能在JavaScript中完全模仿数组的行为,在ES6中的代理和反射API可以用来创建一个对象,该对象的行为与添加和删除属性时内建数组类型的行为相同。
当需要使用代理和类来模拟数组的行为时,可以回到这一节来看。
第15节 将代理用作原型
尽管代理作为原型使用时及其受限,但是仍有几个陷阱可以使用。例如:
1. 在原型上使用get陷阱
2. 使用set陷阱
3. 使用has陷阱
4. 还可以将代理用作类的原型
遇到以上提到的几种情况的时候,可以回头来看本节的内容。