Skip to content

Proxy

  • Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

  • Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

  • Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

Proxy拦截操作

Proxy拦截操作有13个(也是实例方法)

handler.apply()

  • 描述:方法用于拦截函数的调用。
javascript
let p = new Proxy(target, {
    apply: function (target, thisArg, argumentsList) {},
});
let p = new Proxy(target, {
    apply: function (target, thisArg, argumentsList) {},
});
  • 参数

    [target]:目标对象(或函数)。
    [thisArg]:被调用时的上下文对象。
    [argumentsList]:被调用时的参数数组。

  • 返回:可以是任意类型。

  • 拦截

    • proxy(...args)
    • Function.prototype.apply()
    • Function.prototype.call()
    • Reflect.apply()
javascript
let proxy = new Proxy(function () {}, {
    apply: function (target, thisArg, argumentsList) {
        console.log("called: " + argumentsList.join(", "));
        return argumentsList[0] + argumentsList[1] + argumentsList[2];
    },
});
proxy(1, 2, 3); // "called: 1, 2, 3"; outputs 6
let proxy = new Proxy(function () {}, {
    apply: function (target, thisArg, argumentsList) {
        console.log("called: " + argumentsList.join(", "));
        return argumentsList[0] + argumentsList[1] + argumentsList[2];
    },
});
proxy(1, 2, 3); // "called: 1, 2, 3"; outputs 6

handler.construct()

  • 描述:方法用于拦截 new 操作符。
javascript
let p = new Proxy(target, {
  construct: function (target, argumentsList, newTarget) { },
});
let p = new Proxy(target, {
  construct: function (target, argumentsList, newTarget) { },
});
  • 参数

    [target]:目标对象。
    [argumentsList]:constructor 的参数列表。
    [newTarget]:最初被调用的构造函数,就上面的例子而言是 p。

  • 返回:必须返回一个对象。

  • 拦截

    • new proxy(...args)
    • Reflect.construct()
javascript
let p = new Proxy(function () {}, {
    construct: function (target, argumentsList, newTarget) {
        console.log("called: " + argumentsList.join(", "));
        return { value: argumentsList[0] * 10 };
    },
});
new p(1).value; // "called: 1"; outputs 10
let p = new Proxy(function () {}, {
    construct: function (target, argumentsList, newTarget) {
        console.log("called: " + argumentsList.join(", "));
        return { value: argumentsList[0] * 10 };
    },
});
new p(1).value; // "called: 1"; outputs 10

handler.defineProperty()

  • 描述: 用于拦截对象的 Object.defineProperty() 操作。
javascript
let p = new Proxy(target, {
  defineProperty: function (target, property, descriptor) { },
});
let p = new Proxy(target, {
  defineProperty: function (target, property, descriptor) { },
});
  • 参数

    [target]:目标对象。
    [property]:待检索其描述的属性名。
    [descriptor]:待定义或修改的属性的描述符。

  • 返回:方法必须以一个 Boolean 返回,表示定义该属性的操作成功与否。

  • 拦截

    • Object.defineProperty()
    • Reflect.defineProperty()
javascript
let p = new Proxy({}, {
    defineProperty: function (target, prop, descriptor) {
        console.log("called: " + prop);
        return true;
    },
});
let desc = { configurable: true, enumerable: true, value: 10 };
Object.defineProperty(p, "a", desc); // "called: a"
let p = new Proxy({}, {
    defineProperty: function (target, prop, descriptor) {
        console.log("called: " + prop);
        return true;
    },
});
let desc = { configurable: true, enumerable: true, value: 10 };
Object.defineProperty(p, "a", desc); // "called: a"

handler.deleteProperty()

  • 描述:方法用于拦截对对象属性的 delete 操作。
javascript
let p = new Proxy(target, {
  deleteProperty: function (target, property) {},
});
let p = new Proxy(target, {
  deleteProperty: function (target, property) {},
});
  • 参数

    [target]:目标对象。
    [property]:待删除的属性名。

  • 返回:方法必须以一个 Boolean 返回,表示了该属性是否被成功删除。

  • 拦截

    • delete proxy[foo]
    • delete proxy.foo
    • Reflect.deleteProperty()
javascript
let p = new Proxy({}, {
    deleteProperty: function (target, prop) {
       console.log("called: " + prop);
       return true;
    },
  },
);
delete p.a; // "called: a"
let p = new Proxy({}, {
    deleteProperty: function (target, prop) {
       console.log("called: " + prop);
       return true;
    },
  },
);
delete p.a; // "called: a"

handler.get()

  • 描述:方法用于拦截对象的读取属性操作。
