javascript
中通常有按值傳遞和按引用傳遞兩種複製方式:javascript
按值傳遞的是基本數據類型(Number,String,Boolean,Null,Undefined,Symbol),通常存放於內存中的棧區,存取速度快,存放量小;java
按引用傳遞的是引用類型(Object,Array,Function),通常存放與內存中的堆區,存取速度慢,存放量大,其引用指針存於棧區,並指向引用自己。正則表達式
深拷貝和淺拷貝是相對於引用類型而言的:數組
淺拷貝: 指兩個js 對象指向同一個內存地址,其中一個改變會影響另外一個;bash
深拷貝: 指複製後的新對象從新指向一個新的內存地址,兩個對象改變互不影響。數據結構
淺拷貝經常使用的方法以下:框架
var arr = [1,2,3];
var newarr = arr;
newarr[0] = "one";
console.log(arr); // ["one", 2, 3]
console.log(newarr); // ["one", 2, 3]
console.log(arr==newarr); // true
console.log(arr===newarr); // true
複製代碼
Object.assign()
方法是ES6的新函數,能夠把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,而後返回目標對象。拷貝的是對象的屬性的引用,而不是對象自己,可是也能夠實現一層深拷貝:var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a); // true
console.log(obj.a.a===newObj.a.a); // true
複製代碼
$.extend({},obj)
使用遞歸思路實現了淺拷貝和深拷貝,第一個參數類型爲Boolean,當爲false的時候必須省略不寫則是淺拷貝,當爲true的時候爲深拷貝:var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a); // true
console.log(obj.a.a===newObj.a.a); // true
複製代碼
淺拷貝是咱們常用的操做一些對象或數組的有效方法,具體的使用須要結合實際場景來合理的使用,還要考慮一些兼容性的問題,可是在大多實際情景下咱們須要使用更多的是深拷貝,尤爲是在各類MVVM
框架中,引入了狀態管理,更多的體如今數據流中但願咱們不改變原對象,這樣的話實現深拷貝就顯得尤其重要。dom
var obj = { a: 10, b: 20};
var newObj = { a: obj.a, b: obj.b};
newObj.b = 100;
console.log(obj); // { a: 10, b: 20}
console.log(newObj); // { a: 10, b: 100};
console.log(obj == newObj); // false
console.log(obj === newObj); // false
複製代碼
Object.assign()
方法是ES6的新函數,只能簡單的複製一層屬性到目標對象,還得考慮兼容性:var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.b = 100;
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello", b: 100 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
複製代碼
JSON.parse(JSON.stringify(obj))
是最簡單粗暴的深拷貝,可以處理JSON格式的全部數據類型,可是對於正則表達式類型、函數類型等沒法進行深拷貝,並且會直接丟失相應的值,還有就是它會拋棄對象的constructor
。也就是深拷貝以後,無論這個對象原來的構造函數是什麼,在深拷貝以後都會變成Object。同時若是對象中存在循環引用的狀況也沒法正確處理:var obj = { a: {a: "hello"}, b: 33 };
var newObj = JSON.parse(JSON.stringify(obj));
newObj.b = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33};
console.log(obj==newObj); // false
console.log(obj===newObj); // false
複製代碼
$.extend(true,{},obj)
使用遞歸思路能夠實現深拷貝,要求第一個參數必須爲true:var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend(true, {}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
複製代碼
lodash中
的_.clone(obj, true)
等價於_.cloneDeep(obj)
兩個方法,lodash花了大量的代碼來實現ES6引入的大量新的標準對象,並針對存在環的對象的處理也是很是出色的,所以對於深拷貝來講lodash和其餘庫相比最友好:var obj = { a: {a: "hello"}, b: 33 };
var newObj = _.cloneDeep(obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
複製代碼
deepClone()
:function deepClone(obj){
if(typeof obj !== "object") return;
let newObj = obj instanceof Array ? [] : {};
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
let obj = {a: 11, b: function(){}, c: {d: 22}};
deepClone(obj); // {a: 11, b: f(), c: {d: 22}};
複製代碼
對於深拷貝來講最經常使用的就是這些方法,固然還有其餘的一些庫,好比deepCopy
等,此外數組經常使用contact和slice來實現深拷貝,不一樣的方法有其最好的適用環境,仍是那句話:"離開場景談性能就是耍流氓",下面用數據具體分析一下一維數據結構和二維數據結構在不一樣方法下的性能對比。函數
var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = Math.random();
}
console.time("assign");
var newObj = Object.assign({}, obj);
console.timeEnd("assign");
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");
複製代碼
通過屢次實驗分析發現,一維數據結構的深拷貝方法性能最佳的爲
Object.assign()
;
var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = {};
for (var j = 0; j < 100; j++) {
obj[i][j] = Math.random();
}
}
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");
複製代碼
通過屢次實驗分析發現,二維數據結構的深拷貝方法性能最佳的爲
JSON.parse(JSON.stringify())
;
一維數據結構的深拷貝方法建議使用:Object.assign()
;性能
二維數據結構及以上的深拷貝方法建議使用:JSON.parse(JSON.stringify())
;
特別複雜的數據結構的深拷貝方法建議使用:Loadsh.cloneDeep()
;
更多精彩內容歡迎關注個人公衆號【天道酬勤Lewis】