JS代理Proxy

本文介绍了JavaScript中的代理(Proxy)和反射(Reflect)特性,它们允许开发者拦截和定制对象的基本操作。通过创建代理对象,可以定义捕获器来在访问或修改目标对象时执行自定义逻辑。例如,使用get()捕获器可以改变属性访问的行为。反射API则提供了与捕获器对应的方法,用于重构原始操作。这些工具为JavaScript编程提供了更细粒度的控制和灵活性。
摘要由CSDN通过智能技术生成

摘自《JavaScript高级程序设计第四版》第九章 代理与反射

ECMAScript 6新增的代理和反射为开发者提供了拦截并向基本操作嵌入额外行为的能力。具体地说,可以给目标对象定义一个关联的代理对象,而这个代理对象可以作为抽象的目标对象来使用。在对目标对象的各种操作影响目标对象之前,可以在代理对象中对这些操作加以控制。

代理是目标对象的抽象。从很多方面看,代理类似C++指针,因为它可以用作目标对象的替身,但又完全独立于目标对象。目标对象既可以直接被操作,也可以通过代理来操作。但直接操作会绕过代理施予的行为。

一、创建空代理

空代理相当于给目标对象一个指针,什么都不做,对空代理的操作会直接反映到目标对象身上。
代理是使用Proxy构造函数创建的。这个构造函数接收两个参数:目标对象和处理程序对象。缺少其中任何一个参数都会抛出TypeError。要创建空代理,可以传一个简单的对象字面量作为处理程序对象,从而让所有操作畅通无阻地抵达目标对象。

语法:

const target = { id: 'target' }
const handler = {}
const proxy = new Proxy(target, handler)
console.log(target.id) // target
console.log(proxy.id) // target

在代理对象上执行的任何操作实际上都会应用到目标对象。唯一可感知的不同就是代码中操作的是代理对象。对代理对象proxy的任何读写都会反映到目标对象上,因此读proxy的property等于读目标对象的property,对proxy对象赋值会改变目标对象的值。

二、定义捕获器

使用代理的主要目的是可以定义捕获器(trap)。捕获器就是在处理程序对象中定义的“基本操作的拦截器”。每个处理程序对象可以包含零个或多个捕获器,每个捕获器都对应一种基本操作,可以直接或间接在代理对象上调用。每次在代理对象上调用这些基本操作时,代理可以在这些操作传播到目标对象之前先调用捕获器函数,从而拦截并修改相应的行为

例如,可以定义一个get()捕获器,在ECMAScript操作以某种形式调用get()时触发。下面的例子定义了一个get()捕获器:

const target = { foo: 'bar' }
// 捕获器在处理程序对象中以方法为键
const handler = { get() { return 'handler override' } }

const proxy = new Proxy(target, handler)
console.log(proxy.foo) // handler override
console.log(target.foo) // bar
console.log(proxy['foo']) // handler override
console.log(target['foo']) // bar
console.log(Object.create(proxy['foo'])) // handler override
console.log(Object.create(target['foo']) // bar

这样,当通过代理对象执行get()操作时,就会触发定义的get()捕获器。当然,get()不是ECMAScript对象可以调用的方法。这个操作在JavaScript代码中可以通过多种形式触发被get()捕获器拦截到。proxy[property]、proxy.property或Object.create(proxy)[property]等操作都会触发基本的get()操作以获取属性。因此所有这些操作只要发生在代理对象上,就会触发get()捕获器。注意,只有在代理对象上执行这些操作才会触发捕获器。在目标对象上执行这些操作仍然会产生正常的行为。

三、捕获器参数和反射API

所有捕获器都可以访问相应的参数,基于这些参数可以重建被捕获方法的原始行为。比如,get()捕获器会接收到目标对象、要查询的属性和代理对象三个参数。

const target = { foo: 'bar' }
const handler = { 
	get(trapTarget, property, receiver) {
	 console.log(target === trapTarget);  } 
	}

有了这些参数,就可以重建被捕获方法的原始行为。通过调用全局Reflect对象上(封装了原始行为)的同名方法来轻松重建。

处理程序对象中所有可以捕获的方法都有对应的反射(Reflect)API方法。这些方法与捕获器拦截的方法具有相同的名称和函数签名,而且也具有与被拦截方法相同的行为。因此,使用反射API也可以像下面这样定义出空代理对象:

const target = { foo: 'bar' }
const handler = { 
	get() {
		return Reflect.get(...arguments)
	  } 
	}
const proxy = new Proxy(target, handler)
console.log(proxy.foo) // bar
console.log(target.foo) // bar

简洁写法:

const target = { foo: 'bar' }
const handler = { 
	get: Reflect.get
const proxy = new Proxy(target, handler)
console.log(proxy.foo) // bar
console.log(target.foo) // bar

事实上,如果真想创建一个可以捕获所有方法,然后将每个方法转发给对应反射API的空代理,那么甚至不需要定义处理程序对象:

const target = { foo: 'bar' }
const proxy = new Proxy(target, Reflect)
console.log(proxy.foo) // bar
console.log(target.foo) // bar

更多见:
MDN文档 - JavaScript标准内置对象 - Proxy
微信读书 -《JavaScript高级程序设计第四版》第九章 代理与反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值