javascript
let p = new Proxy(target, {
  get: function (target, property, receiver) {},
});
let p = new Proxy(target, {
  get: function (target, property, receiver) {},
});
  • 参数

    [target]:目标对象。
    [property]:待删除的属性名。
    [receiver]:Proxy 或者继承 Proxy 的对象。

  • 返回:可以返回任何值。

  • 拦截

    • 访问属性:proxy[foo] 和 proxy.bar
    • 访问原型链上的属性:Object.create(proxy)[foo]
    • Reflect.get()
javascript
let p = new Proxy({},{
    get: function (target, prop, receiver) {
        console.log("called: " + prop);
        return 10;
    },
  },
);
console.log(p.a); // "called: a"; ouptut 10
let p = new Proxy({},{
    get: function (target, prop, receiver) {
        console.log("called: " + prop);
        return 10;
    },
  },
);
console.log(p.a); // "called: a"; ouptut 10

handler.getOwnPropertyDescriptor()

  • 描述:方法是 Object.getOwnPropertyDescriptor() 的钩子。
javascript
let p = new Proxy(target, {
  getOwnPropertyDescriptor: function (target, prop) {},
});
let p = new Proxy(target, {
  getOwnPropertyDescriptor: function (target, prop) {},
});
  • 参数

    [target]:目标对象。
    [prop]:返回属性名称的描述。

  • 返回:必须返回一个 object 或 undefined。
  • 拦截
    • Object.getOwnPropertyDescriptor()
    • Reflect.getOwnPropertyDescriptor()
javascript
let p = new Proxy({ a: 20 },{
    getOwnPropertyDescriptor: function (target, prop) {
      console.log("called: " + prop);
      return { configurable: true, enumerable: true, value: 10 };
    },
  },
);
Object.getOwnPropertyDescriptor(p, "a").value; // "called: a"; output 10
let p = new Proxy({ a: 20 },{
    getOwnPropertyDescriptor: function (target, prop) {
      console.log("called: " + prop);
      return { configurable: true, enumerable: true, value: 10 };
    },
  },
);
Object.getOwnPropertyDescriptor(p, "a").value; // "called: a"; output 10

handler.getPrototypeOf()

  • 描述:当读取代理对象的原型时,该方法就会被调用。
  • 返回:返回值必须是一个对象或者 null。
  • 5种方法会出发
    • Object.getPrototypeOf()
    • Reflect.getPrototypeOf()
    • Object.prototype.__proto__
    • Object.prototype.isPrototypeOf()
    • instanceof
javascript
let p = new Proxy({}, {
  getPrototypeOf(target) {
    return Array.prototype;
  },
});
Object.getPrototypeOf(p) === Array.prototype, // true
Reflect.getPrototypeOf(p) === Array.prototype, // true
p.__proto__ === Array.prototype, // true
Array.prototype.isPrototypeOf(p), // true
p instanceof Array, // true
let p = new Proxy({}, {
  getPrototypeOf(target) {
    return Array.prototype;
  },
});
Object.getPrototypeOf(p) === Array.prototype, // true
Reflect.getPrototypeOf(p) === Array.prototype, // true
p.__proto__ === Array.prototype, // true
Array.prototype.isPrototypeOf(p), // true
p instanceof Array, // true

handler.has()

  • 描述:是针对 in 操作符的代理方法。
javascript
let p = new Proxy(target, {
  has: function (target, prop) {},
});
let p = new Proxy(target, {
  has: function (target, prop) {},
});
  • 参数

    [target]:目标对象。
    [prop]:需要检查是否存在的属性。

  • 返回:返回一个 boolean 属性的值。

  • 拦截

    • 属性查询:foo in proxy
    • 继承属性查询:foo in Object.create(proxy)
    • with 检查: with(proxy) { (foo); }
    • Reflect.has()
javascript
let p = new Proxy({},{
    has: function (target, prop) {
      console.log("called: " + prop);
      return true;
    },
  },
);
"a" in p; // "called: a"; outputs true
let p = new Proxy({},{
    has: function (target, prop) {
      console.log("called: " + prop);
      return true;
    },
  },
);
"a" in p; // "called: a"; outputs true

handler.isExtensible()

  • 描述:用于拦截对对象的 Object.isExtensible()。
javascript
let p = new Proxy(target, {
  isExtensible: function (target) {},
});
let p = new Proxy(target, {
  isExtensible: function (target) {},
});
  • 返回:isExtensible方法必须返回一个 Boolean 值或可转换成 Boolean 的值。
  • 拦截
    • Object.isExtensible()
    • Reflect.isExtensible()
