一、简介
代理是一种基本设计模式,一种对象结构模式。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象等,代理对象在客户端和目标对象之间起到中介作用,可以在目标对象被访问前后进行一些操作,使得使用者无须关心目标对象的具体实现。
在 JavaScript 中,我们可以使用一个对象来代理另一个对象。而 ProxyJS 就是一个封装了代理模式的 JavaScript 库,旨在为 JavaScript 程序员提供一个扩展语言核心的机制,这个机制允许你定义一个对象,在其上定义一组操作,这些操作对这个对象执行的所有操作都会被拦截器处理。
二、基本用法
在使用 ProxyJS 时,我们需要创建一个代理实例,并在其上添加一些拦截器(Handlers)。比如下面的例子,我们使用 Proxy 代理了一个对象,然后在代理时添加了一个 `get` 拦截器,当试图访问代理实例的 `name` 属性时,这个拦截器会被调用:
const obj = { name: "Alice" }; const p = new Proxy(obj, { get(target, property, receiver) { console.log("Someone is trying to access the property!"); return Reflect.get(target, property, receiver); } }); console.log(p.name); // 输出 "Someone is trying to access the property!" 和 "Alice"
上面的代码中,我们创建了一个普通对象 obj,并使用 Proxy 创建了一个代理实例 p。当我们尝试访问 p 的 `name` 属性时,代理实例会触发 `get` 拦截器,这个拦截器会在获取 `name` 属性值之前输出一条日志,然后再返回属性值。
三、代理操作的拦截器
在上面的例子中我们已经使用了一个 `get` 拦截器,它会在代理对象上获取属性时被调用。ProxyJS 提供了一些预置的拦截器,它们会在执行一些操作时被调用,比如:
- get(target, property, receiver)
- set(target, property, value, receiver)
- has(target, property)
- deleteProperty(target, property)
- apply(target, thisArg, argumentsList)
- construct(target, argumentsList, newTarget)
- getPrototypeOf(target)
- setPrototypeOf(target, prototype)
- defineProperty(target, property, descriptor)
- getOwnPropertyDescriptor(target, property)
- … …
通过在代理对象上添加这些拦截器(Handlers),我们可以修改代理对象执行这些操作时的行为。比如下面的例子中,我们定义了一个代理对象 p,其中添加了一个 `set` 拦截器,在给代理对象设置属性时,这个拦截器会将属性的名称转换为小写存储,而不是原来的大写:
const p = new Proxy({}, { set(target, property, value, receiver) { if (typeof property === 'string') { property = property.toLowerCase(); } return Reflect.set(target, property, value, receiver); } }); p.Foo = 'bar'; console.log(p.Foo); // 输出 "bar" console.log(p.foo); // 输出 "bar"
在上面的代码中,当我们试图在代理对象 p 上设置一个属性时,`set` 拦截器会首先将属性的名称转换为小写,然后再将属性保存到代理对象中。这样一来,我们可以在属性访问时使用大小写不敏感的方式,而且代理对象会始终使用小写名称存储属性。
四、反射器(Reflect API)
ProxyJS 还提供了一个反射器 API,可以让我们在访问代理对象时直接调用方法,而不需要使用拦截器。这个 API 的核心就是 Reflect 对象,它提供了一组操作代理对象的方法。
比如,我们可以使用 Reflect.get() 方法直接获取代理对象的属性值,而不需要定义一个 `get` 拦截器:
const obj = { name: "Alice" }; const p = new Proxy(obj, {}); console.log(Reflect.get(p, 'name')); // 输出 "Alice"
在上面的代码中,我们首先创建了一个普通对象 obj,然后将其传递给 Proxy 构造函数,创建了一个代理对象 p。最后,我们使用 Reflect.get() 方法获取代理对象 p 的 `name` 属性值(在这里我们并没有定义一个 `get` 拦截器),它将返回原始对象 obj 中的名称属性值。
五、应用场景
ProxyJS 可以在很多场景下使用。比如,我们可以将一个对象的访问权限限制为只读,或者只允许访问一部分属性。
一个经典的案例就是拦截对象之间的依赖关系。通过一个对象重新包装另一个对象,并在其属性访问时添加依赖关系,从而实现数据绑定等功能,可以较为轻松的实现数据层和视图层的解耦。
let obj = {a:1, b:2}; let dependentObj = {x: 'Hello, '}; let mixinObj = Object.assign( {}, obj, { get a() { return obj.a; }, set a(value) { dependentObj.x += `a的值改变为:` + value; obj.a = value; } } ); mixinObj.a = 3; // 执行setter操作,触发依赖关系 console.log(dependentObj.x); // 输出 "Hello, a的值改变为:3"
在上面的代码中,我们通过一个 mixinObj 对象重新包装了 obj 对象,并添加了一个 get/set 拦截器,从而在 obj 对象的 `a` 属性访问时可以将这个访问依赖添加到 dependentObj 对象的 `x` 属性中。这种拦截器模式为数据层和视图层的解耦提供了一种新的编程方式。
六、总结
ProxyJS 是一个很强大的库,它允许 JavaScript 程序员重新定义 JavaScript 的对象操作。通过添加一些拦截器和反射器,我们可以在执行操作时做一些额外的工作,从而实现颠覆传统对象访问的编程方式。当然,ProxyJS 也有许多其他功能,比如可以用它来添加自定义的事件监听器和事件分发器,还可以用它来实现像 Mixin 和 Traits 合成等高级编程技巧。
使用 ProxyJS 进行编程,你需要从面向对象的思维方式转变为面向拦截器的方式,这可能会需要一些练习,但一旦掌握,它将可以为你提供很强的编程能力和灵活性。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/206108.html