1. 程式人生 > 程式設計 >JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

本文例項講述了JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)。分享給大家供大家參考,具體如下:

深拷貝和淺拷貝是隻針對Object和Array這樣的引用資料型別的

淺拷貝

只是拷貝了基本型別的資料,而引用型別資料,複製後也是會發生引用,我們把這種拷貝叫做淺拷貝(淺複製)

淺拷貝只複製指向某個物件的指標,而不復制物件本身,新舊物件還是共享同一塊記憶體。但深拷貝會另外創造一個一模一樣的物件,新物件跟原物件不共享記憶體,修改新物件不會改到原物件。

賦值和淺拷貝的區別

當我們把一個物件賦值給一個新的變數時,賦的其實是該物件的在棧中的地址,而不是堆中的資料。也就是兩個物件指向的是同一個儲存空間,無論哪個物件發生改變,其實都是改變的儲存空間的內容,因此,兩個物件是聯動的。

淺拷貝是按位拷貝物件,它會建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值;如果屬性是記憶體地址(引用型別),拷貝的就是記憶體地址 ,因此如果其中一個物件改變了這個地址,就會影響到另一個物件。即預設拷貝建構函式只是對物件進行淺拷貝複製(逐個成員依次拷貝),即只複製物件空間而不復制資源。

我們先來看兩個例子,對比賦值與淺拷貝會對原物件帶來哪些改變?

// 物件賦值
var obj1 = {
 'name' : 'zhangsan','age' : '18','language' : [1,[2,3],[4,5]],};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

// 淺拷貝
var obj1 = {
 'name' : 'zhangsan',};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
 var dst = {};
 for (var prop in src) {
   if (src.hasOwnProperty(prop)) {
     dst[prop] = src[prop];
   }
 }
 return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

上面例子中,obj1是原始資料,obj2是賦值操作得到,而obj3淺拷貝得到。我們可以很清晰看到對原始資料的影響,具體請看下錶:

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

1、Object.assign()

Object.assign() 方法可以把任意多個的源物件自身的可列舉屬性拷貝給目標物件,然後返回目標物件。但是 Object.assign() 進行的是淺拷貝,拷貝的是物件的屬性的引用,而不是物件本身。

var obj = { a: {a: "kobe",b: 39} };
var initalObj = Object.assign({},obj);
initalObj.a.a = "wade";
console.log(obj.a.a); //wade

注意:當object只有一層的時候,是深拷貝

let obj = {
 username: 'kobe'
 };
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj);//{username: "kobe"}

2、Array.prototype.concat()

let arr = [1,3,{
 username: 'kobe'
 }];
let arr2=arr.concat();  
arr2[2].username = 'wade';
console.log(arr)

修改新物件會改到原物件:

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

3、Array.prototype.slice()

let arr = [1,{
 username: ' kobe'
 }];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

同樣修改新物件會改到原物件:

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

關於Array的slice和concat方法的補充說明:Array的slice和concat方法不修改原陣列,只會返回一個淺複製了原陣列中的元素的一個新陣列。

原陣列的元素會按照下述規則拷貝:

  • 如果該元素是個物件引用(不是實際的物件),slice 會拷貝這個物件引用到新的數組裡。兩個物件引用都引用了同一個物件。如果被引用的物件發生改變,則新的和原來的陣列中的這個元素也會發生改變。

  • 對於字串、數字及布林值來說(不是 String、Number 或者 Boolean 物件),slice 會拷貝這些值到新的數組裡。在別的數組裡修改這些字串或數字或是布林值,將不會影響另一個數組。

可能這段話晦澀難懂,我們舉個例子,將上面的例子小作修改:

let arr = [1,{
 username: ' kobe'
 }];
let arr3 = arr.slice();
arr3[1] = 2
console.log(arr,arr3);

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

4.ES6

let {...b} = a; let [...b]= a; //當object只有一層的時候,是深拷貝

深拷貝

深拷貝:在計算機中開闢了一塊新的記憶體地址用於存放複製的物件。(對屬性中所有引用型別的值,遍歷到是基本型別的值為止。)

實現方式

1、JSON.parse(JSON.stringify())

let arr = [1,{
 username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr,arr4)

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

原理: 用JSON.stringify將物件轉成JSON字串,再用JSON.parse()把字串解析成物件,一去一來,新的物件產生了,而且物件會開闢新的棧,實現深拷貝。

這種方法雖然可以實現陣列或物件深拷貝,但不能處理函式。

let arr = [1,{
 username: ' kobe'
},function(){}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan'; 
console.log(arr,arr4)

JS賦值、淺拷貝和深拷貝(陣列和物件的深淺拷貝)例項詳解

這是因為 JSON.stringify() 方法是將一個JavaScript值(物件或者陣列)轉換為一個 JSON字串,不能接受函式。

2、手寫遞迴方法

遞迴方法實現深度克隆原理:遍歷物件、陣列直到裡邊都是基本資料型別,然後再去複製,就是深度拷貝。

//定義檢測資料型別的功能函式
 function checkedType(target) {
  return Object.prototype.toString.call(target).slice(8,-1)
 }
 //實現深度克隆---物件/陣列
 function clone(target) {
  //判斷拷貝的資料型別
  //初始化變數result 成為最終克隆的資料
  let result,targetType = checkedType(target)
  if (targetType === 'object') {
   result = {}
  } else if (targetType === 'Array') {
   result = []
  } else {
   return target
  }
  //遍歷目標資料
  for (let i in target) {
   //獲取遍歷資料結構的每一項值。
   let value = target[i]
   //判斷目標結構裡的每一值是否存在物件/陣列
   if (checkedType(value) === 'Object' ||
    checkedType(value) === 'Array') { //物件/數組裡嵌套了物件/陣列
    //繼續遍歷獲取到value值
    result[i] = clone(value)
   } else { //獲取到value值是基本的資料型別或者是函式。
    result[i] = value;
   }
  }
  return result
 }

感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。

更多關於JavaScript相關內容感興趣的讀者可檢視本站專題:《JavaScript陣列操作技巧總結》、《JavaScript遍歷演算法與技巧總結》、《javascript面向物件入門教程》、《JavaScript數學運算用法總結》、《JavaScript資料結構與演算法技巧總結》及《JavaScript錯誤與除錯技巧總結》

希望本文所述對大家JavaScript程式設計有所幫助。