javascript
let p = new Proxy({},{
    isExtensible: function (target) {
      console.log("called");
      return true; // 也可以 return 1; 等表示为 true 的值
    },
  },
);
Object.isExtensible(p); // "called"; outputs true
let p = new Proxy({},{
    isExtensible: function (target) {
      console.log("called");
      return true; // 也可以 return 1; 等表示为 true 的值
    },
  },
);
Object.isExtensible(p); // "called"; outputs true

handler.ownKeys()

  • 描述:方法用于拦截 Reflect.ownKeys()
javascript
let p = new Proxy(target, {
  ownKeys: function (target) {},
});
let p = new Proxy(target, {
  ownKeys: function (target) {},
});
  • 返回:方法必须返回一个可枚举对象。
  • 拦截
    • Object.getOwnPropertyNames()
    • Object.getOwnPropertySymbols()
    • Object.keys()
    • Reflect.ownKeys()
javascript
let p = new Proxy({},{
    ownKeys: function (target) {
      console.log("called");
      return ["a", "b", "c"];
    },
  },
);
Object.getOwnPropertyNames(p); 
// "called"; outputs [ 'a', 'b', 'c' ]
let p = new Proxy({},{
    ownKeys: function (target) {
      console.log("called");
      return ["a", "b", "c"];
    },
  },
);
Object.getOwnPropertyNames(p); 
// "called"; outputs [ 'a', 'b', 'c' ]

handler.preventExtensions()

  • 描述:用于设置对Object.preventExtensions()的拦截
javascript
let p = new Proxy(target, {
  preventExtensions: function (target) {},
});
let p = new Proxy(target, {
  preventExtensions: function (target) {},
});
  • 返回:返回一个布尔值。
  • 拦截
    • Object.preventExtensions()
    • Reflect.preventExtensions()
javascript
let p = new Proxy({},{
    preventExtensions: function (target) {
      console.log("called");
      Object.preventExtensions(target);
      return true;
    },
  },
);
Object.preventExtensions(p); 
// "called"; outputs false
let p = new Proxy({},{
    preventExtensions: function (target) {
      console.log("called");
      Object.preventExtensions(target);
      return true;
    },
  },
);
Object.preventExtensions(p); 
// "called"; outputs false

handler.set()

  • 描述:方法是设置属性值操作的捕获器。
javascript
new Proxy(target, {
  set(target, property, value, receiver) {}
});
new Proxy(target, {
  set(target, property, value, receiver) {}
});
  • 参数

    [target]:目标对象。
    [property]:将被设置的属性名或 Symbol。
    [value]:新属性值。
    [receiver]:最初接收赋值的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。

  • 返回:方法应当返回一个布尔值。

  • 拦截

    • 指定属性值:proxy[foo] = bar 和 proxy.foo = bar
    • 指定继承者的属性值:Object.create(proxy)[foo] = bar
    • Reflect.set()
javascript
let p = new Proxy({},{
    set: function (target, prop, value, receiver) {
      target[prop] = value;
      console.log("property set: " + prop + " = " + value);
      return true;
    },
  },
);
console.log("a" in p); // false
p.a = 10; // "property set: a = 10"
console.log("a" in p); // true
console.log(p.a); // 10
let p = new Proxy({},{
    set: function (target, prop, value, receiver) {
      target[prop] = value;
      console.log("property set: " + prop + " = " + value);
      return true;
    },
  },
);
console.log("a" in p); // false
p.a = 10; // "property set: a = 10"
console.log("a" in p); // true
console.log(p.a); // 10

handler.setPrototypeOf()

  • 描述:主要用来拦截 Object.setPrototypeOf()
javascript
let p = new Proxy(target, {
  setPrototypeOf: function (target, prototype) {},
});
let p = new Proxy(target, {
  setPrototypeOf: function (target, prototype) {},
});
  • 参数

    [target]:被拦截目标对象。
    [prototype]:对象新原型或为null。

  • 返回:如果成功修改了[[Prototype]], setPrototypeOf 方法返回 true,否则返回 false。

  • 拦截

    • Object.setPrototypeOf()
    • Reflect.setPrototypeOf()
javascript
let handlerReturnsFalse = {
  setPrototypeOf(target, newProto) {
    return false;
  },
};
let newProto = {};
let target = {};
let p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false
let handlerReturnsFalse = {
  setPrototypeOf(target, newProto) {
    return false;
  },
};
let newProto = {};
let target = {};
let p1 = new Proxy(target, handlerReturnsFalse);
Object.setPrototypeOf(p1, newProto); // throws a TypeError
Reflect.setPrototypeOf(p1, newProto); // returns false

Proxy静态方法

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
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked

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