JavaScript中級指南-04 JS深淺拷貝原理及使用方式

深淺拷貝在咱們開發項目中是常常使用到的,本篇文章帶你詳細瞭解和使用深淺拷貝的原理及使用方式。數組

首先先了解一下JS中的數據類型bash

JS中的數據類型

  • Null
  • String
  • Boolean
  • undefined
  • Number
  • Object
  • BigInt(ES10新增)
  • Symbol(ES6新增)
基本數據類型: undefined、null、Boolean、Number、String  和Symbol(ES6) 還有 BigInt(ES10新增)
引用數據類型: Object(Array, Date, RegExp, Function)
複製代碼

深淺拷貝

深淺拷貝只是針對引用類型的,由於引用類型是存放在堆內存中,在棧地址有一個或者多個地址來指向推內存的某一數據函數

淺拷貝

被複制對象的全部變量都含有與原來的對象相同的值,且全部對其對象的引用不會指向原來的對象的內存地址。ui

淺拷貝僅僅複製所考慮的對象,而不復制它所引用的對象,也就是說淺拷貝只能拷貝第一層,更加深一層次的對象依然是賦值操做,更改第二層對象的值依舊會改變原對象。spa

深拷貝

深拷貝是一個整個獨立的對象拷貝,深拷貝會拷貝全部的屬性,並拷貝屬性指向的動態分配的內存。當對象和它所引用的對象一塊兒拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢而且花銷較大。指針

深拷貝把要複製的對象所引用的對象都複製了一遍。若是你修改了「副本」的值,那麼原來的對象不會被修改,二者是相互獨立的。code

JSON實現深拷貝

let arr = [0, 1, 2, 3]
let newArr = JSON.parse(JSON.stringify(arr))
newArr[0] = 4
console.log(newArr, arr)
複製代碼

結果如圖:cdn

對比以前賦值的結果,咱們發現原來的數組arr並無被改變,能夠說二者是相互獨立的 很顯然,這就是一個深拷貝的例子

原理: JSON.parse() 方法用於將一個 JSON 字符串轉換爲對象。 JSON.stringify() 方法用於將 JavaScript 值(一般爲對象或數組)轉換爲 JSON 字符串。 在JSON.stringify()完成後,對象就轉爲了字符串,也就能夠說實實在在的複製了一個值,不存在引用之說。 再利用JSON.parse()轉爲對象,這樣達到深拷貝的目的對象

JSON實現深拷貝的缺陷

let obj = {
    nul: null,
    und: undefined,
    sym: Symbol('sym'),
    str: 'str',
    bol: true,
    num: 45,
    arr: [1, 4],
    reg: /[0-9]/,
    dat: new Date(),
    fun: function() {},  
}
console.log(JSON.parse(JSON.stringify(obj)))
複製代碼

結果如圖:blog

咱們能夠發現有些屬性被忽略了:

  • undefined
  • symbol
  • function

能夠看得出來JSON實現深拷貝也有不足之處。

ES6新特性實現淺拷貝

  • Object.assign()

Object.assign方法用於對象的合併,將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。 Object.assign(target, ...sources) target目標對象。 sources源對象。 返回的是目標對象

let obj = {
    a: '1',
    b: {
        bb: '2'
    }
}

let newObj = Object.assign({}, obj)
newObj.a = '11'
newObj.b.bb = '22'
console.log(newObj, obj)
複製代碼

結果如圖:

咱們能夠看到a1的值被改變,而b的值沒有被改變,說明了:

Obejct.assign()只能對一層進行深拷貝 若是拷貝的層數超過了一層的話,那麼就會進行淺拷貝

  • 展開運算符(...)

對象的擴展運算符(...)用於取出參數對象的全部可遍歷屬性,拷貝到當前對象之中。

let obj = {
    a: '1',
    b: {
        bb: '2'
    }
}

let newObj = {...obj}
newObj.a = '11'
newObj.b.bb = '22'
console.log(newObj, obj)
複製代碼

結果如圖:

擴展運算符只能對一層進行深拷貝 若是拷貝的層數超過了一層的話,那麼就會進行淺拷貝

原理: 對於引用類型,賦值操做符只是把存放在棧內容中的指針賦值給另一個變量。 因此在賦值完成後,在棧內存就有兩個指針指向堆內存同一個數據。 也就能夠說兩個變量在共用着同一個數據,這就是淺拷貝。

尾聲

咱們能夠看到Object.assign() 和 展開原算符對於深淺拷貝的結果是同樣。

若是拷貝的層數超過了一層的話,那麼就會進行淺拷貝

因此要慎用這兩個來進行拷貝!!!

通常咱們在項目中須要深淺拷貝的數據通常也不會具備如下三種類型

  • undefined
  • symbol
  • function

全部正常來講JSON實現拷貝就夠用了,若是有特殊需求,咱們能夠經過本身封裝一個深拷貝的函數。

相關文章
相關標籤/搜索