詳解JavaScript對象的深淺復(fù)制
前言
從層次上來看,對象的復(fù)制可以簡單地分為淺復(fù)制和深復(fù)制,顧名思義,淺復(fù)制是指只復(fù)制一層對象的屬性,不會(huì)復(fù)制對象中的對象的屬性,對象的深復(fù)制會(huì)復(fù)制對象中層層嵌套的對象的屬性。
在復(fù)制對象時(shí),除了要復(fù)制對象的屬性外,還要兼顧到是否保留了對象的constructor屬性,是否對每一種數(shù)據(jù)類型(JavaScript常見的數(shù)據(jù)類型有String,Number,Boolean,Data,RegExp,Array,Funtion,Object)都實(shí)現(xiàn)正確的復(fù)制。項(xiàng)目中,我們可以根據(jù)實(shí)際情況,決定需要實(shí)現(xiàn)什么樣程度的復(fù)制。
本文是我在復(fù)制對象方面的一些心得總結(jié),由淺復(fù)制到深復(fù)制,由只復(fù)制簡單屬性到復(fù)制Function,RegExp等復(fù)雜屬性,層層遞進(jìn)。如有陳述不當(dāng)之處,煩請指出,不勝感激。
正文
淺復(fù)制
淺復(fù)制只會(huì)依次復(fù)制對象的每一個(gè)屬性,不會(huì)對這些屬性進(jìn)行遞歸復(fù)制。下面是一個(gè)簡單的淺復(fù)制實(shí)現(xiàn)。
//對象淺復(fù)制
function shadowCopy(obj){
if(typeof obj !== 'object') return obj;
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
仔細(xì)觀察,不難發(fā)現(xiàn)上述方法的缺陷:
1.不能正確實(shí)現(xiàn)數(shù)組的淺復(fù)制
2.復(fù)制操作丟失了對象的constructor屬性
好,我們現(xiàn)在已經(jīng)發(fā)現(xiàn)了問題所在,只需針對性地解決,一個(gè)還算完美的淺復(fù)制對象的方法就誕生了!
//對象淺復(fù)制
function shadowCopy(obj){
if(typeof obj !== 'object') return ;
var newObj;
//保留對象的constructor屬性
if(obj.constructor === Array){
newObj = [];
} else {
newObj = {};
newObj.constructor = obj.constructor;
}
for(var prop in obj){
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
瀏覽器中測試一下:
var arr1 = [0,1,2]; console.log(arr1); console.log(shadowCopy(arr1)); var arr2 = [0,1,2,[3,4,5]], arr2Copy = shadowCopy(arr2); console.log(arr2); console.log(arr2Copy); arr2Copy[3][0] = 6; console.log(arr2[3][0]); //6

Good! 可以正確實(shí)現(xiàn)數(shù)組復(fù)制和并且保留constructor了,但細(xì)心的你一定發(fā)現(xiàn)了,淺復(fù)制后的對象的 arr2Copy[3] 和 arr2[3] 指向的是一個(gè)對象,改變其中一個(gè),同時(shí)也會(huì)改變另一個(gè)。我們想要實(shí)現(xiàn)的是 復(fù)制,但這并不是復(fù)制呀!
這是淺復(fù)制的一個(gè)弊端所在,接下讓我們看看深復(fù)制是怎樣解決這個(gè)問題的。
深復(fù)制
深復(fù)制需要層層遞歸,復(fù)制對象的所有屬性,包括對象屬性的屬性的屬性....(暈~)
如果只是需要簡單地復(fù)制對象的屬性,而不用考慮它的constructor,也不用考慮函數(shù),正則,Data等特殊數(shù)據(jù)類型,那這里有一個(gè)深復(fù)制的小trick,兩行代碼即可:
function deepCopy(obj){
if(typeof obj !== "object"){ return ;}
var str = JSON.stringify(obj);
return JSON.parse(str);
}
大多數(shù)情況下,上面的就可以滿足要求了,但一些時(shí)候,我們需要把函數(shù),正則等特殊數(shù)據(jù)類型也考慮在內(nèi),或者當(dāng)前環(huán)境不支持JSON時(shí),上面的方法也就不適用了。這時(shí),我們可以通過遞歸來實(shí)現(xiàn)對象的深層復(fù)制,如下:
function deepCopy(obj){
if(typeof obj !== "object"){ return ;}
var newObj;
//保留對象的constructor屬性
if(obj.constructor === Array){
newObj = [];
} else {
newObj = {};
newObj.constructor = obj.constructor;
}
for(var prop in obj){
if(typeof obj[prop] === 'object'){
if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
newObj[prop] = obj[prop];
} else {
//遞歸
newObj[prop] = deepCopy(obj[prop]);
}
} else {
newObj[prop] = obj[prop];
}
}
return newObj;
}
先用上面的例子測試:

棒!可以正確實(shí)現(xiàn)多維數(shù)組的復(fù)制,再看是否能實(shí)現(xiàn)函數(shù)和正則的復(fù)制:
function Person(name){
this.name = name;
this.age = age;
this.search = new RegExp(name);
this.say = function(){
console.log(this.name + "今年" + this.age + "歲了");
}
}
var p1 = new Person("Claiyre",20),
p2 = deepCopy(p1);
console.log(p1);
console.log(p2);
p2.age = 22;
p1.say();
p2.say();

圓滿完成?。?/p>
稍加整理,我們就可以得到一個(gè)較為通用的js對象復(fù)制函數(shù):
function deepCopy(obj){
var newObj = obj.constructor === Array ? []:{};
newObj.constructor = obj.constructor;
if(typeof obj !== "object"){
return ;
} else if(window.JSON){
//若需要考慮特殊的數(shù)據(jù)類型,如正則,函數(shù)等,需把這個(gè)else if去掉即可
newObj = JSON.parse(JSON.stringify(obj));
} else {
for(var prop in obj){
if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
newObj[prop] = obj[prop];
} else if(typeof obj[prop] === 'object'){
//遞歸
newObj[prop] = deepCopy(obj[prop]);
} else {
newObj[prop] = obj[prop];
}
}
}
return newObj;
}
結(jié)語
面向?qū)ο蟮木幊陶Z言,其核心是對象,因此深入了解對象的相關(guān)操作,縱向比較異同,對學(xué)習(xí)過程是極有好處的。
以上所述是小編給大家介紹的JavaScript對象的深淺復(fù)制,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
js屏蔽鼠標(biāo)鍵盤(右鍵/Ctrl+N/Shift+F10/F11/F5刷新/退格鍵)
屏蔽鼠標(biāo)右鍵、Ctrl+N、Shift+F10、F11、F5刷新、退格鍵/Alt+ 方向鍵 →等等,太多了就不一一寫來了感興趣的朋友可以了解下啊,希望本文對你有所幫助2013-01-01
JavaScript讀寫二進(jìn)制數(shù)據(jù)的方法詳解
avascript里有兩個(gè)內(nèi)置對象,一個(gè)是ArrayBuffer;一個(gè)是DataView,讀寫二進(jìn)制數(shù)據(jù)都需要使用這兩個(gè)對象。這篇文章主要給大家介紹了關(guān)于JavaScript讀寫二進(jìn)制數(shù)據(jù)的方法,需要的朋友可以參考下2018-09-09
JS實(shí)現(xiàn)的點(diǎn)擊表頭排序功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)的點(diǎn)擊表頭排序功能,可實(shí)現(xiàn)針對表格中的字母、數(shù)字、日期等格式進(jìn)行排序的功能,涉及javascript針對頁面table元素的獲取及字符串、數(shù)字等排序操作相關(guān)技巧,需要的朋友可以參考下2017-03-03
js隨機(jī)生成字母數(shù)字組合的字符串 隨機(jī)動(dòng)畫數(shù)字
本篇文章給大家分享的js隨機(jī)生成字母數(shù)字組合的字符串,js隨機(jī)生成動(dòng)畫數(shù)字,包括常用的產(chǎn)生隨機(jī)數(shù)的用法,需要的朋友可以參考下2015-09-09

