我們使用=操作符復(fù)制對(duì)象時(shí),由于對(duì)象是通過(guò)引用傳遞的,父子對(duì)象引用的是內(nèi)存上同一個(gè)堆的地址,修改任意父子對(duì)象,父子對(duì)象都會(huì)改變,為了保證子對(duì)象修改不影響到父對(duì)象,需要進(jìn)行深拷貝
var arr = ['1','2'];
var obj = {
name:'yy',
age:['1','2'],
ojbs: {
value: 1,
text: '中國(guó)'
},
arrs:[{
value: 3,
text: '江西'
},{
value: 4,
text: '廣東'
}]
};
obj.an = '1111';
數(shù)組深拷貝
var newArr = arr.slice(0);
var newArr2 = arr.concat();
newArr.push('4')
newArr2.push('4333')
console.log(newArr)
console.log(newArr2)
對(duì)象深拷貝
var deepCopyObj = function(obj ) {
var copy;
// 處理3個(gè)簡(jiǎn)單的類型, null 或者 undefined
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if (obj instanceof Array) {
var copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
if (obj instanceof Object) {
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) {
copy[attr] = clone(obj[attr]);
}
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
console.time('deepCopyObj');
var newObj = deepCopyObj(obj);
console.log(JSON.stringify(newObj))
console.timeEnd('deepCopyObj');
下面來(lái)總結(jié)一下實(shí)現(xiàn)深復(fù)制的的基本思路:
1.檢測(cè)當(dāng)前屬性是否為對(duì)象
2.因?yàn)閿?shù)組是特殊的對(duì)象,所以,在屬性為對(duì)象的前提下還需要檢測(cè)它是否為數(shù)組。
3.如果是數(shù)組,則創(chuàng)建一個(gè)[]空數(shù)組,否則,創(chuàng)建一個(gè){}空對(duì)象,并賦值給子對(duì)象的當(dāng)前屬性。然后,遞歸調(diào)用extendDeep函數(shù)。
上面例子使我們自己使用遞歸算法實(shí)現(xiàn)的一種深度復(fù)制方法。事實(shí)上,ES5新增的JSON對(duì)象提供的兩個(gè)方法也可以實(shí)現(xiàn)深度復(fù)制,
分別是JSON.stringify()和JSON.parse();前者用來(lái)將對(duì)象轉(zhuǎn)成字符串,后者則把字符串轉(zhuǎn)換成對(duì)象。下面我們使用該方法來(lái)實(shí)現(xiàn)一個(gè)深度復(fù)制的函數(shù):
根據(jù)不包含引用對(duì)象的普通數(shù)組深拷貝得到啟發(fā),不拷貝引用對(duì)象,拷貝一個(gè)字符串會(huì)新辟一個(gè)新的存儲(chǔ)地址,這樣就切斷了引用對(duì)象的指針聯(lián)系。
console.time('JSON');
var newObj1 = JSON.parse(JSON.stringify(obj))
console.log(JSON.stringify(newObj1))
console.timeEnd('JSON');
測(cè)試發(fā)現(xiàn),它也實(shí)現(xiàn)了深度復(fù)制。一般推薦使用后面這種方法,因?yàn)镴SON.parse和JSON.stringify是內(nèi)置函數(shù),處理起來(lái)會(huì)比較快。
另外,前面的那種方法使用了遞歸調(diào)用,我們都知道,遞歸是效率比較低的一種算法。
newObj.name = 'xx';
newObj.age.push('3');
newObj.ojbs.value = 3;
newObj.ojbs.code = '10001';
newObj.arrs.push({value: 5,text: '河北' });
newObj1.arrs.push({value: 5,text: '河北' });
newObj1.arrs.push({value: 6,text: '北京' });
console.log(JSON.stringify(obj))
console.log(JSON.stringify(newObj))
console.log(JSON.stringify(newObj1))
上面的方法完全能夠Object、Array、Date、String、Number或者Boolean類型這6個(gè)簡(jiǎn)單類型的深拷貝,只要對(duì)象和數(shù)組中的數(shù)據(jù)形成一個(gè)樹(shù)狀結(jié)構(gòu),也就是說(shuō),在1個(gè)對(duì)象中沒(méi)有多于1個(gè)的對(duì)相同數(shù)據(jù)的引用。例如:
// 這是可以克隆的
var tree = {
"left": { "left": null, "right": null, "data": 3 },
"right": null,
"data": 8
};
// 這樣也可以工作,但是你會(huì)得到2份內(nèi)部節(jié)點(diǎn),而不是2個(gè)引用相同的副本
var directedAcyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
var newdirectedAcyclicGraph = clone(directedAcyclicGraph)
newdirectedAcyclicGraph.data = 9;
console.log(JSON.stringify(directedAcyclicGraph))
console.log(JSON.stringify(newdirectedAcyclicGraph))
// 這種情況因?yàn)闊o(wú)限的遞歸,會(huì)導(dǎo)致堆棧溢出
//"Uncaught RangeError: Maximum call stack size exceeded"
var cylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cylicGraph["right"] = cylicGraph;
var newcylicGraph = deepCopyObj(cylicGraph)
newcylicGraph.data = 9;
console.log(JSON.stringify(cylicGraph))
console.log(JSON.stringify(newcylicGraph))
這個(gè)clone的方法不能處理所有的JS對(duì)象,但已經(jīng)能滿足大部分的需求了,只要你不把所有的工作的丟給它就可以了。
作者:mervynYang
鏈接:http://www.itdecent.cn/p/37f39715c2f8
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。