在js中,一切皆對象(null和undefined有待另外的了解),JavaScript 中的幾乎所有對象都是位于原型鍊頂部
Object
的執行個體。
對象的基本的一些操縱,我們是必須要知道的。
let obj = {
a: 1,
b: 2,
};
let copy = obj;
copy.a = 5;
console.log(obj.a); // ==> 5
上面的代碼應該都能了解,指派運算符不會建立一個對象的副本,它隻配置設定一個引用,當然數組也提供了concat 和slice 來進行淺拷貝。
那我們就想到了,循環周遊對象
var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
shallowObj.a = 4 ==> obj.a =1
shallowObj.arr[1] = 5 ==> obj.arr[1] = 5
顯然上面的周遊是起了一定的效果的,但是這隻能算對象的淺拷貝(shallow copy),原因:因為淺拷貝隻會将對象的各個屬性進行依次複制,并不會進行遞歸複制,而 JavaScript 存儲對象都是存位址的,是以淺拷貝會導緻 obj.arr 和 shallowObj.arr 指向同一塊記憶體位址。
那改變一下上面的代碼,我們需要用疊代來實作對對象的深拷貝(deep copy)
var obj = { a:1, arr: [2,3] };
var deepObj = deepCopy(obj);
function deepCopy(obj) {
var newObj = obj.constructor === Array ? [] : {}
if (typeof obj !== 'object') {
return
} else {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
// 判斷子元素是否為對象
newObj[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i]
}
}
}
return newObj
}
deepObj.arr[1] = 5
console.log(obj.arr[1]) ==> 3
解釋:知乎網友的解釋
對于字元串類型,淺複制是對值的複制,對于對象來說,淺複制是對對象位址的複制,并沒 有開辟新的棧,也就是複制的結果是兩個對象指向同一個位址,修改其中一個對象的屬性,則另一個對象的屬性也會改變,而深複制則是開辟新的棧,兩個對象對應兩個不同的位址,修改一個對象的屬性,不會改變另一個對象的屬性。
項目中,我也遇到Object.assign({},obj) 這種淺拷貝與JSON.parse(JSON.stringfy(obj))這種深拷貝,但是JSON.parse(JSON.stringfy(obj)) 這種方法是有缺陷的
- 不能解決循環引用的對象
- 會忽略
undefined
- 不能序列化函數
let obj = {
a: 1,
b: {
c: 2
},
}
obj.c = obj.b
obj.b.c = obj.c
let newObj = JSON.parse(JSON.stringify(obj))
let obj = {
age: undefined,
name: 'demo',
test: function test() {
return true;
},
}
let method1 = Object.assign({}, obj);
let method2 = JSON.parse(JSON.stringify(obj));
console.log(method1);
/* result
age: undefined,
{
test: function test() {
return true;
},
name: "demo"
}
*/
console.log(method2);
/* result
{
name: "demo"
}
*/
最後,我也看到了一篇文章,《深入剖析 JavaScript 的深複制》,涉及到 jQuery, underscore, lodash 等庫關于深複制的實作,有興趣可以看看。