Proxy
是 ES6 引入的一种强大的拦截机制,用于定义对象的基本操作(如读取、赋值、删除等)的自定义行为。相较于 Object.defineProperty
,Proxy
提供了更灵活、全面的拦截能力。
1. Proxy 语法
const proxy = new Proxy(target, handler);
target
:被代理的对象handler
:定义拦截行为的对象
2. Proxy 基本用法
(1) 拦截对象的属性访问
const person = {
name: "Alice",
age: 25,
};
const proxyPerson = new Proxy(person, {
get(target, prop) {
console.log(`访问属性: ${prop}`);
return prop in target ? target[prop] : "属性不存在";
},
});
console.log(proxyPerson.name); // 访问属性: name -> "Alice"
console.log(proxyPerson.gender); // 访问属性: gender -> "属性不存在"
(2) 拦截对象的属性修改
const proxyPerson = new Proxy(person, {
set(target, prop, value) {
if (prop === "age" && typeof value !== "number") {
throw new Error("年龄必须是数字");
}
target[prop] = value;
console.log(`设置 ${prop} 为 ${value}`);
return true;
},
});
proxyPerson.age = 30; // 设置 age 为 30
proxyPerson.age = "abc"; // 抛出错误: 年龄必须是数字
(3) 拦截对象的属性删除
const proxyPerson = new Proxy(person, {
deleteProperty(target, prop) {
console.log(`删除属性: ${prop}`);
return delete target[prop];
},
});
delete proxyPerson.age; // 删除属性: age
(4) 拦截 in 操作符 (has
方法)
const proxyPerson = new Proxy(person, {
has(target, prop) {
console.log(`检查属性是否存在: ${prop}`);
return prop in target;
},
});
console.log("name" in proxyPerson); // 检查属性是否存在: name -> true
console.log("gender" in proxyPerson); // 检查属性是否存在: gender -> false
const range = { start: 10, end: 50 };
const proxy = new Proxy(range, {
has(target, prop) {
return prop >= target.start && prop <= target.end;
}
});
console.log(15 in proxy); // true
console.log(60 in proxy); // false
(5) 拦截函数调用 (apply
方法)
const multiply = new Proxy((a, b) => a * b, {
apply(target, thisArg, args) {
console.log(`调用函数 multiply,参数: ${args}`);
return target(...args);
}
});
console.log(multiply(3, 4)); // 调用函数 multiply,参数: 3,4 -> 12
(6) 拦截构造函数 (construct
方法)
const Person = new Proxy(class {
constructor(name) {
this.name = name;
}
}, {
construct(target, args) {
console.log(`创建实例,参数: ${args}`);
return new target(...args);
}
});
const user = new Person("Alice"); // 创建实例,参数: Alice
特点:
- 可以 监听整个对象,而不是单个属性。
- 能拦截 所有操作(如
get
、set
、has
、deleteProperty
、apply
等)。 - 可以用于 动态代理,使得代码更具扩展性。
3. Proxy 实际使用场景
(1) 数据验证和格式化
const user = new Proxy({}, {
set(target, prop, value) {
if (prop === "age" && typeof value !== "number") {
throw new Error("年龄必须是数字");
}
target[prop] = value;
return true;
}
});
(2) 实现私有属性和方法
const createUser = () => {
const privateData = new WeakMap();
return new Proxy({}, {
get(target, prop) {
if (prop.startsWith("_")) {
throw new Error("无法访问私有属性");
}
return target[prop];
}
});
};
(3) 添加日志记录和调试功能
const logger = new Proxy({}, {
get(target, prop) {
console.log(`访问属性: ${prop}`);
return target[prop];
}
});
(4) 提供默认值和只读访问
const defaultSettings = new Proxy({}, {
get(target, prop) {
return prop in target ? target[prop] : "默认值";
},
set() {
throw new Error("设置操作被禁止");
}
});
(5) 实现惰性加载和缓存
const lazyObject = new Proxy({}, {
get(target, prop) {
if (!(prop in target)) {
console.log(`初始化 ${prop}`);
target[prop] = prop.toUpperCase();
}
return target[prop];
}
});
(6) 解决 this
指向问题
const obj = {
name: "Alice",
greet() {
return `Hello, ${this.name}`;
}
};
const proxyObj = new Proxy(obj, {
get(target, prop, receiver) {
return typeof target[prop] === "function" ? target[prop].bind(target) : target[prop];
}
});
const greet = proxyObj.greet;
console.log(greet()); // Hello, Alice
4.Object.defineProperty
Object.defineProperty()
允许直接在对象上定义新的属性,或者修改已有属性的特性(如可读写性、是否可枚举等)。
示例:
const person = {};
Object.defineProperty(person, "name", {
value: "Alice",
writable: false, // 不能修改
enumerable: true,
configurable: false
});
console.log(person.name); // Alice
person.name = "Bob"; // 失败,严格模式下会报错
console.log(person.name); // Alice
特点:
- 只能加工 单个属性,不能监听整个对象。
- 只能 定义静态的行为,不能动态处理对象属性的操作。
- 不能拦截 删除、新增属性 或 函数调用。
5. Proxy
与 Object.defineProperty
详细对比
特性 | Object.defineProperty | Proxy |
---|---|---|
监听属性读取 | ❌ 不支持 | ✅ 支持 (get ) |
监听属性赋值 | ✅ 支持 (set ) | ✅ 支持 (set ) |
监听属性删除 | ❌ 不支持 | ✅ 支持 (deleteProperty ) |
监听属性存在性 | ❌ 不支持 | ✅ 支持 (has ) (in 关键字) |
监听对象新增属性 | ❌ 不支持 | ✅ 支持 (set ) |
监听函数调用 | ❌ 不支持 | ✅ 支持 (apply ) |
监听构造函数 | ❌ 不支持 | ✅ 支持 (construct ) |
监听整个对象 | ❌ 需要对每个属性定义 | ✅ 一次性监听整个对象 |
适用于数组或集合 | ❌ 不适合 | ✅ 适合 |
可扩展性 | ❌ 需手动定义 | ✅ 更强大,支持代理嵌套 |