JavaScript中的數據劫持和數據代理使用
一、數據劫持(Data Hijacking)
1. 生活中的例子
假設你開了一家餐廳,你雇了一個 “賬房先生”(收銀員),他的工作是:
- 任何客人 查詢賬單 時,他都會記錄查詢行為,并告訴客人正確的金額。
- 任何客人 修改賬單 時,他都會檢查金額是否合理,并記錄修改情況。
現(xiàn)實中的“賬房先生”就是 Object.defineProperty()
,它攔截每個數據的讀取和修改。
2. 代碼示例
我們用 Object.defineProperty()
來模擬這個 “賬房先生”:
let bill = {}; // 創(chuàng)建賬單對象 Object.defineProperty(bill, 'amount', { get() { console.log('客人查詢了賬單金額'); return 100; // 假設賬單金額是100 }, set(value) { console.log(`客人修改了賬單金額為 ${value}`); } }); console.log(bill.amount); // 觸發(fā) get 方法,查詢金額 bill.amount = 200; // 觸發(fā) set 方法,修改金額
運行結果:
客人查詢了賬單金額
100
客人修改了賬單金額為 200
問題:
- 這個方法 只能劫持已
sh()
、pop()
這樣的操作不會觸發(fā)set
。 - 必須對每個屬性單獨定義
get/set
,如 - 有的屬性,如果
bill
里沒有amount
,defineProperty
無法監(jiān)聽到新增的amount
屬性。 - 無法監(jiān)聽數組變化,比如
pu
- 果對象有很多屬性,就要定義很多次,性能較低。
二、數據代理(Data Proxy)
1. 生活中的例子
假設你家有一個 智能管家(類似于小愛同學、Siri),你可以問他:
- "今天天氣怎么樣?" 他會去查天氣然后告訴你(相當于攔截 讀取 操作)。
- "幫我把燈光調亮一些" 他會控制家里的燈光調亮(相當于攔截 修改 操作)。
智能管家就是 Proxy
,它可以代理整個房子里所有的設備,而不需要逐個綁定(不像 Object.defineProperty()
必須單獨劫持每個設備的控制)。
2. 代碼示例
用 Proxy
實現(xiàn)智能管家的功能:
let house = { temperature: 22, light: 'off' }; // 房子里的設備 let smartHouse = new Proxy(house, { get(target, key) { console.log(`查詢 ${key} 的狀態(tài):${target[key]}`); return target[key]; }, set(target, key, value) { console.log(`修改 ${key} 的狀態(tài)為 ${value}`); target[key] = value; return true; } }); console.log(smartHouse.temperature); // 觸發(fā) get,查詢溫度 smartHouse.light = 'on'; // 觸發(fā) set,修改燈光狀態(tài)
運行結果:
查詢 temperature 的狀態(tài):22
修改 light 的狀態(tài)為 on
Proxy 的優(yōu)勢:
- 可以代理整個對象,不用為每個屬性單獨定義
get/set
。 - 支持監(jiān)聽新增屬性,比如
house.newDevice = 'TV'
也能被攔截! - 支持數組,例如監(jiān)聽
push()
、pop()
這些操作。
三、對比總結
對比項 | 數據劫持(Object.defineProperty) | 數據代理(Proxy) |
---|---|---|
Vue 版本 | Vue 2.x | Vue 3.x |
監(jiān)聽對象 | 只能監(jiān)聽已有的屬性 | 可以監(jiān)聽整個對象 |
監(jiān)聽新增/刪除屬性 | 不能監(jiān)聽(需 Vue.set) | 可直接監(jiān)聽 |
監(jiān)聽數組 | 需要重寫數組方法 | 原生支持 |
深層嵌套 | 需要遞歸遍歷 | 訪問時自動代理 |
兼容性 | ES5 及以上 | 僅支持 ES6 及以上 |
四、再舉一個更貼近開發(fā)的例子
使用 Object.defineProperty() 監(jiān)聽對象
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
問題:
- 不能監(jiān)聽
person.newProp = 'test'
這樣的新增屬性。 - 不能監(jiān)聽
delete person.age
這樣的刪除操作。
使用 Proxy 監(jiān)聽整個對象
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'; // 監(jiān)聽新增屬性 delete reactivePerson.age; // 監(jiān)聽刪除屬性
優(yōu)勢:
Proxy
可以監(jiān)聽對象的新增/刪除屬性,而Object.defineProperty()
不行!Proxy
可以監(jiān)聽數組的變化,而Object.defineProperty()
不能監(jiān)聽push()
、splice()
。Proxy
可以代理整個對象,而Object.defineProperty()
需要遍歷所有屬性,性能較差。
五、總結
概念 | 方式 | 適用場景 | 優(yōu)勢 |
---|---|---|---|
數據劫持 | Object.defineProperty() | Vue 2.x | 兼容性好,支持老瀏覽器,但不能監(jiān)聽新增/刪除屬性 |
數據代理 | Proxy | Vue 3.x | 功能更強,可監(jiān)聽新增/刪除,支持數組操作 |
結論:
- 如果是 Vue 2,只能用
Object.defineProperty()
來實現(xiàn)數據劫持。 - 如果是 Vue 3,推薦用
Proxy
,因為它更強大,能處理所有數據變化。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
解決 viewer.js 動態(tài)更新圖片導致無法預覽的問題
Viewer.js 是一款強大的圖片查看器,這篇文章主要介紹了解決 viewer.js 動態(tài)更新圖片導致無法預覽的問題 ,需要的朋友可以參考下2019-05-05