Proxy这个单词,翻译过来,就是代理,是 ES6
中提供的新 API
Proxy对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)
(在文档中被称为 traps(陷阱)
,我觉得可以理解为针对对象各种行为的钩子)
const p = new Proxy(target, handler)
target 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
let target = {}
let handlers = {} // do nothing
let proxy = new Proxy(target, handlers)
proxy.a = 123
console.log(target.a) // 123
在第二个参数为空对象的情况下,基本可以理解为是对第一个参数做的一次浅拷贝(Proxy必须是浅拷贝,如果是深拷贝则会失去了代理的意义)
getter
、setter
属性let obj = {
_age: 20,
get age() {
return `I'm ${this._age} years old`
},
set age(val) {
this._age = Number(val)
}
}
console.log(obj.age) // I'm 20 years old
obj.age = 21
console.log(obj.age) // I'm 21 years old
就像这段代码描述的一样,我们设置了一个属性 _age
,然后又设置了一个 get age
和 set age
然后我们可以直接调用 obj.age
来获取一个返回值,也可以对其进行赋值。
这么做有几个缺点:
getter
、setter
key
(如果我们直接在getter
里边调用this.age
则会出现堆栈溢出的情况,因为无论何时调用this.age
进行取值都会触发getter
)。
Proxy
很好的解决了这两个问题:let obj = {
name: ‘hk’,
age: 20
}
let handlers = {
get(target, property) {
if (property === ‘name') {
return `My name is ${target[property]}`
} else if (property === ‘age’) {
return `I am ${target[property]} years old`
}
return target[property]
}
set (target, property, value) {
target[property] = value
}
}
let proxy = new Proxy(obj, handlers)
console.log(proxy.name, obj.name) // My name is hk hk
console.log(proxy.age, obj.age) // I'm 20 years old 20
obj.age = 21
console.log(obj.age) // 21
我们通过创建get
、set
两个trap
来统一管理所有的操作,可以看到,在修改proxy
的同时,obj
的内容也被修改,而且我们对proxy
的行为进行了一些特殊的处理。
而且我们无需额外的用一个key
来存储真实的值,因为我们在trap
内部操作的是target
对象,而不是proxy
对象
确定一个对象是否是一个代理是不可能的 根据Javascript语言规范,无法确定对象是否是代理。 但是,在 Node 10+上,可以使用 util.types.isProxy 方法。