今天來跟大家說一下JS的js深拷貝和淺拷貝,它們有什麼差別,有什麼作用呢?下面給大家舉例說明一下。
var m = { a: 10, b: 20 }var n = m;n.a = 15;// 這時m.a的值是多少
m.a會輸出15,因為這是淺拷貝,n和m指向的是同一個堆,物件複製只是複製的物件的引用。
深拷貝
深拷貝和上面淺拷貝不同,就是徹底copy一個對象,而不是copy對象的引用,例如,還是之前的例子,我們這麼寫:
var m = { a: 10, b: 20 }var n = {a:m.a,b:m.b};n.a = 15;
這次,我們再來輸出m.a ,發現m.a的值還是10,並沒有改變,m物件和n物件是雖然所有的值都是一樣的,但是在堆裡面,對應的不是同一個了,這個就是深拷貝。
深拷貝和淺拷貝
深拷貝和淺拷貝的示意圖大致如下:
淺拷貝只複製指向某個對象的指針,而不複製物件本身,新舊物件還是共享同一塊記憶體。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對像不共享內存,修改新對像不會改到原對象。
淺拷貝的實作方式
1、可以透過簡單的賦值實作
類似上面的例子,當然,我們也可以封裝一個簡單的函數,如下:
function simpleClone(initalObj) { var obj = {}; for ( var i in initalObj) { obj[i] = initalObj[i]; } return obj; } var obj = { a: "hello", b:{ a: "world", b: 21 }, c:["Bob", "Tom", "Jenny"], d:function() { alert("hello world"); } } var cloneObj = simpleClone(obj); console.log(cloneObj.b); console.log(cloneObj.c); console.log(cloneObj.d); cloneObj.b.a = "changed"; cloneObj.c = [1, 2, 3]; cloneObj.d = function() { alert("changed"); }; console.log(obj.b); console.log(obj.c); console.log(obj.d);
2、Object.assign()實作
Object.assign() 方法可以把任意多個的來源物件本身的可枚舉屬性拷貝給目標對象,然後傳回目標物件。但是 Object.assign() 進行的是淺拷貝,拷貝的是物件的屬性的引用,而不是物件本身。
var obj = { a: {a: "hello", b: 21} };var initalObj = Object.assign({}, obj);initalObj.a.a = "changed";console.log( obj.a.a); // "changed"
注意:當object只有一層的時候,是深拷貝,例如如下:
var obj1 = { a: 10, b: 20, c: 30 };var obj2 = Object.assign({}, obj1);obj2.b = 100;console.log(obj1);// { a: 10, b: 20, c: 30 } <-- 沒被改到console.log(obj2);// { a: 10, b: 100, c: 30 }
深拷貝的實作方式
1、方法一或手動複製
和上面的範例一樣,手動複製可以實現深拷貝。
2、物件只有一層的話可以使用上面的:Object.assign()函數
#3、轉成JSON 再轉回來
var obj1 = { body: { a: 10 } };var obj2 = JSON.parse(JSON.stringify(obj1));obj2.body.a = 20;console.log(obj1);// { body: { a: 10 } } <-- 沒被改到console.log(obj2);// { body: { a: 20 } }console.log(obj1 === obj2);// falseconsole.log(obj1.body === obj2.body);// false
用JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件。
可以封裝如下函數
var cloneObj = function(obj){ var str, newobj = obj.constructor === Array ? [] : {}; if(typeof obj !== 'object'){ return; } else if(window.JSON){ str = JSON.stringify(obj), //系列化对象 newobj = JSON.parse(str); //还原 } else { for(var i in obj){ newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i]; } } return newobj;};
4、遞迴拷貝
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : {}; arguments.callee(prop, obj[i]); } else { obj[i] = prop; } } return obj;}var str = {};var obj = { a: {a: "hello", b: 21} };deepClone(obj, str);console.log(str.a);
5、使用Object.create()方法
直接使用var newObj = Object. create(oldObj),可以達到深拷貝的效果。
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { var prop = initalObj[i]; // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况 if(prop === obj) { continue; } if (typeof prop === 'object') { obj[i] = (prop.constructor === Array) ? [] : Object.create(prop); } else { obj[i] = prop; } } return obj;}
6、jquery
jquery 有提供一個$.extend可以用來做 Deep Copy。
var $ = require('jquery');var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3]};var obj2 = $.extend(true, {}, obj1);console.log(obj1.b.f === obj2.b.f);// false
7、lodash
另外一個很熱門的函數函式庫lodash,也有提供_.cloneDeep用來做 Deep Copy。
var _ = require('lodash');var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3]};var obj2 = _.cloneDeep(obj1);console.log(obj1.b.f === obj2.b.f);// false
這個效能還不錯,使用起來也很簡單。
相信看了這些案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!
相關閱讀:
以上是什麼是js深拷貝和淺拷貝及其實現方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!