1. 程式人生 > 實用技巧 >JavaScript 的 深拷貝和淺拷貝

JavaScript 的 深拷貝和淺拷貝

深拷貝和淺拷貝都是針對的引用型別,

JS中的變數型別分為值型別(基本型別)和引用型別;

對值型別進行復制操作會對值進行一份拷貝,而對引用型別賦值,則會對地址進行拷貝,最終兩個變數指向同一份資料

一、先來看看JS中的資料型別

let x = 1;        //number型別
let x = 0.1;     //number型別,JS不區分整數值和浮點數值

let x = "hello world"; //由雙引號內文字構成字串
let x = 'javascript';   //單引號內文字同樣可以構成字串

let x = true;    // boolean 布林型別

let x = null
; let x = undefined; //null和undefined很相似,是特殊的型別

JS 中資料分為兩種型別:

  • 原始資料型別
    • number
    • string
    • boolean
    • null
    • undefined
  • 物件資料型別
    • array 陣列 特殊物件型別
    • function 函式 特殊物件型別
    • object 物件

還有 undefined 和 null,此處暫不討論

object物件需要注意的點:

  • 物件是可變的,即值是可以修改的
  • 物件的比較並非值得比較

比如:var a = [], b = [];

   a == b; //false,只有在引用相同(指向的地址相同)時,兩個只才會相等

由此可以延伸出深拷貝和淺拷貝 的問題。

=========================================================================

我們的困惑:

1. 看著相等,卻又不等

2. 想要不等,卻又相等

那麼造成這樣問題的原因在哪呢?

> 物件的引用

> 引用只會對地址進行賦值,所以

1. 不同的變數 a 和 b,他們的地址不同,即使資料相同,本身也不會相等,這是造成困惑一的原因;

2. 而變數 aa 和 bb 指向同一地址,當該地址的資料改變時,所有使用該地址的變數全部改變(同一資料),這是造成困惑二的原因

達不到我們想要的效果,怎麼辦呢?

二、引用(物件)資料型別的賦值和比較問題

解決辦法:笨辦法,也是唯一的方式,既然物件資料型別 是由基本資料型別組成的,而基本資料型別可以正常賦值、比較,那我們就把物件型別變成一個個的基本型別進行操作

方法一: 遍歷物件中的內容,一個一個的進行賦值,這樣只進行一層拷貝的方式了,就是淺拷貝

// 淺拷貝方法
function shallowClone(source) {
    let target = {};
    for(leti in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }
    return target;
}

方法二: 相對於一層拷貝的淺拷貝,無線層次的拷貝叫做 深拷貝

// 簡單深拷貝
function clone(source) {
    let target = {};
    for(let i in source) {
        if (source.hasOwnProperty(i)) {
            if (typeof source[i] === 'object') {
                target[i] = clone(source[i]); // 判斷仍是物件,就進行遞迴
            } else {
                target[i] = source[i];
            }
        }
    }
    return target;
}

上面 clone 方法 和 shallowClone 方法的 區別就是 多了 遞迴

但是仍然有些問題需要注意:

  • 引數需要檢驗
  • 判斷是否物件的邏輯不夠嚴謹
  • 需要考慮陣列的情況

暫不細說,判斷物件可以用此方法:

// 更嚴謹的判斷物件的方法
function isObject(x) {
    return Object.prototype.toString.call(x) === '[object Object]';
}

當然我們也可以參考其他方法或使用外掛

比如: 簡單粗暴的JSON.parse(JSON.stringify(oldObj))

比如:ES6的assign方法(淺拷貝)

比如: 通過immutableJS實現深拷貝

三、最後

無論 淺拷貝,還是深拷貝 都會帶來效能問題(平白的需要遍歷,只是重新賦值)

所以我們物件最好寫的淺一點,精簡一點。。。

詳細可以看這篇:https://yanhaijing.com/javascript/2018/10/10/clone-deep/