首先,咱們必須明確一點,就是JavaScript的變量能夠分爲如下兩種類型:javascript
基本變量是直接按值存放的,存放在棧內存中的簡單數據段,能夠直接訪問。java
存放在堆內存中的對象,變量保存的是一個指針,這個指針指向另外一個位置。當須要訪問引用類型(如對象,數組等)的值時,首先從棧內存中得到該對象的地址指針,而後再從堆內存中取得所需的數據。正則表達式
一個簡單的例子數組
var a = 2;
var obj1 = {b:2};
var obj2 = obj1;
obj2.b = 3;
console.log(obj1.b); // 3
console.log(obj2.b); // 3
複製代碼
那麼問題就來了,有一些場景咱們須要將一個對象含的值所有Copy
給另外一個對象,這個時候若是隻是簡單的賦值操做,只是對指針進行了一個複製,而在堆內存區的值並無發生改變。因此咱們獲得如下的結論:bash
深拷貝便是在堆內存區拷貝出一個對象來。函數
深拷貝是開闢一塊新的內存地址,將原對象的各個屬性逐個複製進去。對拷貝對象和源對象各自的操做互不影響。工具
以前一直有一個錯誤實現深拷貝的想法,就是遍歷一個對象的k-v對並一一複製給另外一個對象即可以實現深拷貝。ui
可是是錯誤的,這個是淺拷貝(shallowCopy)spa
緣由很簡單,當K-V
對裏value
是Object
的時候,複製過去便仍然是複製引用。好比prototype
let obj = {
a: 1,
b:{
c: 2,
d: 3
}
}
let obj2 = {}
for(let item of Object.keys(obj)){
obj2[item] = obj[item]
}
obj2.b.d = 2;
obj.b.d // 此時obj.b.d 變成了2
複製代碼
另一點
Object.assign
也是一個shallowCopy
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
複製代碼
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c = 3;
console.log(obj2.b.c); // { a: 0 , b: { c: 3}};
console.log(obj1.b.c); // { a: 0 , b: { c: 0}};
複製代碼
該方法夠處理JSON格式能表示的全部數據類型,可是沒法拷貝對象裏面的函數
,正則表達式
等,並且會喪失全部的constructor
,也就是說,將是破壞整條prototype
鏈。
剛纔提到淺拷貝只能拷貝對象的一層,那麼對淺拷貝進行遞歸即可以實現深拷貝。
function deepCopy (oldObj, newObj){
for(let key in oldObj){
if(typeof oldObj[key] != 'object'){
// 是基本類型直接複製
newObj[key] = oldObj[key];
}else {
newObj[key] = oldObj[key].constructor == '[Function: Array]'?[]:{};
deepCopy(oldObj[key],newObj[key])
}
}
}
複製代碼
arguments.calle
能夠在匿名函數中實現遞歸,此處也能夠用deepCopy(oldObj[key],newObj[key])
,該方法的缺陷是,一旦欲拷貝對象和原對象存在相互引用的狀況,即可能形成死循環。(一直往下遞歸仍然判斷爲Object即形成死循環)因此須要加上判斷跳出的語句
...
for(let key in oldObj){
if(newObj[key] === oldObj[key]){
continue;
}
...
}
...
複製代碼
loadash
loadash
有一個.cloneDeep
的方法能夠實現深拷貝。
使用方法:
obj2 = _.cloneDeep(obj1);
淺拷貝
(shallowCopy)