Object.defineProperty()Proxy詳解(Vue23數據劫持實現)

底層原理👇🏿
總結一下,結構應該包括:

1. 方法的基本作用和參數。

2. 數據描述符和存取描述符的區別。

3. 屬性定義的內部處理流程。

4. 在Vue中的應用實例。

5. 常見錯誤和正確實踐。

每個部分都要結合搜索結果的信息,并正確引用來源。

Object.defineProperty 底層原理詳解

1. 核心機制與參數作用

Object.defineProperty() 是 JavaScript 中用于精確控制對象屬性的方法,其底層實現可分為以下步驟:

? 參數校驗:首先檢查傳入的 obj 是否為對象,否則拋出 TypeError。

? 屬性定義/修改:若屬性存在則更新其特性;若不存在則創建新屬性。

? 描述符處理:根據 descriptor 中的配置(如 value、writable 等)設置屬性的特性。

2. 數據描述符與存取描述符的區分

? 數據描述符:通過 value 和 writable 直接控制屬性值和可寫性,例如:

Object.defineProperty(obj, 'age', { value: 20, writable: false });


此時直接修改 obj.age 會因 writable: false 而失敗。

? 存取描述符:通過 get 和 set 函數攔截屬性的讀取和賦值操作,例如:

let _value;
Object.defineProperty(obj, 'name', {
? get() { return _value; },
? set(newVal) { _value = newVal.toUpperCase(); }
});


此時 obj.name = 'alice' 會觸發 set 并存儲為 'ALICE'。
注意:get/set 與 value/writable 互斥,若混合使用會報錯。

3. 屬性特性的內部處理流程

? 默認特性:未顯式配置時,configurable、enumerable、writable 默認為 false。

? 動態攔截:通過 set 方法可實現數據劫持。例如 Vue 的響應式系統中,set 內觸發視圖更新。

? 遞歸監聽:若屬性值是對象,需遞歸調用 Object.defineProperty 實現深層監聽。

4. 常見問題與解決方案

? 循環遞歸陷阱:在 set 中直接操作當前屬性會導致無限遞歸。例如:

// 錯誤示例
set(val) { this.name = val; } ?// 觸發 set 后再次調用 set,導致棧溢出


正確方案:通過中間變量(如 _name)存儲實際值,避免直接操作屬性。

? 性能優化:高頻修改屬性時,需結合防抖/節流減少觸發次數。

5. 應用場景與框架實踐

? 數據綁定:Vue 2.x 使用 Object.defineProperty 攔截數據變化,實現響應式更新。

? 屬性封裝:限制某些屬性的可寫性(如 API 密鑰)或隱藏敏感數據(如 enumerable: false)。

? 動態擴展對象:為已有對象添加計算屬性或校驗邏輯。

總結

Object.defineProperty 的底層原理圍繞屬性描述符的精細化控制展開,通過 get/set 實現數據劫持,結合 configurable 等特性確保屬性安全性。其局限性(如無法監聽數組索引變化)促使了 Vue 3 轉向 Proxy 方案,但在舊版本框架和特定場景中仍為核心技術。

為什么有缺陷??

?

首先,Object.defineProperty的工作原理是針對對象的屬性進行劫持,但數組的索引操作如push、pop等并不會觸發屬性的getter和setter。這是因為數組的方法修改的是數組本身的結構,而Object.defineProperty無法捕獲這些操作。例如,網頁1提到,Vue2通過重寫數組的原型方法(如push、pop)來實現監聽,而網頁7和8也解釋了數組的length屬性和索引變化的問題。

?

接下來,需要詳細說明Vue2的具體實現方法。例如,網頁2和網頁7提到,Vue2創建了一個新的數組原型對象,覆蓋了原生的方法,并在調用這些方法時手動觸發更新。這樣,雖然Object.defineProperty本身不能監聽數組的變化,但通過重寫方法,Vue2能夠間接地捕獲這些變化。

?

