浅谈ES6中的Proxy

时间:2023-03-08 16:57:45
浅谈ES6中的Proxy

Proxy是一个很有趣的对象,它能够修改某些操作的默认行为,等同于在语言层面做出修改,属于一种‘元编程’,即对编程语言进行编程。

Proxy其实很好理解,就是在目标对象之前架设一层拦截,外界的访问都得通过这层拦截,所以我们可以实现对外界访问的过滤和改写。

Proxy的使用其实很简单,举几个栗子你就清楚了:

我们重新定义属性的读取(get)和设置(set)行为,当我读取Person对象的age属性时,当age属性值大于100时,就让它等于99:

var person = {
name:'fancy',
age:123
}//定义一个对象
var proxy = new Proxy(person,//第一个参数传要代理的对象
{//第二个参数传要重定义的属性
get:(target,key)=>{
if (key == 'age'&& target[key] > 100) {
return 99
}else{
return target[key]
}
}
});
proxy.name //fancy
proxy.age //
person.age //123

我们创建了一个Proxy对象去代理person对象,并将属性的get方法重定义,当属性值等于‘age’并且值大于100时,返回99,否则直接返回属性值。get接收两个参数,第一个是原对象,第二个是属性名。

数据打印出来后,发现proxy的age值变成了99,而person的age值仍然是123,proxy实际上是重载(overload)了点运算符,用自己的定义覆盖了语言的原始定义。


我们再用set方法用来拦截某个属性的赋值操作。

假定Person对象有一个age属性,该属性应该是一个不大于200的整数,那么可以使用Proxy保证age的属性值符合要求。

let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于age以外的属性,直接保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator); person.age = 100; person.age //
person.age = 'young' // 抛出异常:The age is not an integer
person.age = 300 // 抛出异常:The age seems invalid

set方法也常常用来做数据绑定,当对象发生改变时,自动更新视图;还可以用来禁止读写内部属性等等。

Proxy对象可以支持的拦截操作总结了下,大概以下13种:

(1)get(target, propKey, receiver)

拦截对象属性的读取,比如proxy.fooproxy['foo']

最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。

(2)set(target, propKey, value, receiver)

拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。

(3)has(target, propKey)

拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。

(4)deleteProperty(target, propKey)

拦截delete proxy[propKey]的操作,返回一个布尔值。

(5)ownKeys(target)

拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy),返回一个数组。该方法返回对象所有自身的属性,而Object.keys()仅返回对象可遍历的属性。

(6)getOwnPropertyDescriptor(target, propKey)

拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。

(7)defineProperty(target, propKey, propDesc)

拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。

(8)preventExtensions(target)

拦截Object.preventExtensions(proxy),返回一个布尔值。

(9)getPrototypeOf(target)

拦截Object.getPrototypeOf(proxy),返回一个对象。

(10)isExtensible(target)

拦截Object.isExtensible(proxy),返回一个布尔值。

(11)setPrototypeOf(target, proto)

拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。

如果目标对象是函数,那么还有两种额外操作可以拦截。

(12)apply(target, object, args)

拦截Proxy实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)

(13)construct(target, args)

拦截Proxy实例作为构造函数调用的操作,比如new proxy(...args)


网上有很多实例可以参考,这里就不一一列举了,有兴趣自己写框架的朋友,可以深究一下Proxy的实现原理。

浅谈ES6中的Proxy