JavaScript: Proxy拦截属性

JavaScript ES6中的Proxy,作用是拦截对象的属性或函数的调用

The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).

看看文档说明:比如属性查找、赋值、枚举、函数调用。

乍看起来好像可以拦截对象的所有操作,实际上,只能拦截部分操作。以下是Proxy能拦截的方法。

  • handler.getPrototypeOf(): A trap for Object.getPrototypeOf.
  • handler.setPrototypeOf(): A trap for Object.setPrototypeOf.
  • handler.isExtensible(): A trap for Object.isExtensible.
  • handler.preventExtensions(): A trap for Object.preventExtensions.
  • handler.getOwnPropertyDescriptor(): A trap for Object.getOwnPropertyDescriptor.
  • handler.defineProperty(): A trap for Object.defineProperty.
  • handler.has(): A trap for the in operator.
  • handler.get(): A trap for getting property values.
  • handler.set(): A trap for setting property values.
  • handler.deleteProperty(): A trap for the delete operator.
  • handler.ownKeys(): A trap for Object.getOwnPropertyNames and - - Object.getOwnPropertySymbols.
  • handler.apply(): A trap for a function call.
  • handler.construct(): A trap for the new operator.

虽然罗列了很多,但Proxy能不能拦截对象的方法呢?答案是:能,但也不能。

虽然handler提供了get方法,但它的作用是拦截对象的属性。对象的方法,本身也属于对象的属性,但只提供一个属性名时,你却无法判断它是数据变量还是函数。

看看Proxy如何拦截属性:

1
2
3
4
5
6
let p = new Proxy({}, {
get: function(target, name) {
return 123;
}
});
console.log(p.a);

以上代码中,p是对象{}的代理,如果直接访问{}.a,会找不到属性a,返回undefined。但如果由p代理,并由get方法进行拦截,则返回了123。

再来看看Proxy如何拦截方法:

1
2
3
4
5
6
7
8
9
let p = new Proxy({}, {
get: function(target, name) {
return function(...args) {
console.log('call function', name, args);
return 123;
};
}
});
console.log(p.f('abc'));

以上代码,如果直接调用{}.f(),会找不到方法而报错。由p代理之后,通过get方法拦截,返回一个“输出log并返回123”的函数。

可见,Proxy还是能够拦截对象的方法。

然而,就目前来看,在属性并未定义的情况下,Proxy并无法既拦截对象的数据变量又拦截对象方法,因为你不知道这个属性是数据还是函数。JavaScript作为一种解释型语言,在没运行到某个语句时,并不知道会不会出错。直接调用p.f或p.a(),都有可能是对的。在这种情况下,要区分两者并没那么容易。

当然,也可以采取一些折中的办法。比如统一使用函数,或者以命名方式来区分。

参考