然后,分析為什么Object.defineProperty無法直接監聽數組變化。網頁3、5、6和8指出,Object.defineProperty無法處理動態新增的索引,數組的length屬性修改,以及像直接通過索引賦值(如arr[0] = 1)的情況。例如,網頁6中的示例顯示,即使對數組索引使用Object.defineProperty,像pop和push這樣的方法也不會觸發setter。

?

最后,總結Proxy在Vue3中的優勢,如網頁3和5提到的,Proxy可以監聽整個對象和數組的變化,無需重寫方法,從而更高效和全面。這部分可以作為對比,幫助用戶理解為什么Vue3選擇Proxy替代Object.defineProperty。

?

👉👉綜合回答:Object.defineProperty 監聽數組的原理與局限性

?

一、如何監聽數組變化?

?

核心方法:通過重寫數組原型方法并劫持操作,間接實現監聽。以下是具體實現邏輯(結合 Vue2 的實踐):

?

1. 重寫數組原型方法

創建新的數組原型對象,覆蓋原生方法(如 push、pop、splice 等),在調用這些方法時手動觸發更新。

?

const oldArrayProto = Array.prototype;

const newArrayProto = Object.create(oldArrayProto);

['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {

? newArrayProto[method] = function(...args) {

? ? const result = oldArrayProto[method].apply(this, args);

? ? triggerUpdate(); // 手動觸發視圖更新

? ? return result;

? };

});

?

?

2. 將數組的原型指向新對象

遍歷數組,將其原型指向自定義的原型對象,從而攔截方法調用:

?

function observeArray(arr) {

? arr.__proto__ = newArrayProto;

}

?

?

3. 遞歸監聽數組元素

若數組元素為對象,仍需對每個元素遞歸應用 Object.defineProperty 監聽:

?

arr.forEach(item => observe(item)); // observe 為遞歸監聽函數

?

?

二、為什么無法直接獲取數組的變化?

?

根本原因:Object.defineProperty 的設計機制與數組特性存在沖突,具體表現為以下限制:

?

1. 無法監聽索引動態變化

?

? ? 直接賦值索引(如 arr[0] = 1)時,若索引已被 Object.defineProperty 劫持,可以觸發 setter;

?

? ? 新增索引(如 arr[5] = 10)時,由于屬性未預先定義,無法觸發監聽。

?

2. 無法攔截數組方法調用

原生方法(如 push、pop)直接操作數組底層存儲結構,繞過屬性訪問器,不會觸發 setter。例如:

?

const arr = [1];

Object.defineProperty(arr, '0', { set() { console.log('觸發') } });

arr.push(2); // 不會輸出“觸發”

?

?

3. 無法監聽 length 屬性變化

修改數組長度(如 arr.length = 0)時,Object.defineProperty 無法檢測到 length 的變化(其 writable 特性為 true,但未定義 setter)。

?

4. 性能與實現復雜度問題

?

? ? 需遞歸遍歷數組元素,對大型數組性能較差;

?

? ? 需手動維護重寫方法,代碼復雜度高。

?

三、對比 Proxy 的解決方案(Vue3 改進)

?

Vue3 改用 Proxy 解決上述問題,優勢包括:

?

1. 直接代理整個數組,無需重寫方法;

?

2. 支持動態索引和 length 屬性,通過 set 陷阱捕獲所有變化;

?

3. 性能優化:惰性監聽,僅在訪問時觸發劫持邏輯。

?

總結

?

? 監聽原理:通過重寫數組方法間接實現,但需手動維護原型鏈和遞歸監聽;

?

? 局限性根源:Object.defineProperty 的機制無法覆蓋數組的動態特性(如索引新增、方法調用、length 修改);

?

? 替代方案:ES6 Proxy 提供更全面的監聽能力,成為現代框架(如 Vue3)的首選。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74653.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74653.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74653.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL 進階語法:函數、約束、多表查詢、事務

