Proxy,框架的响应式基础(1): 初识 Proxy

一、引言

想象这样一个场景:银行中有一个保险柜,其中存放了100枚金币5颗钻石,我们用safe对象来表示:

// 保险柜对象
const safe = {
	gold: 100, // 100枚金币
	diamond: 5, // 5颗钻石
}

然而,由于保险柜密码被多名员工掌握,任何人都能直接操作保险柜内的物品:

safe.gold -= 50 // 被偷走50个金币
safe.diamond -= 3 // 被偷走3颗钻石

这导致库存数据频繁异常,却无法追溯操作记录和责任人。银行行长为此头疼不已,直到引入了一台智能机器人代理系统—— 所有对保险柜的操作必须通过机器人完成,机器人会根据预设规则校验权限、验证操作合法性,并记录下全程日志。

下面请你带入该场景,我和一起探讨框架响应式基础—— Proxy

二、什么是Proxy?

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义

我们可以将 Proxy 形象化为银行行长引入的 「智能机器人代理系统」。当使用 new Proxy(target, handler) 代理一个对象时(类比为用机器人系统接管保险柜),Proxy 会在对象的基础操作层插入一层「透明拦截器」,自动拦截并处理该操作。

首先我们需要创建一个对象的代理:

// 保险柜对象
const safe = {
	gold: 100, // 100枚金币
	diamond: 5, // 5颗钻石
}
// 创建 safe 对象的代理
const safeProxy = new Proxy(safe, handle)

1、get 读取拦截

这样银行家的智能机器人代理系统就有了个大概样子。但是我们现在还没有给这个系统添加规则,告诉它什么时候应该拦截,也就是补全上面代码中的 handle对象。

const handle = {
	get: function(target, prop) {
		console.log(`${prop}属性被读取!`)
	},
}

在上面代码中,我们在 handle 对象中添加 get 方法,该方法在对象的属性被读取时调用(员工取出物品时执行),该函数接受三个参数:

  • target: 被代理对象,这里指safe(保险柜)
  • prop: 被读取的属性名
  • receiver: 暂不介绍

现在我们的操作都要经过智能机器人代理系统哦~

console.log(safeProxy.gold)
// gold 属性被读取
// undefined

可以看到,当我们试图读取 gold 属性的时候,打印 gold 属性被调用,可见拦截器 get 确实拦截到了外界对对象属性的读取操作。

但是为什么当我们打印的代码会显示 undefined 呢?原来 get 拦截器除了拦截读取操作外,还需要决定返回值(返回给正在读取的对象什么值)。如果我们放行本次操作可以这样更改:

const handle = {
	get: function(target, prop) {
		console.log(`${prop}属性被读取!`)
		return target[prop] // 将对象的值返回给读取者
	},
}

这样我们的打印语句就可以正常打印了。

现在银行家想更改规则,由于金币比较多,所以允许员工获取,但是钻石数量少,所以当员工获取时需要返回 ‘diamond not allowed’ 的字样,你知道如何修改代码了吗?

// 保险柜对象
const safe = {
	gold: 100, // 100枚金币
	diamond: 5, // 5颗钻石
}

const handle = {
	get: function(target, prop) {
		if (prop === 'gold') {
			return target[prop]
		} else if (prop === 'diamond') {
			return 'diamond not allowed'
		}
		return '无此属性'
	},
}

// 创建 safe 对象的代理
const safeProxy = new Proxy(safe, handle)

console.log(safeProxy.gold)  // 100
console.log(safeProxy.diamond)  // diamond not allowed
console.log(safeProxy.lalala)  // 无此属性

2、set 设置拦截

回到最初的场景,当员工前来存钱的时候,也需要经过该系统,此时也必须有一个设置拦截规则来对员工的操作进行拦截和校验。我们需要在 handle对象中添加 set函数来处理值变更时的拦截逻辑:

const handle = {
	get: // ... 逻辑忽略
	set: function(target, prop, value, receiver) {
		return true // 当我们 return true 表示允许本次修改
		return false // 当我们 return false 表示拦截这次修改
	}
}

该函数接受四个参数:

  • target: 被代理对象,这里指safe(保险柜)
  • prop: 被读取的属性名
  • value: 更改后的值
  • receiver: 暂不介绍

如果银行家希望金币的数量不低于50枚,钻石不低于3颗以防库存不足,这我们该怎么实现呢?

// 保险柜对象
const safe = {
	gold: 100, // 100枚金币
	diamond: 5, // 5颗钻石
}

const handle = {
	set: function(target, prop, value) {
		if (prop === 'gold') {
			if (value < 50) return false
			target[prop] = value
			return true
		} else if (prop === 'diamond') {
			if (value < 3) return false
			target[prop] = value
			return true
		}
	}
}

// 创建 safe 对象的代理
const safeProxy = new Proxy(safe, handle)
safeProxy.gold -= 10
console.log(safeProxy.gold)  // 90
safeProxy.gold -= 50
console.log(safeProxy.gold)  // 90 因为 90 -50 = 40 < 50 更改被拦截

三、总结

Proxy 是对象的智能代理,通过拦截读写操作可以实现权限控制、数据校验,避免直接操作对象导致的安全问题和追溯问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值