跳到主要内容

Proxy 代理

  • Proxy 是 JavaScript 中的一个强大工具,用于定义对象的基本操作的自定义行为(如属性查找、赋值、枚举等)
  • 可以通过 Proxy 来拦截并重新定义对象的操作

创建

  • Proxy 的构造函数接受两个参数
    • 目标对象 (target): 你要代理的对象
    • 处理器对象 (handler): 一个包含“拦截器”(trap)的对象,用于定义拦截行为
  • Proxy 构造函数需要使用 new 关键字调用,并且没有 prototype 属性
let proxy = new Proxy(target, handler);
logd(proxy); // 输出代理对象

常见拦截器(Traps)

  • get(target, prop, receiver): 拦截属性读取操作。
  • set(target, prop, value, receiver): 拦截属性设置操作。
  • has(target, prop): 拦截 in 操作符。
  • deleteProperty(target, prop): 拦截 delete 操作符。
  • apply(target, thisArg, argumentsList): 拦截函数调用。
  • ownKeys(target): 拦截 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols。

1.get

  • 定义一个 get 拦截器,用于拦截属性读取操作
let handler = {
get: function(target, prop) {
logd(`Reading property: ${prop}`);
return target[prop];
}
};
let proxy = new Proxy({}, handler);
logd(proxy.example);
// 输出: Reading property: example
// 输出: undefined

2. set

  • 定义一个 set 拦截器,用于拦截属性设置操作
 let handler = {
set: function (target, prop, value) {
logd(`Setting property: ${prop} to ${value}`);
target[prop] = value;
return true;
}
};
let proxy = new Proxy({}, handler);
proxy.example = 'test'; // 输出: Setting property: example to test

3. has

  • 定义一个 has 拦截器,用于拦截 in 操作符
let handler = {
has: function(target, prop) {
logd(`Checking if property exists: ${prop}`);
return prop in target;
}
};
let proxy = new Proxy({ example: true }, handler);
logd('example' in proxy);
// 输出: Checking if property exists: example
// 输出: true

4.deleteProperty

  • 定义一个 deleteProperty 拦截器,用于拦截 delete 操作符
let handler = {
deleteProperty: function(target, prop) {
logd(`Deleting property: ${prop}`);
delete target[prop];
return true;
}
};
let proxy = new Proxy({ example: true }, handler);
delete proxy.example; // 输出: Deleting property: example

5. apply

  • 定义一个 apply 拦截器,用于拦截函数调用
let handler = {
apply: function(target, thisArg, argumentsList) {
logd('Applying function');
return Reflect.apply(target, thisArg, argumentsList);
}
};
let proxy = new Proxy(function() {}, handler);
proxy(); // 输出: Applying function

6. ownKeys

  • 定义一个 ownKeys 拦截器,用于拦截 Object.getOwnPropertyNamesObject.getOwnPropertySymbols 操作
let handler = {
ownKeys: function(target) {
logd('Getting own keys');
return Reflect.ownKeys(target);
}
};
let proxy = new Proxy({ example: true }, handler);
logd(Object.getOwnPropertyNames(proxy));
// 输出: Getting own keys
// 输出: example

7. Proxy.revocable

  • Proxy.revocable 方法返回一个对象,该对象包含一个 proxy 属性和一个 revoke 方法
  • 可以使用 revoke 方法撤销代理,从而释放内存和资源
  • 使用 revoke 方法后,代理将无法再被访问,并且无法再被撤销
let {proxy, revoke} = Proxy.revocable({}, {});
logd(proxy); // 输出: [object Object]
revoke(); // 撤销代理
logd(proxy); // 输出: TypeError: Illegal operation attempted on a revoked proxy

8. Arrays

  • Proxy 可以用于代理数组,从而实现一些特殊的操作,如拦截数组的索引访问、修改、删除等操作
let handler = {
get: function (target, prop) {
logd(`Reading array index: ${prop}`);
return target[prop];
}
};
let proxy = new Proxy([1, 2, 3], handler);
logd(proxy[0]);
// 输出: Reading array index: 0
// 输出: 1

示例

1. 拦截属性读取和设置

let target = {
name: "Alice",
age: 25
};

let handler = {
get(target, prop) {
if (prop === 'age') {
return target[prop] + " years old";
}
return target[prop];
},
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
target[prop] = value;
return true; // 表示设置成功
}
};

let proxy = new Proxy(target, handler);

logd(proxy.age); // 输出: "25 years old"
proxy.age = 30;
logd(proxy.age); // 输出: "30 years old"

2. 拦截函数调用

let target = function (message) {
return `Hello, ${message}`;
};

let handler = {
apply(target, thisArg, argumentsList) {
let message = argumentsList[0];
return target(message.toUpperCase());
}
};

let proxy = new Proxy(target, handler);

logd(proxy("World")); // 输出: "Hello, WORLD"

3. 添加函数

let target = {
a: 1,
b: 2,
c: 3
};

let handler = {
get(target, prop) {
if (prop === 'sum') {
return function() {
let total = 0;
for (let key in target) {
if (target.hasOwnProperty(key)) {
total += target[key];
}
}
return total;
};
}
return target[prop];
}
};

let proxy = new Proxy(target, handler);

logd(proxy.sum()); // 输出: 6