3

js的Proxy代理对象?看完你就会了!

 2 years ago
source link: https://my.oschina.net/cc4zj/blog/5046826
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

什么是Proxy代理?

// pOjb就是通过new Proxy创建的代理对象
var pObj = new Proxy(obj, handlers)

为什么需要代理对象

举个记账的例子:

// obj代表我们,wallet属性指我们钱包,现在我们钱包里有100元
// consume指消费次数,每次消费加1, 记一笔账
var obj = {wallet: 100}
var consume = 0

// 这个月,我们喝了五次肥宅快乐水,每次消费我们都记一笔

// 今天消费3元
consume++
obj.wallet = 97

// 今天消费3元
consume++
obj.wallet = 94

// 今天消费3元
consume++
obj.wallet = 91

// 今天消费3元
consume++
obj.wallet = 88

// 今天消费3元
consume++
obj.wallet = 85

每次我们修改钱包剩余金额时,都要执行一次consume++去执行一次记账的操作。有没有更简单的方式,不需要每次都写上一行代码去增加消费次数呢?

答案当然有,它就是Proxy代理对象!使用代理对象,你想对目标对象的属性操作全部改为对代理对象相同属性的操作,代理对象提供了对属性获取 [[get]] 修改 [[set]] 等操作的拦截,js将这种拦截称为trap(捕捉器)。

通过捕捉器,我们就可以捕获到 代码中对属性的操作时机,让我们能够先执行我们自定义的业务逻辑代码。因为我们对目标对象的属性操作改为了对代理对象相同的属性操作,所以我们在最后需要通过Reflact执行目标对象的原始操作。

var consume = 0
// 目标对象
var obj = {wallet: 100}
// 捕获器trap
var handlers = {
	set(target, key, val) {
		// target 目标对象
		// key 代理对象要修改的属性
		
		// 记录一笔消费
		consume++
		// 通过Reflact对象触发原始目标对象的属性操作
		// 相当于执行 target[key] = val
		Reflect.set(target, key, val)
	}
}
// 代理对象
var pObj = new Proxy(obj, handlers)
// 将对目标对象obj的属性wallet操作改为代理对象相同属性wallet的操作
pObj.wallet = 97
pObj.wallet = 94
pObj.wallet = 91
pObj.wallet = 88
pObj.wallet = 85

console.log(obj.wallet) // 85
console.log(consume) // 5

如何取消代理

假如某一天,你实现了财务自由,不需要再精打细算记录每一笔消费了,你可能就需要取消此前的代理,代码很简单,往下看:

var consume = 0
var obj = {wallet:  100}
var handlers = {
	set(target, key, val) {
		consume++
		Reflect.set(target, key, val)
	}
}

// 使用Proxy.revocable创建代理
var tmpObj = Proxy.revocable(obj, handlers)
var pObj = tmpObj.proxy
var prevoke = tmpObj.revoke

// 使用代理对象进行消费记账
pObj.wallet = 97
pObj.wallet = 94
pObj.wallet = 91

// 某一天,我们实现了一个小目标
pObj.wallet = 100000000
// 我们不需要记账了,我们需要取消创建的代理
prevoke() // 执行prevoke即可,就是这么简单 哦耶~

pObj.wallet = 99999997 // TypeError 报错啦 (代理取消之后就不能使用了哟!)

代理在后模式

前面的示例都是先执行代理捕获器中的业务逻辑,最后再通过Reflect执行目标对象的属性操作,这种捕获代码操作在前,目标对象操作在后的模式称为“代理在先”模式,有在先,当然就有在后模式。

当然这里的“代理在后”模式并不是先使用Reflect对象触发目标对象属性操作,在执行捕获器中的其他操作代码。而是指代理作为目标对象的一种补充,我们仍然操作的是目标对象,只是当某些操作在目标对象上无法实现时,才使用代理对象。

等会,当某些操作目标对象无法提供时,js会向目标对象的原型prototype上进行查找,所以“代理在后”模式是对目标对象的原型进行代理!

var handlers = {
	get(target, key, context) {
		return function () {
			context.speak(key + '!')
		}
	}
}

var catchall = new Proxy({}, handlers)

var greeter = {
	speak(who = 'someone') {
		console.log('hello ', who)
	}
}

// 将catchall设置为greeter的原型
Object.setPrototypeOf(greeter, catchall)

greeter.speak() // hello someone
greeter.speak('world') // hello world

// 执行greater上不存在的方法
greeter.everyone() // hello everyone!

Reflect

Reflect对象用来触发目标对象执行相应的操作,就是这么简单!

Reflect.get(target, key, context) // 等价于  target[key]
Reflect.set(target, key, val) // 等价于 target[key] = val

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK