window.Proxy 是 JavaScript 的元编程特性,用于创建对象的代理(拦截器)! 🎯
🎯 基本概念
// Proxy 允许你创建一个对象的代理
// 可以拦截和自定义对象的基本操作
const target = {}; // 目标对象
const handler = {}; // 拦截器对象
const proxy = new Proxy(target, handler);
🚀 基本语法
// 创建 Proxy
const proxy = new Proxy(target, handler);
// target: 要代理的目标对象
// handler: 包含拦截方法的对象(陷阱)
🔧 核心拦截器(陷阱)
1. 基本操作拦截
const handler = {
// 拦截属性读取
get(target, property, receiver) {
console.log(`读取属性: ${property}`);
return target[property] || '默认值';
},
// 拦截属性设置
set(target, property, value, receiver) {
console.log(`设置属性: ${property} = ${value}`);
target[property] = value;
return true; // 表示成功
},
// 拦截属性删除
deleteProperty(target, property) {
console.log(`删除属性: ${property}`);
delete target[property];
return true;
}
};
const obj = { name: '张三' };
const proxy = new Proxy(obj, handler);
proxy.name; // 输出: 读取属性: name
proxy.age = 25; // 输出: 设置属性: age = 25
delete proxy.name; // 输出: 删除属性: name
2. 存在性检查拦截
const handler = {
// 拦截 in 操作符
has(target, property) {
console.log(`检查属性是否存在: ${property}`);
return property in target;
}
};
const obj = { name: '李四' };
const proxy = new Proxy(obj, handler);
console.log('name' in proxy); // 输出: 检查属性是否存在: name
🎯 实际应用场景
1. 数据验证
// 创建带验证的 Proxy
const validator = {
set(target, property, value) {
// 年龄验证
if (property === 'age') {
if (typeof value !== 'number' || value < 0 || value > 150) {
throw new Error('年龄必须是 0-150 的数字');
}
}
// 邮箱验证
if (property === 'email') {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
throw new Error('邮箱格式不正确');
}
}
target[property] = value;
return true;
}
};
const user = new Proxy({}, validator);
user.age = 25; // ✅ 成功
user.age = 200; // ❌ 抛出错误
user.email = 'test@example.com'; // ✅ 成功
2. 默认值和计算属性
const withDefaults = {
get(target, property) {
if (property in target) {
return target[property];
}
// 提供默认值
const defaults = {
theme: 'light',
language: 'zh-CN',
pageSize: 10
};
return defaults[property] || `属性 ${property} 不存在`;
}
};
const config = new Proxy({}, withDefaults);
console.log(config.theme); // "light"(默认值)
console.log(config.language); // "zh-CN"(默认值)
config.theme = 'dark'; // 设置自定义值
console.log(config.theme); // "dark"(自定义值)
3. 观察者模式(数据绑定)
function createObservable(obj, onChange) {
return new Proxy(obj, {
set(target, property, value) {
const oldValue = target[property];
target[property] = value;
// 触发变更回调
if (oldValue !== value) {
onChange(property, value, oldValue);
}
return true;
}
});
}
// 使用示例
const state = createObservable(
{ count: 0, name: '' },
(key, newValue, oldValue) => {
console.log(`状态变更: ${key} 从 ${oldValue} 变为 ${newValue}`);
}
);
state.count = 1; // 输出: 状态变更: count 从 0 变为 1
state.name = '张三'; // 输出: 状态变更: name 从 变为 张三
4. 数组操作拦截
// 监听数组变化
const observableArray = (array, onChange) => {
return new Proxy(array, {
get(target, property) {
const value = target[property];
// 拦截数组方法
if (typeof value === 'function') {
return function(...args) {
const result = value.apply(target, args);
onChange(property, args, target);
return result;
};
}
return value;
}
});
};
const arr = observableArray([1, 2, 3], (method, args, array) => {
console.log(`数组操作: ${method}`, args, '=>', array);
});
arr.push(4); // 输出: 数组操作: push [4] => [1,2,3,4]
arr.pop(); // 输出: 数组操作: pop [] => [1,2,3]
🔧 高级用法
1. 函数代理
// 代理函数调用
function logger(target) {
return new Proxy(target, {
apply(fn, thisArg, argumentsList) {
console.log(`调用函数: ${fn.name}`, argumentsList);
const result = fn.apply(thisArg, argumentsList);
console.log(`返回结果:`, result);
return result;
}
});
}
function add(a, b) {
return a + b;
}
const loggedAdd = logger(add);
loggedAdd(2, 3);
// 输出: 调用函数: add [2, 3]
// 输出: 返回结果: 5
2. 链式操作代理
function createChainable() {
const methods = {
select() { /* ... */ },
where() { /* ... */ },
orderBy() { /* ... */ }
};
return new Proxy({}, {
get(target, property) {
if (property in methods) {
return function(...args) {
console.log(`调用方法: ${property}`, args);
// 返回代理自身,支持链式调用
return this;
}.bind(proxy);
}
return target[property];
}
});
}
const query = createChainable();
query.select('name').where('age > 18').orderBy('name');
🎯 Vue 3 的响应式系统
Vue 3 使用 Proxy 实现响应式
// Vue 3 响应式原理(简化版)
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key); // 依赖收集
return target[key];
},
set(target, key, value) {
target[key] = value;
trigger(target, key); // 触发更新
return true;
}
});
}
// 使用示例
const state = reactive({ count: 0 });
// 自动响应式
console.log(state.count); // 触发 get
state.count = 1; // 触发 set
⚠️ 注意事项
1. 目标对象保护
const obj = { data: '敏感信息' };
const proxy = new Proxy(obj, {
get(target, property) {
if (property === 'data') {
return '访问被拒绝';
}
return target[property];
}
});
console.log(proxy.data); // "访问被拒绝"
// ❌ 但仍然可以直接访问原对象
console.log(obj.data); // "敏感信息"
2. this 绑定问题
const obj = {
name: 'obj',
getName() {
return this.name;
}
};
const proxy = new Proxy(obj, {
get(target, property, receiver) {
// receiver 确保正确的 this 绑定
const value = target[property];
if (typeof value === 'function') {
return value.bind(receiver);
}
return value;
}
});
console.log(proxy.getName()); // "obj"(正确绑定)
🔧 浏览器兼容性
// 检查浏览器支持
if (typeof Proxy !== 'undefined') {
console.log('✅ 浏览器支持 Proxy');
} else {
console.log('❌ 浏览器不支持 Proxy');
}
// 支持情况:
// Chrome 49+, Firefox 18+, Safari 10+, Edge 12+, Node.js 6+
🚀 与 Object.defineProperty 对比
Proxy 的优势
// 1. 拦截更多操作
const proxy = new Proxy(obj, {
// 可以拦截这些操作:
get, set, deleteProperty, has, apply, construct,
getPrototypeOf, setPrototypeOf, isExtensible,
preventExtensions, getOwnPropertyDescriptor,
defineProperty, ownKeys
});
// 2. 数组响应式(Vue 2 的痛点)
const arr = new Proxy([1, 2, 3], {
set(target, index, value) {
console.log('数组变化:', index, value);
target[index] = value;
return true;
}
});
arr.push(4); // 可以被拦截
arr[0] = 10; // 可以被拦截
✅ 总结
Proxy 的核心特性:
| 特性 | 说明 |
|---|---|
| 🎯 元编程 | 拦截和自定义对象基本操作 |
| 🚀 强大拦截 | 支持 13 种陷阱方法 |
| 📱 数组友好 | 完美处理数组变化检测 |
| ⚡ 性能优秀 | 比 defineProperty 更高效 |
| 🌍 现代标准 | ES6 标准,主流浏览器支持 |
适用场景:
- ✅ 响应式框架(Vue 3、MobX)
- ✅ 数据验证
- ✅ 观察者模式
- ✅ API 封装
- ✅ 权限控制
Proxy 是现代 JavaScript 元编程的强大工具! 💪
