JavaScript中的数据劫持和数据代理使用
更新时间:2025年02月21日 17:13:09 作者:遇见很ok
文章主要介绍了数据劫持(Object.defineProperty)和数据代理(Proxy)两种方式在JavaScript中的应用,并通过生活中的例子来详细解释它们的工作原理和使用场景,文章还对比了两种方式的优缺点,并指出了它们在Vue.js中的应用
一、数据劫持(Data Hijacking)
1. 生活中的例子
假设你开了一家餐厅,你雇了一个 “账房先生”(收银员),他的工作是:
- 任何客人 查询账单 时,他都会记录查询行为,并告诉客人正确的金额。
- 任何客人 修改账单 时,他都会检查金额是否合理,并记录修改情况。
现实中的“账房先生”就是 Object.defineProperty()
,它拦截每个数据的读取和修改。
2. 代码示例
我们用 Object.defineProperty()
来模拟这个 “账房先生”:
let bill = {}; // 创建账单对象 Object.defineProperty(bill, 'amount', { get() { console.log('客人查询了账单金额'); return 100; // 假设账单金额是100 }, set(value) { console.log(`客人修改了账单金额为 ${value}`); } }); console.log(bill.amount); // 触发 get 方法,查询金额 bill.amount = 200; // 触发 set 方法,修改金额
运行结果:
客人查询了账单金额
100
客人修改了账单金额为 200
问题:
- 这个方法 只能劫持已
sh()
、pop()
这样的操作不会触发set
。 - 必须对每个属性单独定义
get/set
,如 - 有的属性,如果
bill
里没有amount
,defineProperty
无法监听到新增的amount
属性。 - 无法监听数组变化,比如
pu
- 果对象有很多属性,就要定义很多次,性能较低。
二、数据代理(Data Proxy)
1. 生活中的例子
假设你家有一个 智能管家(类似于小爱同学、Siri),你可以问他:
- "今天天气怎么样?" 他会去查天气然后告诉你(相当于拦截 读取 操作)。
- "帮我把灯光调亮一些" 他会控制家里的灯光调亮(相当于拦截 修改 操作)。
智能管家就是 Proxy
,它可以代理整个房子里所有的设备,而不需要逐个绑定(不像 Object.defineProperty()
必须单独劫持每个设备的控制)。
2. 代码示例
用 Proxy
实现智能管家的功能:
let house = { temperature: 22, light: 'off' }; // 房子里的设备 let smartHouse = new Proxy(house, { get(target, key) { console.log(`查询 ${key} 的状态:${target[key]}`); return target[key]; }, set(target, key, value) { console.log(`修改 ${key} 的状态为 ${value}`); target[key] = value; return true; } }); console.log(smartHouse.temperature); // 触发 get,查询温度 smartHouse.light = 'on'; // 触发 set,修改灯光状态
运行结果:
查询 temperature 的状态:22
修改 light 的状态为 on
Proxy 的优势:
- 可以代理整个对象,不用为每个属性单独定义
get/set
。 - 支持监听新增属性,比如
house.newDevice = 'TV'
也能被拦截! - 支持数组,例如监听
push()
、pop()
这些操作。
三、对比总结
对比项 | 数据劫持(Object.defineProperty) | 数据代理(Proxy) |
---|---|---|
Vue 版本 | Vue 2.x | Vue 3.x |
监听对象 | 只能监听已有的属性 | 可以监听整个对象 |
监听新增/删除属性 | 不能监听(需 Vue.set) | 可直接监听 |
监听数组 | 需要重写数组方法 | 原生支持 |
深层嵌套 | 需要递归遍历 | 访问时自动代理 |
兼容性 | ES5 及以上 | 仅支持 ES6 及以上 |
四、再举一个更贴近开发的例子
使用 Object.defineProperty() 监听对象
let person = { name: 'Alice', age: 25 }; function makeReactive(obj) { for (let key in obj) { let value = obj[key]; Object.defineProperty(obj, key, { get() { console.log(`读取 ${key}: ${value}`); return value; }, set(newVal) { console.log(`修改 ${key} 为 ${newVal}`); value = newVal; } }); } } makeReactive(person); console.log(person.name); // 读取 name person.age = 30; // 修改 age
问题:
- 不能监听
person.newProp = 'test'
这样的新增属性。 - 不能监听
delete person.age
这样的删除操作。
使用 Proxy 监听整个对象
let person = { name: 'Alice', age: 25 }; let reactivePerson = new Proxy(person, { get(target, key) { console.log(`读取 ${key}: ${target[key]}`); return target[key]; }, set(target, key, value) { console.log(`修改 ${key} 为 ${value}`); target[key] = value; return true; }, deleteProperty(target, key) { console.log(`删除属性 ${key}`); return delete target[key]; } }); console.log(reactivePerson.name); // 读取 name reactivePerson.age = 30; // 修改 age reactivePerson.newProp = 'test'; // 监听新增属性 delete reactivePerson.age; // 监听删除属性
优势:
Proxy
可以监听对象的新增/删除属性,而Object.defineProperty()
不行!Proxy
可以监听数组的变化,而Object.defineProperty()
不能监听push()
、splice()
。Proxy
可以代理整个对象,而Object.defineProperty()
需要遍历所有属性,性能较差。
五、总结
概念 | 方式 | 适用场景 | 优势 |
---|---|---|---|
数据劫持 | Object.defineProperty() | Vue 2.x | 兼容性好,支持老浏览器,但不能监听新增/删除属性 |
数据代理 | Proxy | Vue 3.x | 功能更强,可监听新增/删除,支持数组操作 |
结论:
- 如果是 Vue 2,只能用
Object.defineProperty()
来实现数据劫持。 - 如果是 Vue 3,推荐用
Proxy
,因为它更强大,能处理所有数据变化。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
最新评论