深拷貝和淺拷貝的區分 以及 實現

淺拷貝:複製一層對象的屬性,並不包括對象裏面的爲引用類型的數據,當改變拷貝的對象裏面的引用類型時,源對象也會改變。javascript

深拷貝:從新開闢一個內存空間,須要遞歸拷貝對象裏的引用,直到子屬性都爲基本類型。兩個對象對應兩個不一樣的地址,修改一個對象的屬性,不會改變另外一個對象的屬性。html

1.JavaScript 的變量類型

基本類型:undefined null number boolen string symbol 變量是直接按值存放的,存放在棧內存中的簡單數據段,能夠直接訪問前端

引用類型:Object, Array,Function,Date,存放在堆內存中的對象,變量保存的是一個指針,這個指針指向另外一個位置。當須要訪問引用類型(如對象,數組等)的值時,首先從棧中得到該對象的地址指針,而後再從堆內存中取得所需的數據。java

基本類型和引用類型的區別:1:內存空間(棧內存和堆內存)   2:對值的操做   3:變量的複製jquery

JavaScript存儲對象都是存地址的,因此淺拷貝會致使 obj1 和obj2 指向同一塊內存地址。改變了其中一方的內容,都是在原來的內存上作修改會致使拷貝對象和源對象都發生改變,而深拷貝是開闢一塊新的內存地址,將原對象的各個屬性逐個複製進去。對拷貝對象和源對象各自的操做互不影響。正則表達式

eg: 數組的拷貝 json

// 淺拷貝,指向的是同一片內存
	let arr1 = [1,2,3,4];
	let arr2 = arr1;
	arr2[0] = 5;
	console.log(arr1); // [5,2,3,4]
	console.log(arr2); // [5,2,3,4]

淺拷貝和深拷貝的區分

深複製和淺複製只針對像 Object, Array,Date 這樣的複雜對象的。簡單來講,淺複製只複製一層對象的屬性,而深複製則遞歸複製了全部層級。數組

function copy(obj) {
   var newObj = {};
   for (let key in obj) {
	newObj[key] = obj[key];
   }
   return newObj;
}
var obj = {name: 'TOM', arr: [1,2,3]};
var end = copy(obj);

由於淺複製只會將對象的各個屬性進行依次複製,並不會進行遞歸複製,而 JavaScript 存儲對象都是存地址的,因此淺複製會致使 obj.arr 和 end.arr 指向同一塊內存地址瀏覽器

end.arr[0] = 5;
console.log(obj.arr); // [5,2,3]
console.log(end.arr); // [5,2,3]

所以修改了end對象 可是obj對象也跟着變了函數

淺拷貝的實現 

簡單的引用複製 

function copy(copyObj) {
  var obj = {};
  for ( var i in copyObj) {
    obj[i] = copyObj[i];
  }
  return obj;
}
var x = {
  a: 1,
  b: { f: { g: 1 } },
  c: [ 1, 2, 3 ]
};
var y = copy(x);
console.log(y.b.f === x.b.f);     // true

Object.assign() 

Object.assign() 方法能夠把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象。 

var x = {
  a: 1,
  b: { f: { g: 1 } },
  c: [ 1, 2, 3 ]
};
var y = Object.assign({}, x);
console.log(y.b.f === x.b.f);     // true

深拷貝的實現 

Array的slice和concat方法 

Array的slice和concat方法不修改原數組,只會返回一個淺複製了原數組中的元素的一個新數組。之因此把它放在深拷貝里,是由於它看起來像是深拷貝。而實際上它是淺拷貝。原數組的元素會按照下述規則拷貝: 

  • 若是該元素是個對象引用 (不是實際的對象),slice 會拷貝這個對象引用到新的數組裏。兩個對象引用都引用了同一個對象。若是被引用的對象發生改變,則新的和原來的數組中的這個元素也會發生改變。
  • 對於字符串、數字及布爾值來講(不是 String、Number 或者 Boolean 對象),slice 會拷貝這些值到新的數組裏。在別的數組裏修改這些字符串或數字或是布爾值,將不會影響另外一個數組。

 若是向兩個數組任一中添加了新元素,則另外一個不會受到影響。例子以下: 

var array = [1,2,3]; 
var array_shallow = array; 
var array_concat = array.concat(); 
var array_slice = array.slice(0); 
console.log(array === array_shallow); //true 
console.log(array === array_slice); //false,「看起來」像深拷貝
console.log(array === array_concat); //false,「看起來」像深拷貝

 能夠看出,concat和slice返回的不一樣的數組實例,這與直接的引用複製是不一樣的。而從另外一個例子能夠看出Array的concat和slice並非真正的深複製,數組中的對象元素(Object,Array等)只是複製了引用。以下: 

//code from http://caibaojian.com/javascript-object-clone.html#comments
var array = [1, [1,2,3], {name:"array"}]; 
var array_concat = array.concat();
var array_slice = array.slice(0);
array_concat[1][0] = 5;  //改變array_concat中數組元素的值 
console.log(array[1]); //[5,2,3] 
console.log(array_slice[1]); //[5,2,3] 
array_slice[2].name = "array_slice"; //改變array_slice中對象元素的值 
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice

JSON對象的parse和stringify 

JSON對象是ES5中引入的新的類型(支持的瀏覽器爲IE8+),JSON對象parse方法能夠將JSON字符串反序列化成JS對象,stringify方法能夠將JS對象序列化成JSON字符串,藉助這兩個方法,也能夠實現對象的深拷貝。 

var source = { name:"source", child:{ name:"child" } } 
var target = JSON.parse(JSON.stringify(source));

這種方法使用較爲簡單,能夠知足基本的深拷貝需求,並且可以處理JSON格式能表示的全部數據類型,可是對於正則表達式類型、函數類型等沒法進行深拷貝(並且會直接丟失相應的值)。還有一點很差的地方是它會拋棄對象的constructor。也就是深拷貝以後,無論這個對象原來的構造函數是什麼,在深拷貝以後都會變成Object。同時若是對象中存在循環引用的狀況也沒法正確處理。

深度複製的徹底實現

Object.prototype.clone = function () {
var o = {};
for (var i in this) {
o[i] = this[i];
}
return o;
};
Array.prototype.clone = function () {
var arr = [];
for (var i = 0; i < this.length; i++)
if (typeof this[i] !== 'object') {
arr.push(this[i]);
} else {
arr.push(this[i].clone());
}
return arr;
};

//測試1 Object
var obj1 = {
name: 'Rattz',
age: 20,
hello: function () {
return "I'm " + name;
}
};
var obj2 = obj1.clone();
obj2.age++;
console.log(obj1.age);

//測試2 Array
var fun = function(log) {console.log},
arr1 = [1, 2, [3, 4], {a: 5, b: 6}, fun],
arr2 = arr1.clone();

console.log(arr1, arr2);

arr2[2][1]= 'Mike';
arr2[3].a = 50;
arr2[4] = 10;
console.log(arr1, arr2);

使用 jQuery 的 extend 方法

var arr1 = [1, 2, [3, 4], {a: 5, b: 6}, 7],
arr2 = $.extend(true, [], arr1);

console.log(arr1, arr2);
arr2[1] = 10;
console.log(arr1, arr2);

  

來源:前端開發博客 

相關文章
相關標籤/搜索