Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
Proxy拦截操作
Proxy拦截操作有13个(也是实例方法)
handler.apply()
- 描述:方法用于拦截函数的调用。
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()
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 操作符。
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()
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() 操作。
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()
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 操作。
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()
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()
- 描述:方法用于拦截对象的读取属性操作。
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()
- 访问属性:
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() 的钩子。
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()
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
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 操作符的代理方法。
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()
- 属性查询:
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()。
let p = new Proxy(target, {
isExtensible: function (target) {},
});
let p = new Proxy(target, {
isExtensible: function (target) {},
});
- 返回:isExtensible方法必须返回一个 Boolean 值或可转换成 Boolean 的值。
- 拦截:
- Object.isExtensible()
- Reflect.isExtensible()
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()
let p = new Proxy(target, {
ownKeys: function (target) {},
});
let p = new Proxy(target, {
ownKeys: function (target) {},
});
- 返回:方法必须返回一个可枚举对象。
- 拦截:
- Object.getOwnPropertyNames()
- Object.getOwnPropertySymbols()
- Object.keys()
- Reflect.ownKeys()
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()的拦截
let p = new Proxy(target, {
preventExtensions: function (target) {},
});
let p = new Proxy(target, {
preventExtensions: function (target) {},
});
- 返回:返回一个布尔值。
- 拦截:
- Object.preventExtensions()
- Reflect.preventExtensions()
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()
- 描述:方法是设置属性值操作的捕获器。
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()
- 指定属性值:
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()
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()
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 实例。
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()的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。