JavaScript中的數(shù)據(jù)劫持和數(shù)據(jù)代理使用
一、數(shù)據(jù)劫持(Data Hijacking)
1. 生活中的例子
假設(shè)你開了一家餐廳,你雇了一個(gè) “賬房先生”(收銀員),他的工作是:
- 任何客人 查詢賬單 時(shí),他都會(huì)記錄查詢行為,并告訴客人正確的金額。
- 任何客人 修改賬單 時(shí),他都會(huì)檢查金額是否合理,并記錄修改情況。
現(xiàn)實(shí)中的“賬房先生”就是 Object.defineProperty(),它攔截每個(gè)數(shù)據(jù)的讀取和修改。
2. 代碼示例
我們用 Object.defineProperty() 來模擬這個(gè) “賬房先生”:
let bill = {}; // 創(chuàng)建賬單對(duì)象
Object.defineProperty(bill, 'amount', {
get() {
console.log('客人查詢了賬單金額');
return 100; // 假設(shè)賬單金額是100
},
set(value) {
console.log(`客人修改了賬單金額為 ${value}`);
}
});
console.log(bill.amount); // 觸發(fā) get 方法,查詢金額
bill.amount = 200; // 觸發(fā) set 方法,修改金額運(yùn)行結(jié)果:
客人查詢了賬單金額
100
客人修改了賬單金額為 200
問題:
- 這個(gè)方法 只能劫持已
sh()、pop()這樣的操作不會(huì)觸發(fā)set。 - 必須對(duì)每個(gè)屬性單獨(dú)定義
get/set,如 - 有的屬性,如果
bill里沒有amount,defineProperty無法監(jiān)聽到新增的amount屬性。 - 無法監(jiān)聽數(shù)組變化,比如
pu - 果對(duì)象有很多屬性,就要定義很多次,性能較低。
二、數(shù)據(jù)代理(Data Proxy)
1. 生活中的例子
假設(shè)你家有一個(gè) 智能管家(類似于小愛同學(xué)、Siri),你可以問他:
- "今天天氣怎么樣?" 他會(huì)去查天氣然后告訴你(相當(dāng)于攔截 讀取 操作)。
- "幫我把燈光調(diào)亮一些" 他會(huì)控制家里的燈光調(diào)亮(相當(dāng)于攔截 修改 操作)。
智能管家就是 Proxy,它可以代理整個(gè)房子里所有的設(shè)備,而不需要逐個(gè)綁定(不像 Object.defineProperty() 必須單獨(dú)劫持每個(gè)設(shè)備的控制)。
2. 代碼示例
用 Proxy 實(shí)現(xiàn)智能管家的功能:
let house = { temperature: 22, light: 'off' }; // 房子里的設(shè)備
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)運(yùn)行結(jié)果:
查詢 temperature 的狀態(tài):22
修改 light 的狀態(tài)為 on
Proxy 的優(yōu)勢(shì):
- 可以代理整個(gè)對(duì)象,不用為每個(gè)屬性單獨(dú)定義
get/set。 - 支持監(jiān)聽新增屬性,比如
house.newDevice = 'TV'也能被攔截! - 支持?jǐn)?shù)組,例如監(jiān)聽
push()、pop()這些操作。
三、對(duì)比總結(jié)
| 對(duì)比項(xiàng) | 數(shù)據(jù)劫持(Object.defineProperty) | 數(shù)據(jù)代理(Proxy) |
|---|---|---|
| Vue 版本 | Vue 2.x | Vue 3.x |
| 監(jiān)聽對(duì)象 | 只能監(jiān)聽已有的屬性 | 可以監(jiān)聽整個(gè)對(duì)象 |
| 監(jiān)聽新增/刪除屬性 | 不能監(jiān)聽(需 Vue.set) | 可直接監(jiān)聽 |
| 監(jiān)聽數(shù)組 | 需要重寫數(shù)組方法 | 原生支持 |
| 深層嵌套 | 需要遞歸遍歷 | 訪問時(shí)自動(dòng)代理 |
| 兼容性 | ES5 及以上 | 僅支持 ES6 及以上 |
四、再舉一個(gè)更貼近開發(fā)的例子
使用 Object.defineProperty() 監(jiān)聽對(duì)象
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)聽整個(gè)對(duì)象
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)勢(shì):
Proxy可以監(jiān)聽對(duì)象的新增/刪除屬性,而Object.defineProperty()不行!Proxy可以監(jiān)聽數(shù)組的變化,而Object.defineProperty()不能監(jiān)聽push()、splice()。Proxy可以代理整個(gè)對(duì)象,而Object.defineProperty()需要遍歷所有屬性,性能較差。
五、總結(jié)
| 概念 | 方式 | 適用場(chǎng)景 | 優(yōu)勢(shì) |
|---|---|---|---|
| 數(shù)據(jù)劫持 | Object.defineProperty() | Vue 2.x | 兼容性好,支持老瀏覽器,但不能監(jiān)聽新增/刪除屬性 |
| 數(shù)據(jù)代理 | Proxy | Vue 3.x | 功能更強(qiáng),可監(jiān)聽新增/刪除,支持?jǐn)?shù)組操作 |
結(jié)論:
- 如果是 Vue 2,只能用
Object.defineProperty()來實(shí)現(xiàn)數(shù)據(jù)劫持。 - 如果是 Vue 3,推薦用
Proxy,因?yàn)樗鼜?qiáng)大,能處理所有數(shù)據(jù)變化。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
5分鐘快速搭建vue3+ts+vite+pinia項(xiàng)目
本文主要介紹了5分鐘快速搭建vue3+ts+vite+pinia項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
JavaScript?中require?和?import區(qū)別及用法示例對(duì)比
在JavaScript中,require和import都是用來導(dǎo)入模塊的語法,但它們屬于不同的模塊系統(tǒng):CommonJS和ECMAScript Modules(ESM),這篇文章主要介紹了JavaScript中require和import區(qū)別及用法示例對(duì)比,需要的朋友可以參考下2025-09-09
前端不使用i18n該如何優(yōu)雅的實(shí)現(xiàn)多語言
多語言的重要性相信不需要多言,下面這篇文章主要給大家介紹了關(guān)于前端不使用i18n該如何優(yōu)雅的實(shí)現(xiàn)多語言,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
JS如何讓你的移動(dòng)端交互體驗(yàn)更加優(yōu)秀
現(xiàn)在在手機(jī)等移動(dòng)端設(shè)備訪問的人越來越多,我們前端開發(fā)者一直致力于將設(shè)計(jì)稿還原成頁面,供用戶訪問。但除高度還原設(shè)計(jì)稿外,交互上的良好體驗(yàn)也是我們應(yīng)該做到的。2021-05-05
javascript:void(0)的真正含義實(shí)例分析
void操作符解釋2008-08-08

