JS深拷貝詳解

一、概述

在JS開發中,我們常常需要複製或者克隆一個對象。對於簡單數據類型,如數字、字符串、布爾值等,這是很容易實現的。但是對於複雜數據類型,如對象或者數組,就需要用到JS的深拷貝。深拷貝是指將一個對象完全複製一份,並開闢一個新的內存空間存放新對象,新對象的改動不會影響原對象。相比之下,JS的淺拷貝只是複製對象的引用,並沒有開闢新的內存空間,因此改動新對象也會影響原對象。

二、基本實現方法

JS的深拷貝可以通過兩種方法實現。

1. JSON.parse()

function deepClone(obj) {
  return JSON.parse(JSON.stringify(obj));
}

通過JSON.stringify()將對象轉化為字符串,然後再通過JSON.parse()將字符串轉化為新的對象,新對象就是原對象的深拷貝。這種方法的優點是實現簡單,代碼量小,但是缺點也顯而易見:無法處理對象中含有函數、正則表達式等非JSON格式數據類型。而且該方法在處理大型對象的時候性能很低,因為需要進行兩次序列化和反序列化操作。

2. 遞歸

function deepClone(obj, hash = new WeakMap()) {
  if (Object(obj) !== obj) return obj;
  if (hash.has(obj)) return hash.get(obj);
  let clonedObj = new obj.constructor();
  hash.set(obj, clonedObj);
  if (obj instanceof Map) {
    obj.forEach((value, key) => {
      clonedObj.set(deepClone(key, hash), deepClone(value, hash));
    });
  } else if (obj instanceof Set) {
    obj.forEach(value => {
      clonedObj.add(deepClone(value, hash));
    });
  } else {
    Object.getOwnPropertyNames(obj).forEach(prop => {
      if (Object.getOwnPropertyDescriptor(obj, prop).enumerable) {
        clonedObj[prop] = deepClone(obj[prop], hash);
      }
    });
  }
  return clonedObj;
}

遞歸方法則不會存在以上的問題,可以處理複雜對象,但是要注意對象中是否包含自引用的情況,因此需要引入哈希表記憶化處理。

三、對象中含有函數的深拷貝

使用遞歸方式進行深拷貝的時候,如果對象中含有函數,就會出現深拷貝失誤的情況。因為遞歸過程中不會拷貝函數。因此需要單獨處理對象中的函數,這可以分為兩步:

1. 拷貝函數

let _toString = Object.prototype.toString;
let _hasOwnProperty = Object.prototype.hasOwnProperty;

function isFunction(obj) {
  return _toString.call(obj) === '[object Function]' || typeof obj === 'function';
}

function cloneFunction(func) {
  let bodyReg = /(?<={)(.|\n)+(?=})/m;
  let paramReg = /(? param.trim());
        return new Function(...params, body[0]);
      } else {
        return new Function(body[0]);
      }
    } else {
      return null;
    }
  } else {
    return eval(funcString);
  }
}

function copy(obj, hash = new WeakMap()) {
  if (Object(obj) !== obj) return obj;
  if (hash.has(obj)) return hash.get(obj);
  let type = _toString.call(obj);
  let copiedObj;
  switch (type) {
    case '[object Array]':
      copiedObj = new obj.constructor();
      break;
    case '[object Function]':
      return cloneFunction(obj);
    case '[object RegExp]':
      copiedObj = new RegExp(obj.source, obj.flags);
      break;
    case '[object Date]':
      copiedObj = new Date(obj.getTime());
      break;
    case '[object Set]':
      copiedObj = new Set();
      break;
    case '[object Map]':
      copiedObj = new Map();
      break;
    default:
      copiedObj = new obj.constructor();
  }
  hash.set(obj, copiedObj);
  if (obj instanceof Map) {
    obj.forEach((value, key) => {
      copiedObj.set(copy(key, hash), copy(value, hash));
    });
  } else if (obj instanceof Set) {
    obj.forEach(value => {
      copiedObj.add(copy(value, hash));
    });
  } else {
    for (let prop in obj) {
      if (_hasOwnProperty.call(obj, prop)) {
        if (type === '[object Array]' || type === '[object Object]') {
          copiedObj[prop] = copy(obj[prop], hash);
        } else {
          Object.defineProperty(copiedObj, prop, Object.getOwnPropertyDescriptor(obj, prop));
        }
      }
    }   
  }
  return copiedObj;
}

上述代碼實現了函數的拷貝,其中cloneFunction()函數就是用於拷貝函數的。

2. 拷貝對象中的函數

function deepClone(obj, hash = new WeakMap()) {
  if (Object(obj) !== obj) return obj;
  if (hash.has(obj)) return hash.get(obj);
  let clonedObj = copy(obj, hash);
  hash.set(obj, clonedObj);
  return clonedObj; 
}

