一、Proxy 是什么?
Proxy(代理)是ES6引入的一个强大的特性,它允许我们拦截并自定义对象的基本操作。简单来说,Proxy就像一个"中间人",控制对目标对象的访问。你可以通过它监控对象的属性读取、赋值、删除等操作,并在这些操作发生时插入自定义逻辑。
通俗的来讲就是想象一下你有一个朋友叫"小代",你想找某个人办事,但这个人不直接见你。你只能通过"小代"去传达你的请求,小代会帮你和那个人打交道。
在这个场景中:
- 那个"被你找的人"就是目标对象(target)
- "小代"就是 Proxy(代理)
- 你要做的事情(比如:问问题、改东西、删东西),都要经过"小代"
所以,Proxy 就是一个中间人,你可以通过它来控制对某个对象的操作。
二、Proxy 的基本结构
javascript
const proxy = new Proxy(target, handler);
target
:你要代理的对象。handler
:一个配置对象,里面定义了"你想在什么时候做点什么事情"。
三、举个最简单的例子:监控属性访问
我们想监控谁在读取或修改对象的属性。
javascript
const obj = {
name: "张三",
age: 20
};
const handler = {
get(target, key) {
console.log("有人要读取属性:" + key);
return target[key];
},
set(target, key, value) {
console.log("有人要设置属性:" + key + " 为 " + value);
target[key] = value;
return true; // 必须返回 true 表示设置成功
}
};
const p = new Proxy(obj, handler);
console.log(p.name); // 输出:有人要读取属性:name
p.age = 30; // 输出:有人要设置属性:age 为 30
解释一下这段代码:
- 我们创建了一个 Proxy 对象
p
- 每次读取属性(如
p.name
)都会触发get
方法 - 每次设置属性(如
p.age = 30
)都会触发set
方法 - 这样我们就知道了谁在操作对象的哪些属性!
拦截器(handler)常用方法一览
get(target, key) |
读取属性 | obj.name |
---|---|---|
set(target, key, value) |
设置属性 | obj.name = 'Tom' |
has(target, key) |
in 运算符 | 'name' in obj |
deleteProperty(target, key) |
delete 操作 | delete obj.name |
apply(target, thisArg, args) |
函数调用 | fn() 、fn.apply() |
construct(target, args) |
new 调用 | new MyClass() |
四、实际应用场景(通俗举例)
场景1:数据验证(防止乱改)
比如你希望别人不能随便把年龄改成字符串:
javascript
const user = {
age: 18
};
const handler = {
set(target, key, value) {
if (key === 'age' && typeof value !== 'number') {
console.error('年龄必须是数字!');
return true; // 不抛错,只是阻止赋值
}
target[key] = value;
return true;
}
};
const proxy = new Proxy(user, handler);
proxy.age = "abc"; // 输出:年龄必须是数字!
proxy.age = 25; // 正常执行
场景2:权限控制(有些属性不能看)
比如某些私有属性不能让别人看到:
javascript
const data = {
username: "admin",
_password: "123456"
};
const handler = {
get(target, key) {
if (key.startsWith('_')) {
throw new Error("不能访问私有属性");
}
return target[key];
}
};
const proxy = new Proxy(data, handler);
console.log(proxy.username); // 正常输出 admin
console.log(proxy._password); // 报错:不能访问私有属性
场景3:响应式系统(Vue3的核心原理)
Vue3用Proxy来监听数据变化,自动更新页面。
比如你改了数据,页面就自动刷新了:
javascript
let data = {
count: 0
};
const handler = {
set(target, key, value) {
console.log("数据变了,准备更新视图!");
target[key] = value;
return true;
}
};
const proxy = new Proxy(data, handler);
proxy.count++; // 输出:数据变了,准备更新视图!
五、常见面试题 & 答案
Q1:Proxy 是干什么的?
答:Proxy 是用来拦截并自定义对象行为的一种机制。就像一个"中间人",我们可以在这个中间人里加一些逻辑,比如打印日志、校验数据、权限控制等。
Q2:Proxy 和 Object.defineProperty 有什么区别?
区别 | Proxy | Object.defineProperty |
---|---|---|
支持数组 | √ | × |
新增属性是否能监听 | √ | × |
拦截操作种类 | 多(13种) | 少(只有get/set) |
兼容性 | ES6+ | ES5+ |
Q3:Reflect 是干嘛的?为什么要和 Proxy 一起用?
答:Reflect 提供了一些默认操作的方法,比如 Reflect.get()、Reflect.set(),这样我们在 Proxy 中调用它们时可以更安全、统一,并且兼容性更好。
六、总结一句话
Proxy 就像一个"门卫",你想访问一个对象的属性或者修改它,都得先过它这一关。
你可以利用这个"门卫"来做很多事,比如:
- 监控谁动了对象
- 控制谁能看到什么
- 防止错误的数据进来
- 实现 Vue 那样的响应式系统
七、练习建议
你可以试着写几个简单的 Proxy:
- 拦截删除操作,让用户确认后再删除。
- 创建一个只读对象,不让别人修改。
- 统计某个对象被访问了多少次。