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 方法拦截函数的调用、 call 和 apply 操作。 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 代理 target,target.m() 内部的 this 就是指向 proxy,而不是 target。所以,虽然 proxy 没有做任何拦截,target.m() 和 proxy.m() 返回不一样的结果。

参考 ​

Proxy - ECMAScript 6入门

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

用 ♥ 撰写