最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript深拷貝徹底切斷引用關(guān)聯(lián)的四種方案

 更新時(shí)間:2026年05月08日 09:25:51   作者:yqcoder  
文章討論了JavaScript中的深拷貝問題,首先解釋了賦值操作和淺拷貝的概念,然后介紹了JSON序列化、結(jié)構(gòu)化克隆API、手寫遞歸深拷貝、第三方庫Lodash等四種深拷貝方案,最后強(qiáng)調(diào)了深拷貝的常見陷阱與注意事項(xiàng),并給出了推薦使用場景和建議,需要的朋友可以參考下

在 JavaScript 中,賦值操作對于引用類型(Object, Array 等)只是復(fù)制了內(nèi)存地址(指針)。
這意味著兩個(gè)變量指向堆內(nèi)存中的同一個(gè)對象

  • 淺拷貝(Shallow Copy):只復(fù)制第一層屬性。如果屬性值是對象,則復(fù)制其引用。
  • 深拷貝(Deep Copy):遞歸復(fù)制所有層級。在堆內(nèi)存中開辟一塊全新的空間,存放完全獨(dú)立的數(shù)據(jù)副本。

1. 為什么需要深拷貝?

先看一個(gè)經(jīng)典的“事故現(xiàn)場”:

const original = {
  name: "Lingma",
  info: {
    age: 1,
    skills: ["JS", "Vue"],
  },
};

// ? 錯(cuò)誤做法:直接賦值或淺拷貝
const copy = { ...original }; // 或者 Object.assign({}, original)

// 修改副本中的嵌套對象
copy.info.age = 99;

console.log(original.info.age); // 99 ?? 原數(shù)據(jù)也被修改了!

原因... 展開運(yùn)算符只復(fù)制了 original 的第一層。info屬性是一個(gè)對象,復(fù)制的是它的引用地址。copy.infooriginal.info 指向堆內(nèi)存中的同一個(gè)對象。

深拷貝的目標(biāo):讓 copyoriginal 在內(nèi)存中完全獨(dú)立,互不影響。

2. 方案一:JSON 序列化(最快但有限制)

這是最簡單、最常用的“一行代碼”深拷貝方法。

代碼實(shí)現(xiàn)

const original = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(original));

copy.b.c = 99;
console.log(original.b.c); // 2 ? 互不影響

優(yōu)點(diǎn)

  • 極簡:無需引入庫,無需手寫邏輯。
  • 快速:瀏覽器底層 C++ 優(yōu)化,性能通常優(yōu)于手寫遞歸。

缺點(diǎn)(致命缺陷)

它會丟失轉(zhuǎn)換以下數(shù)據(jù)類型:

  1. undefined:會被忽略。
  2. Function:會被忽略。
  3. Symbol:會被忽略。
  4. Date:會變成字符串(ISO 格式),不再是 Date 對象。
  5. RegExp:會變成空對象 {}
  6. NaN, Infinity:會變成 null。
  7. 循環(huán)引用:直接報(bào)錯(cuò) TypeError: Converting circular structure to JSON。

適用場景:純數(shù)據(jù)對象(POJO),不包含函數(shù)、日期、正則等特殊類型。

3. 方案二:結(jié)構(gòu)化克隆 APIstructuredClone(現(xiàn)代首選)

ES2022 引入的全局 API,旨在解決 JSON 方法的局限性。

代碼實(shí)現(xiàn)

const original = {
  date: new Date(),
  map: new Map([["key", "value"]]),
  set: new Set([1, 2]),
  nested: { a: 1 },
};

const copy = structuredClone(original);

copy.nested.a = 99;
console.log(original.nested.a); // 1 ?

console.log(copy.date instanceof Date); // true ? (JSON 方法做不到)

優(yōu)點(diǎn)

  • 原生支持:無需 polyfill(現(xiàn)代瀏覽器已支持)。
  • 類型保留:支持 Date, Map, Set, RegExp, ArrayBuffer 等。
  • 處理循環(huán)引用:不會報(bào)錯(cuò),能正確處理環(huán)形結(jié)構(gòu)。

缺點(diǎn)

  • 不支持函數(shù):依然無法拷貝 Function。
  • 兼容性:舊版瀏覽器(如 IE)不支持,需檢查環(huán)境。

適用場景:現(xiàn)代項(xiàng)目,需要拷貝包含 Date/Map/Set 但不含函數(shù)的復(fù)雜對象。

4. 方案三:手寫遞歸深拷貝(面試必考,最靈活)

在面試中,面試官通常會要求你手寫一個(gè)深拷貝函數(shù),以考察你對遞歸、類型判斷和邊界情況的處理。

核心思路

  1. 判斷類型:如果是基本類型,直接返回。
  2. 處理特殊對象:Date, RegExp 等需特殊構(gòu)造。
  3. 處理循環(huán)引用:使用 WeakMap 緩存已拷貝的對象,防止死循環(huán)。
  4. 遞歸拷貝:遍歷對象/數(shù)組的每個(gè)屬性,遞歸調(diào)用自身。

