Skip to content
字数
882 字
阅读时间
4 分钟

概念

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

Proxy 实际上重载(overload)了点运算符,即用自己的定义覆盖了语言的原始定义

注意,要使得 Proxy 起作用,必须针对 Proxy 实例(上例是 proxy 对象)进行操作,而不是针对目标对象(上例是空对象)进行操作

示例

如果 handler 没有设置任何拦截,那就等同于直接通向原对象。

同一个拦截器函数,可以设置拦截多个操作。

javascript
var handler = {
  get: function(target, name) {
    if (name === 'prototype') {
      return Object.prototype;
    }
    return 'Hello, ' + name;
  },

  apply: function(target, thisBinding, args) {
    return args[0];
  },

  construct: function(target, args) {
    return {value: args[1]};
  }
};

var fproxy = new Proxy(function(x, y) {
  return x + y;
}, handler);

fproxy(1, 2) // 1
new fproxy(1, 2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo === "Hello, foo" // true

对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错

拦截操作

apply()

apply 方法拦截函数的调用、 callapply 操作。 apply 方法可以接受三个参数,分别是目标对象、目标对象的上下文对象( this )和目标对象的参数数组

has()

has() 拦截只对 in 运算符生效,对 for...in 循环不生效,导致不符合要求的属性没有被 for...in 循环所排除

receiver 含义

Proxy 或者继承于 Proxy 的对象

js
let user = {
    _name: "张三",
    get name() {
        return this._name;
    }
};

let userProxy = new Proxy(user, {
    get(target, prop, receiver) {
        return Reflect.get(target, prop);
		// return Reflect.get(target, prop, receiver); // 李四
        // return target[prop]; // (*) target = user
    }
});

let admin = {
    __proto__: userProxy,
    _name: "李四"
};

// 期待 『李四』,却输出了 『张三』(?!?)
console.log(admin.name); // => 张三

Proxy.revocable()

Proxy.revocable() 方法返回一个可取消的 Proxy 实例。

javascript
let target = {};
let handler = {};

let {proxy, revoke} = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo // 123

revoke();
proxy.foo // TypeError: Revoked

Proxy.revocable() 方法返回一个对象,该对象的 proxy 属性是 Proxy 实例,revoke 属性是一个函数,可以取消 Proxy 实例。上面代码中,当执行 revoke 函数之后,再访问 Proxy 实例,就会抛出一个错误。

Proxy.revocable() 的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问

this 指向

虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的 this 关键字会指向 Proxy 代理。

javascript
const target = {
  m: function () {
    console.log(this === proxy);
  }
};
const handler = {};

const proxy = new Proxy(target, handler);

target.m() // false
proxy.m()  // true

上面代码中,一旦 proxy 代理 targettarget.m() 内部的 this 就是指向 proxy,而不是 target。所以,虽然 proxy 没有做任何拦截,target.m()proxy.m() 返回不一样的结果。

参考

Proxy - ECMAScript 6入门

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

撰写