以下内容为学习记录,可以参考 MDN 原文。
环境
- node v12.18.1
- npm 6.14.5
- vscode 1.46
- Microsoft Edge 83
概念
Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
静态方法 revocable
Proxy.revocable() 方法可以用来创建一个可撤销的代理对象。
var revocable = Proxy.revocable({}, {
get: function(target, name) {
return "[[" + name + "]]";
}
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
revocable.revoke();
console.log(proxy.foo); // TypeError is thrown
proxy.foo = 1 // TypeError again
delete proxy.foo; // still TypeError
typeof proxy // "object", typeof doesn't trigger any trap
apply
handler.apply() 方法用于拦截函数的调用。
function sum(a, b) {
return a + b;
}
const handler = {
apply: function(target, thisArg, argumentsList) {
console.log(`Calculate sum: ${argumentsList}`);
// expected output: "Calculate sum: 1,2"
return target(argumentsList[0], argumentsList[1]) * 10;
}
};
const proxy1 = new Proxy(sum, handler);
console.log(sum(1, 2));
// expected output: 3
console.log(proxy1(1, 2));
// expected output: 30
construct
handler.construct() 方法用于拦截 new 操作符。为了使 new 操作符在生成的 Proxy 对象上生效,用于初始化代理的目标对象自身必须具有 [[Construct]] 内部方法(即 new target 必须是有效的)。
function monster1(disposition) {
this.disposition = disposition;
}
const handler1 = {
construct(target, args) {
console.log('monster1 constructor called');
// expected output: "monster1 constructor called"
return new target(...args);
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(new proxy1('fierce').disposition);
// expected output: "fierce"
defineProperty
handler.defineProperty() 用于拦截对对象的 Object.defineProperty() 操作。
const handler1 = {
defineProperty(target, key, descriptor) {
invariant(key, 'define');
return true;
}
};
function invariant(key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
const monster1 = {};
const proxy1 = new Proxy(monster1, handler1);
console.log(proxy1._secret = 'easily scared');
// expected output: Error: Invalid attempt to define private "_secret" property
deleteProperty
handler.deleteProperty() 方法用于拦截对对象属性的 delete 操作。
const monster1 = {
texture: 'scaly'
};
const handler1 = {
deleteProperty(target, prop) {
if (prop in target) {
delete target[prop];
console.log(`property removed: ${prop}`);
// expected output: "property removed: texture"
}
}
};
console.log(monster1.texture);
// expected output: "scaly"
const proxy1 = new Proxy(monster1, handler1);
delete proxy1.texture;
console.log(monster1.texture);
// expected output: undefined
get
handler.get() 方法用于拦截对象的读取属性操作。
const monster1 = {
secret: 'easily scared',
eyeCount: 4
};
const handler1 = {
get: function(target, prop, receiver) {
if (prop === 'secret') {
return `${target.secret.substr(0, 4)} ... shhhh!`;
}
return Reflect.get(...arguments);
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(proxy1.eyeCount);
// expected output: 4
console.log(proxy1.secret);
// expected output: "easi ... shhhh!"
getOwnPropertyDescriptor
handler.getOwnPropertyDescriptor() 方法是 Object.getOwnPropertyDescriptor() 的钩子。
const monster1 = {
eyeCount: 4
};
const handler1 = {
getOwnPropertyDescriptor(target, prop) {
console.log(`called: ${prop}`);
// expected output: "called: eyeCount"
return { configurable: true, enumerable: true, value: 5 };
}
};
const proxy1 = new Proxy(monster1, handler1);
console.log(Object.getOwnPropertyDescriptor(proxy1, 'eyeCount').value);
// expected output: 5
getPrototypeOf
handler.getPrototypeOf() 是一个代理(Proxy)方法,当读取代理对象的原型时,该方法就会被调用。
const monster1 = {
eyeCount: 4
};
const monsterPrototype = {
eyeCount: 2
};
const handler = {
getPrototypeOf(target) {
return monsterPrototype;
}
};
const proxy1 = new Proxy(monster1, handler);
console.log(Object.getPrototypeOf(proxy1) === monsterPrototype);
// expected output: true
console.log(Object.getPrototypeOf(proxy1).eyeCount);
// expected output: 2