切換語言為:簡體

JavaScript 中你認為的深複製真的夠「深」嗎?

  • 爱糖宝
  • 2024-06-26
  • 2069
  • 0
  • 0

前言

在我們日常編碼的時候,經常會碰到一種情況,就是要複製某個物件(在js中複製只討論在引用型別,也就是物件上,因為js中基本型別的複製直接賦值就好了),拿到一個“一模一樣”的“新”物件,但是在js中,很多種方式的複製並不能有效地拿到這個“新”物件,<>而只是簡單地拿到了這個物件的引用地址,這樣的引用就有可能會影響到原物件的屬性,並不能滿足拿到一個一模一樣的“新”物件這個需求,我們稱這種複製為淺複製
深複製則相反,是能夠真正拿到一個“一模一樣”並且使用起來不影響原物件的的物件。

今天我們就來具體講講淺複製和深複製的實現吧。。

淺複製(Shallow Copy)

實際上js中大部分方法對複製的實現都是淺複製

  • shallowCopy原理
    --先建立一個新物件newObj
    --用for..in..遍歷原物件上的屬性加入到新物件中
    --藉助hasOwnProperty規避原物件隱式具有的屬性

1、使用迴圈遍歷物件的屬性(將物件屬性一個一個賦值到新物件)

function shallowCopy(obj) {
2  let newObj = {};
3  for(let key in obj) {
4    if(obj.hasOwnProperty(key)) {
5      newObj[key] = obj[key];
6    }
7  }
8  return newObj;
9}

2、利用Object.create()方法,以原物件作為實參傳遞給該方法,建立出一個新物件。

let newObj = Object.create(obj)

3、使用Object.assign()方法 object.assign()方法是es6引用可以用來合併物件用的,同樣可以實現淺複製,只要將原物件和一個空物件合併就好了

let obj = {a: 1, b: {c: 2}};
let newObj = Object.assign({}, obj);

4、陣列的Array.prototype.slice()Array.from() 對陣列進行淺複製

let array = [1, 2, {a: 3}]; 
let newArray = array.slice(); 
// 或 
let newArray = Array.from(array);

5、使用展開運算子 ...,進行解構賦值。

let array = [1, 2, {a: 3}]; 
let newArray = [...array];   //陣列
let obj = {a: 1, b: {c: 2}}; 
let newObj = {...obj};       //物件

這是一些在js中常用的淺複製的方法,淺複製所得到的物件,可能會在進行一些數值操作時,會影響到原物件的值,所以當我們需要得到一個一模一樣的物件,但是不希望它影響原物件時,這就要用到深複製 了。

深複製(Deep Copy)

在原生js中我們要實現深複製,並且是沒有物件屬性的限制,我們要用到遞迴複製,思路:為遞迴物件中的所有屬性,如果碰到基本資料型別,則直接賦值,若碰到物件,則遞迴進入,直到找到基本資料型別。

實現程式碼如下:

function deepCopy(obj){
    let newObj = {}
    for (let key in obj) {
        //key 是不是 obj 顯示具有的,隱式的不copy
        if (obj.hasOwnProperty(key)) {
            if(obj[key] instanceof Object){
              newObj[key] = deepCopy(obj[key])
            }else {
            newObj[key] = obj[key]
            }
        }
    }
    return newObj
}

當然還有其他的深複製方法
使用JSON物件的 stringifyparse 方法
這種方法簡單,但有限制,如無法處理函式、RegExp、Date、undefined等型別,且會丟失原型鏈上的屬性。

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

藉助第三方庫
使用如lodash的 _.cloneDeep 方法,這是一個強大的深複製實現,能夠處理各種複雜的物件結構,包括迴圈引用。

const _ = require('lodash'); 
let clonedObj = _.cloneDeep(originalObj);

使用擴充套件運算子結合遞迴(僅適用於陣列或扁平物件)
對於陣列,可以結合擴充套件運算子和遞迴來實現深複製,但這種方法不適用於含有巢狀物件的複雜結構。

function deepCloneArray(arr) {
return arr.map(item => Array.isArray(item) ? deepCloneArray(item) : item);
}

小結

選擇複製型別

  • 如果數據結構簡單,不包含巢狀的物件或陣列,或者你希望修改複製物件時原物件也跟著改變,可以選擇淺複製。

  • 當你需要完全獨立的資料副本,尤其是在處理複雜數據結構時,深複製是更合適的選擇。

理解JavaScript中的複製機制對於避免意外的資料修改和確保資料的正確傳遞至關重要。在實際開發中,根據具體情況選擇合適的複製方式是基本技能之一。

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.