ES6的Proxy(一)

前段时间看阮一峰老师的es6入门向大家提了一点关于解构赋值的问题之后,有个学长说es6最难懂的应该是Proxy了,所以说有难度的东西就应该挑战着玩一下的说。

然后看了一下觉得怪有意思的。所谓的Proxy,是一个用于修改某些操作的默认行为,原话是:

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

说实话,我并不是很能明白这段话说了什么。简单的学习来看,Proxy给了我一种人如其名的感觉,以网络为原型的话相当于在目标主机与请求方之间建立了一个代理,然后可以肆意的操作两边来往的数据。又有点像laravel的中间件,又有点像python的装饰器。

一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let person = {
name: '咕咕咕',
age: 38
}
let proxy = new Proxy(person, {
get : (target, key) => {
if(key == 'age') {
return target[key] > 18 ? 18 : target[key];
}else{
return target[key];
}
}
})
console.log(proxy.age) //18

这里新建了一个Proxy实例,定义了一个针对获取person对象的属性的拦截行为,即get

一般而言,调用person对象的属性会这么写成这样:person.age,其中的age就是属性名,然后person是对象,然后在Proxy也就是代理内部的话就可以获取到这个对象以及调用属性名,就是targetkey

然后再调用proxyget方法的时候实际上就执行了定义在Proxyget方法。

写在Proxy内的方法的参数名字并不重要,顺序即可似乎。

Proxy支持的拦截操作似乎较多,但是很多都是目前的我不甚熟悉的,所以只做摘录(x

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

对于get方法而言,允许传入的三个参数分别是targetkeyreceiver,分别代表着

  • 代理的目标
  • 获取的参数
  • 读操作所在对象(一般就是proxy的实例)

然后有些注意事项就是即使在proxy内也不能修改来自targetread-only and non-configurable,至于为什么要有这个规则不是很符合我的理解。这不符合一个作为中间件或者代理服务器的行为,说明我对他的理解出现了一些偏差。

比如这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let x = Object.defineProperties({},{
age : {
value : 19,
writable : false,
configurable: false
},
name : {
value : "咕咕咕",
writable : false,
configurable: false
},
})

let proxy = new Proxy(x,{
get: (t,k) => 18
})
console.log(proxy.age)

这样的代码当xage确实是18的时候,可以正常返回18

但是当x.age不是18的时候则会遇到报错:

1
TypeError: 'get' on proxy: property 'age' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '19' but got '18')

让人感到有些困惑。