目錄 一、MySQL 常用函數 1. 字符串函數 1.1 基本字符串操作 1.2 字符串截取與處理 1.3 字符串搜索與替換 2. 數值函數 2.1 基本數學運算 2.2 數學計算 2.3 隨機數與符號 3. 日期時間函數 3.1 獲取當前時間 3.2 日期時間計算 3.3 日期時間提取 3.4 日期時間格式化…

第 12 章(番外)| Solidity 安全前沿趨勢 × 審計生態 × 職業路徑規劃

🌐 第 12 章(番外)| Solidity 安全前沿趨勢 審計生態 職業路徑規劃 ——做得了審計,也接得了項目,走進 Web3 安全工程師的職業實戰地圖 ? 本章導讀 Solidity 安全,不只是代碼安全、業務安全、審計安全…

1、pytest基本用法

目錄 先給大家分享下學習資源 1. 安裝pytest 2. 編寫用例規則 3. 執行用例 最近在學習pytest的用法 并且用這套框架替換了原來的unittest, 同是測試框架 確實感覺到pytest更加便捷 這邊分享給大家我得學習心得 先給大家分享下學習資源 1 官方文檔 pytest 官方…

【sylar-webserver】5 協程調度模塊

文章目錄 設計思路三種協程的切換 協程調度模塊,需要把前面的線程模塊和協程模塊結合使用 ~ 設計思路 構造函數定義 線程池 基本信息。start(),創建線程池,每個線程創建都執行 run()。每個線程在 run() 里,查找任務隊列 m_tasks…

Go 語言規范學習(1)

文章目錄 IntroductionNotation示例(Go 語言的 if 語句): Source code representationCharacters例子:變量名可以是中文 Letters and digits Lexical elementsCommentsTokensSemicolons例子:查看程序所有的token Ident…

探索抓包利器ProxyPin,實現手機APP請求抓包,支持https請求

以下是ProxyPin的簡單介紹: - ProxyPin是一個開源免費HTTP(S)流量捕獲神器,支持 Windows、Mac、Android、IOS、Linux 全平臺系統- 可以使用它來攔截、檢查并重寫HTTP(S)流量,支持捕獲各種應用的網絡請求。ProxyPin基于Flutter開發&#xff0…

深度學習3-pytorch學習

深度學習3-pytorch學習 Tensor 定義與 PyTorch 操作 1. Tensor 定義: Tensor 是 PyTorch 中的數據結構,類似于 NumPy 數組。可以通過不同方式創建 tensor 對象: import torch# 定義一個 1D Tensor x1 torch.Tensor([3, 4])# 定義一個 Fl…

深入淺出Spring-Boot-3.x.pdf

通過網盤分享的文件:深入淺出Spring-Boot-3.x.pdf 鏈接: https://pan.baidu.com/s/10ZkhmeIXphEwND9Rv4EBlg?pwduatm 提取碼: uatm

springboot啟動事件CommandLineRunner使用

什么是CommandRunner CommandRunner是springboot啟動完成時會調用的一個runner 啟動參數會傳遞到這個runner 我們能用來做一些初始化工作和緩存預熱等工作 ApplicationRunner VS CommandRunner? 這兩個Runner作用一樣 只是得到的啟動參數格式不一樣 前者是一個Argument對象…

數據可視化TensorboardX和tensorBoard安裝及使用

tensorBoard 和TensorboardX 安裝及使用指南 tensorBoard 和 TensorBoardX 是用于可視化機器學習實驗和模型訓練過程的工具。TensorBoard 是 TensorFlow 官方提供的可視化工具,而 TensorBoardX 是其社區驅動的替代品,支持 PyTorch 等其他框架。以下是它…

藍橋杯C++基礎算法-多重背包

這段代碼實現了一個多重背包問題的動態規劃解法。多重背包問題與完全背包問題類似,但每個物品有其數量限制。以下是代碼的詳細思路解析: 1. 問題背景 給定 n 個物品,每個物品有其體積 v[i]、價值 w[i] 和數量 s[i],以及一個容量為…