代碼實(shí)現(xiàn)

function deepClone(obj, hash = new WeakMap()) {
  // 1. 處理 null 或非對象類型
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // 2. 處理循環(huán)引用:如果已經(jīng)拷貝過,直接返回緩存
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 3. 處理特殊內(nèi)置對象
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  if (obj instanceof RegExp) {
    return new RegExp(obj.source, obj.flags);
  }

  // 如果需要支持 Map/Set,可在此添加邏輯
  // if (obj instanceof Map) { ... }
  // if (obj instanceof Set) { ... }

  // 4. 創(chuàng)建新容器(保持原型鏈)
  // 使用 Object.create 可以保留原型鏈,比 {} 或 [] 更嚴(yán)謹(jǐn)
  const cloneObj = Array.isArray(obj)
    ? []
    : Object.create(Object.getPrototypeOf(obj));

  // 5. 存入緩存,防止循環(huán)引用
  hash.set(obj, cloneObj);

  // 6. 遞歸拷貝所有屬性
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }

  return cloneObj;
}

測試循環(huán)引用

const a = { val: 1 };
a.self = a; // 循環(huán)引用

const b = deepClone(a);
console.log(b.val); // 1
console.log(b.self === b); // true ? 正確保持了內(nèi)部引用關(guān)系

優(yōu)點(diǎn)

  • 可控:你可以決定如何處理函數(shù)、Symbol、DOM 節(jié)點(diǎn)等。
  • 兼容性好:可在任何 JS 環(huán)境中運(yùn)行。
  • 面試加分項(xiàng):展示了對 WeakMap、原型鏈和遞歸的深刻理解。

缺點(diǎn)

  • 性能較差:遞歸開銷大,深層嵌套對象可能棧溢出。
  • 代碼復(fù)雜:需要考慮很多邊界情況。

5. 方案四:第三方庫 Lodash

在生產(chǎn)環(huán)境中,如果項(xiàng)目已經(jīng)引入了 Lodash,直接使用 _.cloneDeep 是最穩(wěn)妥的選擇。

import _ from "lodash";

const copy = _.cloneDeep(original);

優(yōu)點(diǎn)

  • 健壯:經(jīng)過多年社區(qū)驗(yàn)證,處理了絕大多數(shù)邊緣情況。
  • 省心:無需自己維護(hù)拷貝邏輯。

缺點(diǎn)

  • 包體積:如果只為這一個(gè)功能引入整個(gè) Lodash,會增加打包體積(建議使用 lodash.clonedeep 按需引入)。

6. 常見陷阱與注意事項(xiàng)

函數(shù)無法被深拷貝
函數(shù)是執(zhí)行代碼的邏輯,不是數(shù)據(jù)。通常深拷貝會忽略函數(shù),或者你只能拷貝函數(shù)的字符串表示(fn.toString()),但這失去了閉包上下文。

DOM 節(jié)點(diǎn)不能深拷貝
DOM 節(jié)點(diǎn)包含大量瀏覽器內(nèi)部狀態(tài),不能用 JSON 或普通遞歸拷貝。如需拷貝 DOM,請使用原生 API node.cloneNode(true)。

性能考量
深拷貝是非常昂貴的操作。對于大型數(shù)據(jù)(如幾萬條列表),頻繁深拷貝會導(dǎo)致頁面卡頓。

優(yōu)化建議:盡量使用不可變數(shù)據(jù)(Immutable Data)思維,只在必要時(shí)拷貝;或使用結(jié)構(gòu)性共享(如 Immutable.js, Immer)。

Getter/Setter 丟失
普通的遞歸拷貝可能會丟失屬性的 get/set 描述符。如果需要保留,需使用 Object.getOwnPropertyDescriptorObject.defineProperty。

總結(jié)

方案優(yōu)點(diǎn)缺點(diǎn)推薦指數(shù)適用場景
JSON簡單、快丟失函數(shù)/Date/undefined,不支持循環(huán)引用???純數(shù)據(jù)對象,后端交互數(shù)據(jù)
structuredClone原生、支持多種類型、支持循環(huán)引用不支持函數(shù),兼容性需注意?????現(xiàn)代瀏覽器環(huán)境首選
手寫遞歸靈活、可定制、面試必備性能一般,代碼復(fù)雜????面試、特殊定制需求
Lodash穩(wěn)健、功能全增加包體積????已使用 Lodash 的項(xiàng)目

博主寄語
深拷貝不是銀彈。
在現(xiàn)代前端開發(fā)中,我們更推崇**不可變數(shù)據(jù)(Immutability)**的理念。
與其每次修改都深拷貝,不如使用 Immer 這樣的庫,通過 Proxy 代理來實(shí)現(xiàn)“看似可變,實(shí)則不可變”的高效狀態(tài)管理。

