ECMAScript 2016 的 Proxy:代理模式的精华
随着 ECMAScript 2016 发布,JavaScript 新增了一个非常重要的特性:Proxy。Proxy 是一种代理模式,可以劫持 JavaScript 对象的常规操作,从而在真实操作前执行一些自定义操作或者处理逻辑。Proxy 的优雅、灵活和强大,为我们提供了更好的控制和包装对象。本文将介绍 Proxy 的原理、使用方法、示例代码和实践指导。
- Proxy 的原理
在 JavaScript 中,对象就是键值对的集合,许多操作就是对于对象的读取、设置、删除和枚举等。Proxy 的本质就是在原有对象的基础上,构建一个代理对象,在代理对象上可以实现预处理和后处理等操作,并且代理对象与原有对象一样可以被使用,并符合 ES6 的语义规范。
Proxy 构造函数的原型定义如下:
var p = new Proxy(target, handler);
其中 target 是需要加强的目标对象,handler 是一个对象,它定义了代理对象的行为。handler 中每个方法的名字叫做“陷阱”(trap),用于拦截目标对象的对应的操作。例如:
- get(target, propKey):拦截属性读取,返回任何值。在读取属性时,即 get 操作时,可以对目标对象进行任何处理、过滤、记录或转换后返回相应的值。
- set(target, propKey, value):拦截属性设置,返回布尔值。在设置属性时可以进行检查,如果检查失败则返回 false。
- deleteProperty(target, propKey):拦截属性删除,返回布尔值。在删除属性时可以添加任何逻辑操作。
- apply(target, thisValue, args):拦截函数调用,返回任何值。在函数调用时,可以进行参数过滤、修改、记录等操作,并返回一个值。
- has(target, propKey):拦截 key in object 操作,返回布尔值。可以检查属性是否存在,或者添加一个新的不存在的属性。
另外,Proxy 如果直接被 JSON.stringify() 调用会导致内存泄漏,因为 JSON.stringify() 过程中没有机会触发 Proxy 的陷阱拦截器,所以建议在实际使用时,不要直接调用 JSON.stringify() 函数。
- Proxy 的使用方法
下面是一个 Proxy 的简单示例,用于对目标对象进行读取相关属性行为的拦截:
-- -------------------- ---- ------- --- ------ - - ----- ------ ---- -- -- --- ----------- - --- ------------- - ----------- -------- - ---------------- --- ------- - - ------- - - ------------ ------ ---------------- -- ----------- -------- ------ - ---------------- --- ------- - - ------- - - -------- -- - - ----- - ----- --------------- - ------ ------ ----- - --- ------------------------------ ----------------------------- ---------------- - ------- ------------------------------
输出结果为:
You are reading name property. Tom You are reading age property. 18 You are setting name property to Jack. You are reading name property. Jack
使用 Proxy 进行对象读取和设置操作,我们可以加入额外的信息、处理逻辑和调试信息,并返回想要的结果,这大大增强了对象操作的灵活性和可维护性。
- Proxy 的实践指导
在实际开发中,我们可以应用 Proxy 的代理模式来实现多种场景。
3.1. 防篡改对象
可以通过 Proxy 来创建一个防篡改对象,防止对象的属性被任意修改。
固定对象属性值的值:
-- -------------------- ---- ------- --- ----- - --------- -- - ----- ----- - -------- -- - -- ------- ---------- ------- - --- ---- --- -- ------- - ------------------- - - -- ----- ------- - - ----------- ---- ------ - ----- --- ----------------- --- -------- -------- -- ------------ - -- ------ ----- -- - ----------- ------ --- ---------- --------- -- ----- --- ---- - ------- --- -- ----- ----- --- --------- - -------- ------------------ -- - --- -- ----- ----- -
该函数中使用 Proxy 来实现固定对象属性值的值,当我们对防篡改对象进行属性的修改,会直接抛出异常。
3.2. 数据校验和强类型
有些场景下会需要对于从前端或者后端的数据进行强校验或类型校验,可以使用 Proxy 实现。
-- -------------------- ---- ------- --- ----- - --------- ------- - ----- ------- - - ----------- ---- ------ - -- ------------- - ------ ------------------- ---- ------- - ----- ---- - ------ ------ -- ----- --- ------------------------- - ----- --- ------------------- ------ ------ -- ------------------------------ - ------ ------------------- ---- ------- - -- ------ ----- ------ -- - --- ----- - --- ---------- --------- ------ ------ -- --- ----- --------- ---- --------- ------ -------- ---- --- ------ - ------- ----- ------- ---- --- ------ ------------- --- ---------- - --------- -------------------- -- ---------- -------- --- ------ -- ------ ------------ - ------------ -------------------- -- - ----- ------- ---- --- ------ ------------- -
该函数可以用于任意对象的强类型校验,任意属性的类型、取值范围、值的合法性等校验都可以在 Proxy 中实现。
3.3. 对象缓存和延迟计算
使用 Proxy 可以实现一个对象的缓存和延迟计算。在对象的某些复杂属性计算过程中,我们可以将结果缓存,避免重复计算,提高程序效率。
-- -------------------- ---- ------- --- -------- - --------- -- - ----- ------- - - ----------- -------- --------- - --- ------------- - ------------------- -------- ---------- -- ---------------- - ------------- - -------------- ------------------- -------- --------------- - ------ -------------- - -- ------ ----- ----- -- - ----- ----- - --- ---------- --------- ---------- - ----- ------ ------ -- ----- --- ---- - ---------- ----- ------- ---- -- -- -------- -- - ---------------------------- ------ --------- - --- - --------- --- ------------------------- -------------------------
输出结果为:
calculate... Jack-20 Jack-20
可以发现,使用 Proxy 进行计算属性缓存,大大提高了程序效率和用户体验。
- 总结
Proxy 作为代理模式的实现,大大增强了 JavaScript 对象的灵活性和可维护性,使用 Proxy 进行对象计算、缓存、校验、拦截等操作非常方便和实用。在实际开发中,我们可以灵活地运用 Proxy,加强对对象的处理逻辑和控制,从而让代码更加优雅、可读、可维护。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/644f8a6b980a9b385b8faaa6