天天看點

JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式

作者:earllynn

#頭條創作挑戰賽#

前端小白Earl筆記

JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式

對于小白如Earl來說,要弄明白JavaScript中指派、淺拷貝和深拷貝這三者的差別就需要先搞明白JavaScript中的變量類型有哪些,以及引用資料類型和基本資料類型的差別。

JavaScript中的變量類型

js變量類型可分為以下兩大類(基本資料類型中ECMAScript 2016新增的Symbol不在本篇文章的讨論範圍,暫不列入)

  • 值類型(基本資料類型):字元串(string)、數值(number)、布爾值(boolean)、null、undefined
  • 引用資料類型:對象(Object)【在JS中除了基本資料類型以外的都是對象】

引用資料類型和基本資料類型的差別

基本資料類型是存放在棧記憶體(stack)中的簡單資料段,是直接按值存放的,是以可以直接按值通路。基本資料類型的值不能添加屬性和方法。基本資料類型的變量名稱和和值都會儲存在棧記憶體中。

引用資料類型則是存放在堆記憶體(heap)中的對象。它們的值是存在于堆記憶體中的,變量名稱其實是儲存的在棧記憶體中的一個指針,也就是說棧記憶體中儲存的隻是堆記憶體中的引用位址。

指派

我們在給基本資料類型指派的時候,就是直接的把棧記憶體中儲存的值傳給變量。

//例(1) 
var a = 1; 
b = a; 
b = 2;  
console.log(a); // 1           

從例(1)中可以看到,我們用b=a拷貝基本資料類型的時候,棧記憶體會開辟一個新的變量名稱和值,b和a都是獨立的。

但是,我們同樣用這種直接指派的方式來拷貝引用資料類型的時候,情況就有些不一樣了。

//例(2) 
var a = [1,2]; 
b = a; 
b[0] = 2;  
console.log(a); // [2,2]           
//例(3) 
var obj = {a: 1}; 
obj2 = obj; 
obj2.a = 2;  
console.log(obj); // {a: 2}           

從例(2)和例(3)中可以看出,當我們把一個對象(引用資料類型)指派給一個新的變量時,指派的其實是對象在棧中的引用位址,而不是堆中的資料。兩個對象指向的是同一個存儲空間,兩個對象是關聯的,無論哪個對象發生改變,其實都是改變的存儲空間的内容,兩個對象都會發生改變。

除非将其中的一個對象重新定義,才會變成兩個獨立的變量。例(4)的代碼在這裡隻是做個比較。

//例(4) 
var obj = {a: 1}; 
obj2 = obj; 
obj2 = {a: 2};  
console.log(obj); // {a: 1}           

淺拷貝

淺拷貝則比指派多了一層,把對象的值複制一份賦給一個新變量。當對象裡的屬性值是基礎資料類型時,其中一個對象改變屬性值,另一個對象不會受到影響;但是當對象裡的屬性值是引用資料類型時,那麼這裡面的引用資料類型就會是關聯的,其中一個對象改變屬性值,另一個對象相應的屬性值就會發生改變。

簡單的說,淺拷貝就相當于給對象的第一層做了周遊指派,淺拷貝的其中一種實作方式就是用for in周遊對象第一層:

JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式

淺拷貝的其他實作方式:

(1)Object.assign(target,sources) 【淺複制時,target要設個空對象或者數組,否則就相當于直接指派而不是淺複制】

(2)擴充運算符方式:cloneObj = {...obj};cloneArr= [...arr]

(3)數組還可以用:arr.slice和arr.concat

深拷貝

深拷貝則是在堆記憶體中完全開辟了一塊記憶體位址,并将原有的對象完全複制過來存放。在拷貝一個對象的時候為了避免修改對資料造成的影響,必須使用深拷貝。

深拷貝的實作方式:

(1) JSON.parse(JSON.stringify(obj))

對象的值中如果有函數、undefined、symbol等類型會被忽略。

(2)遞歸周遊

JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式
JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式
JS指派、淺拷貝和深拷貝的差別,以及深淺拷貝的實作方式

繼續閱讀