在進行遞歸遍歷後,調用copy()方法對對象進行拷貝,並記錄到哈希表中。這樣即可實現深拷貝。

四、應用場景

JS的深拷貝主要用於解決對象的克隆和數據格式轉化等問題。在以下場景中深拷貝會被廣泛應用:

1. React組件之間的通信

在React組件內部,有時需要將一個組件的狀態傳遞給子組件,這時候就需要使用深拷貝將狀態複製一份,以防止子組件改變父組件的狀態,從而避免數據污染的情況。

2. 前端緩存

在前端開發中,經常需要緩存一些數據到本地存儲中。如果數據源是一個對象,使用深拷貝可以保證數據不會在緩存的過程中被修改,從而避免數據完整性的問題。

3. 數據結構的複製

在JS中,集合類數據結構(例如Map,Set等)是常用的存儲數據的方式。在使用時,需要對這些數據結構進行複製,以便進行修改、刪除等操作。使用深拷貝可以確保複製後的數據和原數據完全獨立,防止由於互相引用導致結果不可預知。

4. 數據類型轉化

在開發中,有時需要將一個數據格式轉換成另一個格式。例如將一個JSON對象轉換為XML或CSV格式,可以通過先將JSON對象深拷貝得到一個JS對象,然後再將JS對象轉換為需要的格式。

五、總結

JS的深拷貝對於複製對象是一項非常關鍵的技術,掌握深拷貝的方法有助於提高代碼效率和數據完整性。本文介紹了兩種實現深拷貝的方法,以及如何處理對象中的函數,同時對深拷貝的應用場景作了介紹。

原創文章,作者:HUDVO,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/370459.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
HUDVO的頭像HUDVO
上一篇 2025-04-22 01:14
下一篇 2025-04-22 01:14

相關推薦

  • JS Proxy(array)用法介紹

    JS Proxy(array)可以說是ES6中非常重要的一個特性,它可以代理一個數組,監聽數據變化並進行攔截、處理。在實際開發中,使用Proxy(array)可以方便地實現數據的監…

    編程 2025-04-29
  • 解析js base64並轉成unit

    本文將從多個方面詳細介紹js中如何解析base64編碼並轉成unit格式。 一、base64編碼解析 在JavaScript中解析base64編碼可以使用atob()函數,它會將b…

    編程 2025-04-29
  • Node.js使用Body-Parser處理HTTP POST請求時,特殊字符無法返回的解決方法

    本文將解決Node.js使用Body-Parser處理HTTP POST請求時,特殊字符無法返回的問題。同時,給出一些相關示例代碼,以幫助讀者更好的理解並處理這個問題。 一、問題解…

    編程 2025-04-29
  • t3.js:一個全能的JavaScript動態文本替換工具

    t3.js是一個非常流行的JavaScript動態文本替換工具,它是一個輕量級庫,能夠很容易地實現文本內容的遞增、遞減、替換、切換以及其他各種操作。在本文中,我們將從多個方面探討t…

    編程 2025-04-28
  • JS圖片沿着SVG路徑移動實現方法

    本文將為大家詳細介紹如何使用JS實現圖片沿着SVG路徑移動的效果,包括路徑製作、路徑效果、以及實現代碼等內容。 一、路徑製作 路徑的製作,我們需要使用到SVG,SVG是可縮放矢量圖…

    編程 2025-04-27
  • 如何使用JS調用Python腳本

    本文將詳細介紹通過JS調用Python腳本的方法,包括使用Node.js、Python shell、child_process等三種方法,以及在Web應用中的應用。 一、使用Nod…

    編程 2025-04-27
  • 如何反混淆美團slider.js

    本文將從多個方面詳細闡述如何反混淆美團slider.js。在開始之前,需要明確的是,混淆是一種保護JavaScript代碼的方法,其目的是使代碼難以理解和修改。因此,在進行反混淆操…

    編程 2025-04-27
  • Python要學JS嗎?

    Python和JavaScript都是非常受歡迎的編程語言。然而,你可能會問,既然我已經學了Python,是不是也需要學一下JS呢?在本文中,我們將圍繞這個問題進行討論,並從多個角…

    編程 2025-04-27
  • 解決js ajax post 419問題

    對於使用ajax post請求時出現的419問題,我們需要進行以下幾個方面的闡述,包括返回碼的含義、可能出現的情況、解決方案等內容。 一、解析419返回碼 419返回碼錶示用戶超時…

    編程 2025-04-27
  • 神經網絡代碼詳解

    神經網絡作為一種人工智能技術,被廣泛應用於語音識別、圖像識別、自然語言處理等領域。而神經網絡的模型編寫,離不開代碼。本文將從多個方面詳細闡述神經網絡模型編寫的代碼技術。 一、神經網…

    編程 2025-04-25

發表回復

登錄後才能評論