ES6 深克隆與淺克隆詳解:原理、實現與應用場景

ES6 深克隆與淺克隆詳解:原理、實現與應用場景

一、克隆的本質與必要性

在 JavaScript 中,數據分為兩大類型:

  • 基本類型:Number、String、Boolean、null、undefined、Symbol、BigInt
  • 引用類型:Object、Array、Function、Date、RegExp 等

克隆的必要性

const original = { a: 1, b: { c: 2 } };
const copy = original; // 引用復制original.a = 10;
console.log(copy.a); // 10 - 修改影響副本original.b.c = 20;
console.log(copy.b.c); // 20 - 深層修改也影響副本

二、淺克隆(Shallow Clone)

1. 概念與特點

  • 只復制對象的第一層屬性
  • 嵌套對象仍然是引用關系
  • 適用于簡單對象結構

2. ES6 實現方式

2.1 擴展運算符(…)
const obj = { a: 1, b: { c: 2 } };
const shallowClone = { ...obj };obj.b.c = 3;
console.log(shallowClone.b.c); // 3(受影響)
2.2 Object.assign()
const arr = [1, 2, { d: 4 }];
const arrClone = Object.assign([], arr);arr[2].d = 5;
console.log(arrClone[2].d); // 5(受影響)
2.3 Array.slice()
const original = [1, { name: 'Alice' }];
const clone = original.slice();original[1].name = 'Bob';
console.log(clone[1].name); // 'Bob'(受影響)

3. 使用場景

  • 組件 props 傳遞配置對象
  • 狀態管理中的狀態快照
  • 簡單數據結構的臨時拷貝

三、深克隆(Deep Clone)

1. 概念與特點

  • 遞歸復制對象的所有層級
  • 創建完全獨立的內存副本
  • 修改原對象不影響克隆對象

2. 手動實現深克隆

基礎遞歸實現:
function deepClone(source) {// 基本類型直接返回if (source === null || typeof source !== 'object') {return source;}// 處理數組if (Array.isArray(source)) {return source.map(item => deepClone(item));}// 處理對象const clone = {};for (const key in source) {if (source.hasOwnProperty(key)) {clone[key] = deepClone(source[key]);}}return clone;
}// 測試
const obj = { a: 1, b: { c: 2 } };
const deepCopy = deepClone(obj);obj.b.c = 3;
console.log(deepCopy.b.c); // 2(不受影響)
增強版(處理特殊對象):
function enhancedDeepClone(source, map = new WeakMap()) {// 基本類型直接返回if (source === null || typeof source !== 'object') {return source;}// 處理循環引用if (map.has(source)) return map.get(source);// 特殊對象處理switch (source.constructor) {case Date:return new Date(source);case RegExp:return new RegExp(source);case Set:return new Set([...source].map(item => enhancedDeepClone(item, map)));case Map:return new Map([...source].map(([k, v]) => [enhancedDeepClone(k, map), enhancedDeepClone(v, map)]));case ArrayBuffer:return source.slice(0);}// 普通對象和數組const clone = Array.isArray(source) ? [] : {};map.set(source, clone);// 處理Symbol鍵const symbolKeys = Object.getOwnPropertySymbols(source);const allKeys = [...Object.keys(source), ...symbolKeys];for (const key of allKeys) {clone[key] = enhancedDeepClone(source[key], map);}return clone;
}

3. JSON 序列化法(有局限)

const obj = {date: new Date(),regex: /pattern/g,func: () => console.log('test')
};const jsonClone = JSON.parse(JSON.stringify(obj));console.log(jsonClone);
// {
//   date: "2023-08-15T12:00:00.000Z", // Date轉為字符串
//   regex: {},                        // RegExp變為空對象
//   func: null                        // 函數丟失
// }

JSON方法的缺陷

  1. 丟失函數屬性
  2. 忽略Symbol鍵
  3. Date對象轉為字符串
  4. RegExp變為空對象
  5. 無法處理循環引用
  6. 忽略undefined值

