Proxy用于创建对象的代理。
// Proxy代理对象,拦截并自定义基本操作
const target = {
name: "东巴文",
age: 25
};
const handler = {
get(target, prop) {
console.log(`读取属性: ${prop}`);
return target[prop];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name);
// 读取属性: name
// 东巴文
// new Proxy(target, handler)
// target: 要代理的目标对象
// handler: 定义拦截操作的对象
const target = { x: 1, y: 2 };
const handler = {};
const proxy = new Proxy(target, handler);
console.log(proxy.x); // 1(无拦截,直接访问目标)
Proxy支持的各种拦截操作。
const target = { name: "东巴文" };
const proxy = new Proxy(target, {
get(target, prop, receiver) {
console.log(`获取: ${prop}`);
if (prop in target) {
return target[prop];
}
return "属性不存在";
}
});
console.log(proxy.name); // 东巴文
console.log(proxy.unknown); // 属性不存在
const target = {};
const proxy = new Proxy(target, {
set(target, prop, value, receiver) {
console.log(`设置: ${prop} = ${value}`);
if (typeof value === "number") {
target[prop] = value;
return true;
}
throw new TypeError("值必须是数字");
}
});
proxy.age = 25; // 设置: age = 25
console.log(proxy.age); // 25
// proxy.name = "东巴文"; // TypeError
const target = {
name: "东巴文",
_secret: "hidden"
};
const proxy = new Proxy(target, {
has(target, prop) {
if (prop.startsWith("_")) {
return false; // 隐藏私有属性
}
return prop in target;
}
});
console.log("name" in proxy); // true
console.log("_secret" in proxy); // false
const target = {
name: "东巴文",
_protected: "不可删除"
};
const proxy = new Proxy(target, {
deleteProperty(target, prop) {
if (prop.startsWith("_")) {
throw new Error("不能删除受保护的属性");
}
delete target[prop];
return true;
}
});
delete proxy.name; // 成功
// delete proxy._protected; // Error
function sum(a, b) {
return a + b;
}
const proxy = new Proxy(sum, {
apply(target, thisArg, args) {
console.log(`调用函数,参数: ${args}`);
return target.apply(thisArg, args);
}
});
console.log(proxy(1, 2)); // 调用函数,参数: 1,2
// 3
class Person {
constructor(name) {
this.name = name;
}
}
const ProxyPerson = new Proxy(Person, {
construct(target, args) {
console.log(`创建实例,参数: ${args}`);
return new target(...args);
}
});
const person = new ProxyPerson("东巴文");
// 创建实例,参数: 东巴文
const target = {};
const proxy = new Proxy(target, {
// 获取属性描述符
getOwnPropertyDescriptor(target, prop) {
console.log(`获取属性描述符: ${prop}`);
return Object.getOwnPropertyDescriptor(target, prop);
},
// 获取原型
getPrototypeOf(target) {
console.log("获取原型");
return Object.getPrototypeOf(target);
},
// 设置原型
setPrototypeOf(target, proto) {
console.log("设置原型");
return Object.setPrototypeOf(target, proto);
},
// 是否可扩展
isExtensible(target) {
console.log("检查可扩展性");
return Object.isExtensible(target);
},
// 禁止扩展
preventExtensions(target) {
console.log("禁止扩展");
return Object.preventExtensions(target);
},
// 获取自有属性键
ownKeys(target) {
console.log("获取属性列表");
return Object.keys(target);
}
});
Proxy的实际应用。
function createValidator(schema) {
return function(target) {
return new Proxy(target, {
set(target, prop, value) {
if (prop in schema) {
const validator = schema[prop];
if (!validator(value)) {
throw new Error(`无效的${prop}值: ${value}`);
}
}
target[prop] = value;
return true;
}
});
};
}
const validateUser = createValidator({
name: v => typeof v === "string" && v.length > 0,
age: v => typeof v === "number" && v >= 0 && v <= 150,
email: v => typeof v === "string" && v.includes("@")
});
const user = validateUser({});
user.name = "东巴文"; // 有效
user.age = 25; // 有效
// user.age = 200; // Error
function reactive(target) {
const callbacks = new Set();
const proxy = new Proxy(target, {
get(target, prop) {
return target[prop];
},
set(target, prop, value) {
const oldValue = target[prop];
target[prop] = value;
if (oldValue !== value) {
callbacks.forEach(cb => cb(prop, value, oldValue));
}
return true;
}
});
proxy.watch = function(callback) {
callbacks.add(callback);
return () => callbacks.delete(callback);
};
return proxy;
}
const state = reactive({ count: 0 });
state.watch((prop, newValue, oldValue) => {
console.log(`${prop}: ${oldValue} -> ${newValue}`);
});
state.count = 1; // count: 0 -> 1
state.count = 2; // count: 1 -> 2
function withPrivate(target) {
return new Proxy(target, {
get(target, prop) {
if (prop.startsWith("_")) {
throw new Error("不能访问私有属性");
}
return target[prop];
},
set(target, prop, value) {
if (prop.startsWith("_")) {
throw new Error("不能设置私有属性");
}
target[prop] = value;
return true;
},
has(target, prop) {
if (prop.startsWith("_")) {
return false;
}
return prop in target;
},
ownKeys(target) {
return Object.keys(target).filter(key => !key.startsWith("_"));
}
});
}
const obj = withPrivate({
name: "东巴文",
_password: "secret"
});
console.log(obj.name); // 东巴文
// console.log(obj._password); // Error
function createCache(fn) {
const cache = new Map();
return new Proxy(fn, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("从缓存返回");
return cache.get(key);
}
console.log("计算并缓存");
const result = target.apply(thisArg, args);
cache.set(key, result);
return result;
}
});
}
const expensiveCalculation = createCache((n) => {
console.log("执行复杂计算...");
return n * n;
});
console.log(expensiveCalculation(5)); // 计算并缓存,25
console.log(expensiveCalculation(5)); // 从缓存返回,25
Reflect是与Proxy配合使用的API。
const obj = { name: "东巴文", age: 25 };
// Reflect方法与Proxy拦截器一一对应
// 获取属性
console.log(Reflect.get(obj, "name")); // 东巴文
// 设置属性
Reflect.set(obj, "age", 26);
console.log(obj.age); // 26
// 检查属性
console.log(Reflect.has(obj, "name")); // true
// 删除属性
Reflect.deleteProperty(obj, "age");
console.log(obj.age); // undefined
// 获取属性描述符
console.log(Reflect.getOwnPropertyDescriptor(obj, "name"));
// 获取原型
console.log(Reflect.getPrototypeOf(obj));
// 设置原型
Reflect.setPrototypeOf(obj, { x: 1 });
console.log(obj.x); // 1
// 是否可扩展
console.log(Reflect.isExtensible(obj)); // true
// 禁止扩展
Reflect.preventExtensions(obj);
// 获取自有属性键
console.log(Reflect.ownKeys(obj));
// Reflect方法返回布尔值,更易处理错误
// Object.defineProperty抛出错误
try {
Object.defineProperty(Object.freeze({}), "x", { value: 1 });
} catch (e) {
console.log("定义失败");
}
// Reflect.defineProperty返回false
const success = Reflect.defineProperty(Object.freeze({}), "x", { value: 1 });
console.log(success); // false
// Object.setPrototypeOf抛出错误
try {
Object.setPrototypeOf(Object.freeze({}), {});
} catch (e) {
console.log("设置原型失败");
}
// Reflect.setPrototypeOf返回false
const result = Reflect.setPrototypeOf(Object.freeze({}), {});
console.log(result); // false
Proxy和Reflect配合使用。
const target = { name: "东巴文" };
const proxy = new Proxy(target, {
get(target, prop, receiver) {
console.log(`获取: ${prop}`);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`设置: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
},
has(target, prop) {
console.log(`检查: ${prop}`);
return Reflect.has(target, prop);
},
deleteProperty(target, prop) {
console.log(`删除: ${prop}`);
return Reflect.deleteProperty(target, prop);
}
});
proxy.name; // 获取: name
proxy.age = 25; // 设置: age = 25
"name" in proxy; // 检查: name
delete proxy.age; // 删除: age
function observe(target, callback) {
return new Proxy(target, {
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
// 如果是对象,递归代理
if (typeof value === "object" && value !== null) {
return observe(value, callback);
}
return value;
},
set(target, prop, value, receiver) {
const oldValue = Reflect.get(target, prop, receiver);
if (oldValue !== value) {
callback(prop, value, oldValue);
}
return Reflect.set(target, prop, value, receiver);
},
deleteProperty(target, prop) {
const oldValue = target[prop];
callback(prop, undefined, oldValue);
return Reflect.deleteProperty(target, prop);
}
});
}
const state = observe(
{ user: { name: "东巴文" } },
(prop, newValue, oldValue) => {
console.log(`变化: ${prop}, ${oldValue} -> ${newValue}`);
}
);
state.user.name = "db-w.cn"; // 变化: name, 东巴文 -> db-w.cn
掌握了Proxy与Reflect后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:Proxy和Reflect是ES6提供的元编程能力,可以拦截和自定义对象的基本操作,是实现响应式框架、数据验证等高级功能的基础。在 db-w.cn,我们帮你掌握元编程技术!