【SUNO】【AI作詞】【提示詞】

仿寫歌詞提示詞模板(升級版) 一、仿寫目標 風格定位 音樂風格: [填寫目標風格,如:民謠/流行/古風/電子/爵士等]參考案例:如《成都》的敘事民謠,《孤勇者》的勵志流行。 情感基調: […

26考研——樹與二叉樹_樹與二叉樹的應用(5)

408答疑 文章目錄 三、樹與二叉樹的應用哈夫曼樹和哈夫曼編碼哈夫曼樹的定義概念帶權路徑長度(WPL)計算示例分析 哈夫曼樹的構造算法描述哈夫曼樹的性質示例 哈夫曼編碼Huffman樹的編碼規則Huffman樹的構建過程前綴編碼前綴編碼的分析及應用 Huffman樹的…

【VUE】day06 動態組件 插槽 自定義指令 ESlint

【VUE】day06 動態組件 & 插槽 & 自定義指令 1. 動態組件1.1 通過不同的按鈕展示不同的組件1.1.1回顧click 1.2 keep-alive的使用1.3 keep-alive對應的生命周期函數1.3.1 keep-alive的include屬性1.3.2 exclude 1.4 組件注冊名稱和組件聲明時name的區別1.4.1 組件聲明時…

nodejs-原型污染鏈

還是老規矩,邊寫邊學,先分享兩篇文章 深入理解 JavaScript Prototype 污染攻擊 | 離別歌 《JavaScript百煉成仙》 全書知識點整理-CSDN博客 Ctfshow web入門 nodejs篇 web334-web344_web334 ctfshow-CSDN博客 334-js審計 var express require(expr…

Oracle 數據庫通過exp/imp工具遷移指定數據表

項目需求:從prod數據庫遷移和復制2個表(BANK_STATE,HBS)的數據到uat數據庫環境。 數據庫版本:Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 遷移工具:客戶端exp/imp工具 -- 執行命令 從Prod數據庫導出數據exp us…

企業級基于SpringBoot的MQTT的構建和使用

基于SpringBoot的MQTT配置及使用 首先要使用EMQX搭建一個MQTT服務器&#xff0c;參考文檔&#xff1a;EMQX快速開始 本著開源分享的觀點&#xff0c;閑話不多說&#xff0c;直接上代碼 導入Maven <dependency><groupId>org.springframework.integration</gro…

26考研——圖_圖的代碼實操(6)

408答疑 文章目錄 五、圖的代碼實操圖的存儲鄰接矩陣結構定義初始化插入頂點獲取頂點位置在頂點 v1 和 v2 之間插入邊獲取第一個鄰接頂點獲取下一個鄰接頂點顯示圖 鄰接表結構定義初始化圖插入頂點獲取頂點位置在頂點 v1 和 v2 之間插入邊獲取第一個鄰接頂點獲取下一個鄰接頂點…

開源webmail郵箱客戶端rainloop的分支版本SnappyMail 設置發件人允許多重身份

RainLoop已多年未更新&#xff0c;SnappyMail 是 RainLoop 的分支&#xff0c;由社區維護。SnappyMail 不僅修復了漏洞&#xff0c;還增加了更多功能和優化。對 IMAP 支持更好&#xff0c;移動端體驗也比 RainLoop 更細致。 安裝過程和設置跟RainLoop一樣&#xff1a; 以寶塔面…

海量數據場景題--查找兩個大文件的URL

查找兩個大文件共同的URL 給定 a、b 兩個文件&#xff0c;各存放 50 億個 URL&#xff0c;每個 URL 各占 64B&#xff0c;找出 a、b 兩個文件共同的 URL。內存限制是 4G。 操作邏輯&#xff1a; 使用哈希函數 hash(URL) % 1000? 將每個URL映射到0-999的編號 文件A切割為a0, a1…