4. 現代瀏覽器原生API:structuredClone()

const obj = {date: new Date(),regex: /pattern/g,array: [1, 2, 3],set: new Set([4, 5, 6])
};const clone = structuredClone(obj);console.log(clone);
// {
//   date: Date, // 保持Date對象
//   regex: /pattern/g, // 保持正則
//   array: [1,2,3],
//   set: Set(3) {4,5,6}
// }

支持的數據類型

  • 原始值
  • Array/ArrayBuffer
  • Map/Set
  • Date/RegExp
  • Blob/File/FileList
  • ImageData/ImageBitmap
  • 錯誤類型(Error, EvalError等)
  • 對象字面量(僅包含以上類型)

不支持的類型

  • Function
  • DOM節點
  • 對象方法
  • 屬性描述符/getter/setter
  • 原型鏈

四、深克隆性能優化

1. 循環引用處理

function deepClone(source, map = new WeakMap()) {// ... 其他代碼if (map.has(source)) {return map.get(source);}const target = new constructor();map.set(source, target);// ... 克隆邏輯
}

2. 非遞歸實現(棧代替遞歸)

function deepCloneStack(source) {const stack = [];const map = new WeakMap();const target = new source.constructor();stack.push([source, target]);map.set(source, target);while (stack.length) {const [current, parent] = stack.pop();for (const key in current) {if (current.hasOwnProperty(key)) {const value = current[key];// 基本類型直接賦值if (value === null || typeof value !== 'object') {parent[key] = value;continue;}// 處理循環引用if (map.has(value)) {parent[key] = map.get(value);continue;}// 創建新對象const child = new value.constructor();parent[key] = child;map.set(value, child);stack.push([value, child]);}}}return target;
}

五、應用場景與最佳實踐

1. 淺克隆適用場景

  • 組件 props 傳遞配置對象
  • Redux reducer 中的狀態更新
  • 函數參數中的對象擴展
  • 無嵌套結構的簡單數據對象
// Vue 組件 props 解構
export default {props: ['config'],setup(props) {const localConfig = { ...props.config }; // 淺克隆}
}

2. 深克隆必要場景

  • 狀態管理中的初始狀態快照
  • 需要完全隔離的緩存數據
  • 撤銷/重做功能實現
  • 復雜配置對象的版本管理
  • Web Worker 數據傳輸
// 在 Web Worker 中處理數據
worker.postMessage(structuredClone(largeDataSet));

3. 現代框架中的克隆實踐

Vue 狀態管理:
// 使用深克隆創建初始狀態
const initialState = deepClone(fetchedData);// 在組件中使用
export default {data() {return {localState: deepClone(this.initialState)}}
}
React 狀態更新:
function reducer(state, action) {switch (action.type) {case 'UPDATE_USER':// 深合并示例return {...state,user: {...state.user,...action.payload}};case 'ADD_ITEM':// 數組深克隆return {...state,items: [...state.items, action.item]};}
}

六、克隆方案選擇指南

場景特征推薦方案原因說明
簡單對象無嵌套淺克隆性能最優
需要保留特殊對象類型structuredClone()原生支持多種類型
包含函數/循環引用lodash.cloneDeep完整處理復雜情況
現代瀏覽器環境structuredClone()原生性能最好
需要處理DOM節點自定義實現需特殊處理DOM API
超大對象(>10MB)分塊克隆避免阻塞主線程
高頻克隆操作(>1000次/秒)淺克隆+immutable性能敏感場景優化

七、總結與最佳實踐

  1. 理解數據本質:區分基本類型和引用類型
  2. 明確克隆需求:深克隆還是淺克隆
  3. 選擇合適方案
    • 優先使用瀏覽器原生API:structuredClone()
    • 復雜場景使用成熟庫:lodash的_.cloneDeep()
    • 特殊需求自定義實現
  4. 處理邊界情況
    • 循環引用使用WeakMap
    • 特殊對象類型單獨處理
    • Symbol屬性不能忽略
  5. 性能考量
    • 避免在循環中深度克隆大對象
    • 考慮使用不可變數據結構
    • 超大對象使用分塊處理
// 最佳實踐示例
import { cloneDeep } from 'lodash-es';// 需要完全隔離的數據
const configBackup = cloneDeep(runtimeConfig);// 瀏覽器環境簡單克隆
const stateSnapshot = structuredClone(appState);// 淺克隆配置對象
const currentSettings = { ...defaultSettings, ...userSettings };

掌握深淺克隆的區別和實現方式,是寫出健壯JavaScript應用的關鍵技能。根據具體場景選擇合適的克隆策略,可以有效避免數據污染和意外行為。

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

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

相關文章

新聞數據加載(鴻蒙App開發實戰)

本案例基于ArkTS的聲明式開發范式,介紹了數據請求和onTouch事件的使用。包含以下功能: 數據請求。列表下拉刷新。列表上拉加載。 網絡數據請求需要權限:ohos.permission.INTERNET 一、案例效果截圖 操作說明: 點擊應用進入主頁…

辦公效率王Word批量轉PDF 50 +文檔一鍵轉換保留原格式零錯亂

各位辦公小能手們,我跟你們說啊!在辦公的時候,咱經常會碰到要把一堆Word文檔轉成PDF格式的情況,比如說要統一文件格式、保護文檔內容或者方便分享啥的。這時候,就需要用到Word批量轉換成PDF的軟件啦。下面我就給你們好…

一張Billing項目的流程圖

流程圖 工作記錄 2016-11-11 序號 工作 相關人員 1 修改Payment Posted的導出。 Claim List的頁面加了導出。 Historical Job 加了Applied的顯示和詳細。 郝 識別引擎監控 Ps (iCDA LOG :剔除了160篇ASG_BLANK之后的結果): LOG_File 20161110.txt BLANK_CDA/ALL 45/10…

SpringAI系列4: Tool Calling 工具調用 【感覺這版本有bug】

前言:在最近發布的 Spring AI 1.0.0.M6 版本中,其中一個重大變化是 Function Calling 被廢棄,被 Tool Calling 取代。Tool Calling工具調用(也稱為函數調用)是AI應用中的常見模式,允許模型通過一組API或工具…

第六十三節:深度學習-模型推理與后處理

深度學習模型訓練完成后,如何高效地將其部署到實際應用中并進行準確預測?這正是模型推理與后處理的核心任務。OpenCV 的 dnn 模塊為此提供了強大支持,本文將深入探討 OpenCV 在深度學習模型推理與后處理中的關鍵技術與實踐。 第一部分:基礎概念與環境搭建 1.1 核心概念解析…

uniapp開發企業微信小程序時 wx.qy.login 在uniapp中使用的時候,需要導包嗎?

在 UniApp 中使用 “wx.qy.login” 不需要手動導包,但需要滿足以下條件: 一、環境要求與配置 1� 企業微信環境判斷 必須確保當前運行環境是企業微信客戶端,通過 “uni.getSystemInfoSync().environment” 判斷是否為 “wxwork”…

ONLYOFFICE文檔API:更強的安全功能

在數字化辦公時代,文檔的安全性與隱私保護已成為企業和個人用戶的核心關切。如何確保信息在存儲、傳輸及協作過程中的安全,是開發者與IT管理者亟需解決的問題。ONLYOFFICE作為一款功能強大的開源辦公套件,不僅提供了高效的文檔編輯與協作體驗…

Linux系統編程之共享內存

概述 在Linux系統中,共享內存也是一種高效的進程間通信機制,允許兩個或多個進程共享同一塊物理內存區域。通過這種方式,不同進程可以直接訪問和操作相同的數據,從而避免了數據的復制。由于數據直接在內存中共享,沒有額…

零知開源——STM32F407VET6驅動Flappy Bird游戲教程

簡介 本教程使用STM32F407VET6零知增強板驅動3.5寸TFT觸摸屏實現經典Flappy Bird游戲。通過觸摸屏控制小鳥跳躍,躲避障礙物柱體,挑戰最高分。項目涉及STM32底層驅動、圖形庫移植、觸摸控制和游戲邏輯設計。 目錄 簡介 一、硬件準備 二、軟件架構 三、…

Elasticsearch創建快照倉庫報錯處理

創建快照倉庫報錯: 根據報錯提示的信息,問題可能出在 Elasticsearch 的配置中。當你嘗試創建一個文件系統(fs)類型的快照倉庫時,雖然已經指定了 location 參數,但 Elasticsearch 仍然報錯,這通…

服務器如何配置防火墻管理端口訪問?

配置服務器防火墻來管理端口訪問,是保障云服務器安全的核心步驟。下面我將根據你使用的不同操作系統(Linux: Ubuntu/Debian/CentOS;Windows Server)介紹常用防火墻配置方法。 ? 一、Linux 防火墻配置(UFW / firewalld…

Redis最佳實踐——安全與穩定性保障之連接池管理詳解

Redis 在電商應用的連接池管理全面詳解 一、連接池核心原理與架構 1. 連接池工作模型 #mermaid-svg-G7I3ukCljlJZAXaA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-G7I3ukCljlJZAXaA .error-icon{fill:#552222;}…

打印機無法遠程打印?可以本地打印,本地網絡打印機設置給異地使用

很多小伙伴常有打印、遠程打印的需求,特別是對于電商人、跨境電商、教師、產品經理、實驗人員等群體來說掌握這項技能可謂是能夠在很多場景下帶來便捷,大幅提升做事效率!打印機是家庭和企業經常用到的設備,很多情況下會遇到本地可…

【Linux】進程地址空間揭秘(初步認識)

10.進程地址空間(初步認識) 文章目錄 10.進程地址空間(初步認識)一、進程地址空間的實驗現象解析二、進程地址空間三、虛擬內存管理補充:數據的寫時拷貝(淺談)補充:頁表(…

深入探討redis:主從復制

前言 如果某個服務器程序,只部署在一個物理服務器上就可能會面臨一下問題(單點問題) 可用性問題,如果這個機器掛了,那么對應的客戶端服務也相繼斷開性能/支持的并發量有限 所以為了解決這些問題,就要引入分布式系統&#xff0c…

MacOS安裝Docker Desktop并漢化

1. 安裝Docker Desktop 到Docker Desktop For Mac下載對應系統的Docker Desktop 安裝包,下載后安裝,沒有賬號需要注冊,然后登陸即可。 2. 漢化 前往漢化包下載鏈接下載對應系統的.asar文件 然后將安裝好的文件覆蓋原先的文件app.asar文件…

索引的選擇與Change Buffer

1. 索引選擇與Change Buffer 問題引出:普通索引 vs 唯一索引 ——如何選擇? 在實際業務中,如果一個字段的值天然具有唯一性(如身份證號),并且業務代碼已確保無重復寫入,那就存在兩種選擇&…

lua注意事項

感覺是lua的一大坑啊,它還不如函數內部就局部變量呢 注意函數等內部,全部給加上local得了

【多線程初階】死鎖的產生 如何避免死鎖

文章目錄 關于死鎖一.死鎖的三種情況1.一個線程,一把鎖,連續多次加鎖2.兩個線程,兩把鎖3.N個線程,M把鎖 --哲學家就餐問題 二.如何避免死鎖死鎖是如何構成的(四個必要條件)打破死鎖 三.死鎖小結 關于死鎖 一.死鎖的三種情況 1.一個線程,一把鎖,連續多次加鎖 -->由synchroni…

【NLP基礎知識系列課程-Tokenizer的前世今生第二課】NLP 中的 Tokenizer 技術發展史

從詞表到子詞:Tokenizer 的“進化樹” 我們常說“語言模型是理解人類語言的工具”,但事實上,模型能不能“理解”,關鍵要看它接收到了什么樣的輸入。而 Tokenizer,就是這一輸入階段的設計者。 在 NLP 的發展歷程中&am…