記住選型原則

  1. 簡單數(shù)據(jù)用 JSON。
  2. 現(xiàn)代環(huán)境用 structuredClone。
  3. 復(fù)雜定制手寫信。
  4. 生產(chǎn)環(huán)境靠 Lodash 或 Immer。

以上就是JavaScript深拷貝徹底切斷引用關(guān)聯(lián)的四種方案的詳細(xì)內(nèi)容,更多關(guān)于JavaScript深拷貝切斷引用關(guān)聯(lián)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于js中document.cookie全面解析

    基于js中document.cookie全面解析

    下面小編就為大家?guī)硪黄趈s中document.cookie全面解析。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • JavaScript生成圖形驗(yàn)證碼

    JavaScript生成圖形驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了JavaScript生成圖形驗(yàn)證碼的相關(guān)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • js的touch事件的實(shí)際引用

    js的touch事件的實(shí)際引用

    研究移動(dòng)端頁面的觸屏滑動(dòng)事件,搜索jquery 的相應(yīng)技術(shù)支持實(shí)在繁瑣,而js只需幾步簡單定義即可,下面與大家分享下js的touch事件的實(shí)際引用
    2014-10-10
  • 詳解JS鼠標(biāo)事件中clientX/screenX/offsetX/pageX屬性的區(qū)別

    詳解JS鼠標(biāo)事件中clientX/screenX/offsetX/pageX屬性的區(qū)別

    在熟悉業(yè)務(wù)中播放器功能的時(shí)候,發(fā)現(xiàn)時(shí)間軸上綁定了點(diǎn)擊和拖動(dòng)事件,頻繁遇到了類似pageX之類的事件屬性,所以本文就來和大家詳細(xì)聊聊clientX/screenX/offsetX/pageX等屬性的區(qū)別吧
    2024-03-03
  • javascript自定義日期比較函數(shù)用法示例

    javascript自定義日期比較函數(shù)用法示例

    這篇文章主要介紹了javascript自定義日期比較函數(shù)用法,涉及javascript日期相關(guān)轉(zhuǎn)換、運(yùn)算操作技巧,需要的朋友可以參考下
    2019-07-07
  • JS常用跨域方法實(shí)現(xiàn)原理解析

    JS常用跨域方法實(shí)現(xiàn)原理解析

    這篇文章主要介紹了JS常用跨域方法實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • JavaScript 本地存儲localStorage完全指南及應(yīng)用場景

    JavaScript 本地存儲localStorage完全指南及應(yīng)用場景

    localStorage 提供了一種非常簡潔和高效的方式來在瀏覽器中持久化數(shù)據(jù),它特別適用于存儲一些不敏感、非臨時(shí)的數(shù)據(jù),下面我們將詳細(xì)探討如何使用 localStorage 來存儲和管理數(shù)據(jù),了解它的工作原理以及常見的使用場景,感興趣的朋友一起看看吧
    2025-04-04
  • js和jquery分別驗(yàn)證單選框、復(fù)選框、下拉框

    js和jquery分別驗(yàn)證單選框、復(fù)選框、下拉框

    這篇文章主要為大家詳細(xì)介紹了js和jquery分別驗(yàn)證單選框、復(fù)選框、下拉框的具體代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2015-12-12
  • xml和web特殊字符

    xml和web特殊字符

    在html和xml文件中,往往會因?yàn)橐韵绿厥庾址鴮?dǎo)致文件解析出錯(cuò),一般通過以下函數(shù)來處理。
    2009-04-04
  • 限制文本框只能輸入數(shù)字||只能是數(shù)字和小數(shù)點(diǎn)||只能是整數(shù)和浮點(diǎn)數(shù)

    限制文本框只能輸入數(shù)字||只能是數(shù)字和小數(shù)點(diǎn)||只能是整數(shù)和浮點(diǎn)數(shù)

    這篇文章主要介紹了限制文本框只能輸入數(shù)字||只能是數(shù)字和小數(shù)點(diǎn)||只能是整數(shù)和浮點(diǎn)數(shù)的實(shí)例代碼,非常不錯(cuò),也比較實(shí)用,需要的小伙伴一起看下吧
    2016-05-05

最新評論

宁南县| 芦山县| 汝南县| 道孚县| 株洲市| 公主岭市| 青岛市| 康定县| 三河市| 长垣县| 康马县| 玉龙| 信丰县| 仁布县| 晋州市| 禄劝| 马山县| 丹凤县| 宜良县| 呈贡县| 锡林浩特市| 保康县| 南川市| 陆河县| 胶州市| 葫芦岛市| 朝阳市| 海林市| 安新县| 灵台县| 小金县| 抚州市| 葫芦岛市| 田阳县| 黄冈市| 建德市| 琼中| 巴南区| 宿州市| 长子县| 长顺县|