JavaScript數(shù)組與對(duì)象拷貝

在我不長(zhǎng)的開(kāi)發(fā)經(jīng)歷中毫痕,常常在js中遇到數(shù)組或?qū)ο罂截惖那闆r数尿,總是不能寫對(duì)赦肃。 于是決定好好梳理一遍相關(guān)的知識(shí)适室。

預(yù)備知識(shí): 引用類型

ECMAScript 變量可能包含兩種不同數(shù)據(jù)類型的值: 基本類型值和引用類型值 勿负。

當(dāng)從一個(gè)變量向另一個(gè)變量復(fù)制引用類型的值時(shí)馏艾,實(shí)際復(fù)制的是指針,指向存儲(chǔ)在堆中的同一個(gè)對(duì)象奴愉。因此改變其中一個(gè)變量琅摩,就會(huì)影響另一個(gè)變量。

——《JavaScript高級(jí)程序設(shè)計(jì)(第三版)》

數(shù)組Array和對(duì)象Object同屬于引用類型锭硼,因此在拷貝時(shí)需要額外的工作房资,而不能簡(jiǎn)單的按照 c=a , a=b, b=c的方式來(lái)賦值。

數(shù)組拷貝

數(shù)組的淺拷貝就是直接賦值

var a = [1,2,3];
var b = a; // b = [1,2,3]
b[0] = 10; // b = [10,2,3], a = [10,2,3]

引用類型傳遞的是地址檀头,因此修改數(shù)組b后數(shù)組a也會(huì)跟著改變轰异。

  • 改進(jìn)辦法1 Array.splice( ) 方法

    數(shù)組類型的splice( )方法本來(lái)時(shí)用于切割數(shù)組的,接收兩個(gè)參數(shù)暑始,起點(diǎn)與終點(diǎn)索引值搭独,將這個(gè)區(qū)間內(nèi)的數(shù)組元素<b>淺拷貝</b>到一個(gè)新數(shù)組并返回,原數(shù)組保持不變蒋荚。 若不傳遞任何參數(shù)給splice( )方法戳稽,則會(huì)默認(rèn)<b>淺拷貝</b>數(shù)組中的每一個(gè)元素到一個(gè)新數(shù)組并返回。由于是復(fù)制的數(shù)組元素而非數(shù)組變量本身期升,因此傳遞的是數(shù)組中每一個(gè)元素的值或地址惊奇。

    var a = [1,2,3];
    var b = a.splic(); //b = [1,2,3]
    b[0] = 10;         //b = [10,2,3], a = [1,2,3]
    

    這個(gè)辦法具有局限性,因?yàn)槠浣K究是淺拷貝播赁,如果數(shù)組中的元素含有引用類型颂郎,那么改變新數(shù)組中它的值仍會(huì)反應(yīng)到原數(shù)組中:

    var obj = { prop: 1};
    var a = [obj,1,2,3]; // a = [{prop:1},1,2,3]
    var b = a.splice();  // b = [{prop:1},1,2,3]
    b[0].prop = 20;
    b[1] = 10;           // b = [{prop:20},10,2,3]
                         // a = [{prop:20},1,2,3]
    

    另外,當(dāng)被拷貝數(shù)組中包含數(shù)組元素時(shí)(類似二維數(shù)組)容为,使用該方法復(fù)制將返回空數(shù)組乓序。

    ?

  • 改進(jìn)辦法2 Array . concat( ) 方法

    數(shù)組類型的concat( ) 方法主要用于連接多個(gè)數(shù)組返回一個(gè)新數(shù)組,若不帶參數(shù)則返回一個(gè)原數(shù)組的拷貝坎背,遺憾的是這個(gè)拷貝仍為淺拷貝替劈。與splice( ) 方法表現(xiàn)稍不同的是,若被復(fù)制數(shù)組中包含數(shù)組元素得滤,它仍將正常工作陨献,返回淺拷貝。

    var a = [1,2,3];
    var b = [4,5];
    var c = [a,b];      //c = [[1,2,3],[4,5]];
    var d = c.concat(); //d = [[1,2,3],[4,5]];
    var e = c.splice(); //e = [];
    d[1][1] = 10;       //d = [[1,2,3],[4,10]];
                        //c = [[1,2,3],[4,10]]
    

對(duì)象拷貝

直接賦值仍然是淺拷貝懂更,要做到對(duì)象的深拷貝眨业,需要將屬性一一拷貝到新對(duì)象上:

var a = {prop1:1, prop2:2, prop3:3};
//聲明一個(gè)函數(shù)執(zhí)行深拷貝
function deepCopy(obj){      //接收待拷貝的對(duì)象最為參數(shù)
  var copy = {};             //聲明新副本對(duì)象
  for(prop in obj){          // for in語(yǔ)句遍歷對(duì)象屬性
    copy[prop] = obj[prop]   // for in 語(yǔ)句中遍歷結(jié)果為屬性的字符串名急膀,因此需以[]方式訪問(wèn)
  }
  return copy;               //返回新對(duì)象
}

var b = deepCopy(a);         // b = {prop1:1,prop2:2,prop3:3}
b.prop1 = 10;                // b = {prop1:10,prop2:2,prop3:3}
                             // a = {prop1:1, prop2:2,prop3:3}

上述這總解決辦法只能拷貝所有屬性均為基本類型值的對(duì)象,如果拷貝的對(duì)象其屬性中包含了Object或Array等引用類型龄捡,仍然會(huì)出現(xiàn)指向同一個(gè)地址的情況(雖然對(duì)象本身沒(méi)有指向同一個(gè)地址了卓嫂,但是這些引用類型屬性仍然指向了同一個(gè)地址)∑钢常考慮到對(duì)象屬性層層嵌套的情況并不少見(jiàn)晨雳,因此deepCopy( )函數(shù)仍然需要優(yōu)化:

function deepCopy(obj) {
  let copy = {};
  for (prop in obj) {
    if (obj[prop] instanceof Object) {  //先判斷該屬性是否也是一個(gè)Object對(duì)象
      copy[prop] = deepCopy(obj[prop]); //若為對(duì)象則對(duì)該屬性再遞歸調(diào)用deepCopy()賦值給新對(duì)象的該屬性
    } else {
      copy[prop] = obj[prop]            //若該屬性不為Object類型,則直接賦值即可
    }
  }
  return copy;        //返回拷貝出的新對(duì)象
}

到這里就斤,上述這些方法函數(shù)已經(jīng)完全能夠解決我遇到的所有情景了悍募。但其實(shí)這個(gè)問(wèn)題還可以推得很深推得很細(xì),下面就來(lái)做點(diǎn)"無(wú)用功", 再往前邁進(jìn)一步洋机。

混合拷貝

如前所述,數(shù)組的拷貝方法在數(shù)組元素為Object或者Array類型時(shí)仍然有無(wú)法做到副本與原變量獨(dú)立洋魂。 再加上JavaScript “獨(dú)特”的語(yǔ)言特性绷旗,數(shù)組與對(duì)象可以做到 “你中有我,我中有你” 的混合狀態(tài)副砍,因此可以再次改進(jìn)deepCopy( ) 衔肢,讓它能實(shí)現(xiàn)混合情景下的深拷貝。

function deepCopy(origin){
        let copy = null;   //聲明副本
        if(origin.constructor === Array){   //確定副本的類型
            copy = [];
        }else if(origin.constructor === Object){
            copy = {};
        }
        for(i in origin){               //遍歷原變量的枚舉屬性
            if(origin[i] instanceof Object){   //Array和Object類型其原型鏈上均含Object構(gòu)造函數(shù)
                copy[i] = deepCopy(origin[i])  
            }else{
                copy[i] = origin[i];
            }
        } 
        return copy;
    }

現(xiàn)在豁翎,deepCopy( ) 可以輕松應(yīng)對(duì)任何包含 數(shù)組或者對(duì)象的深拷貝了角骤。

雖然 MDN不推薦用for in遍歷數(shù)組,因?yàn)槠浞祷氐氖撬饕龜?shù)字的字符串值心剥,并且在不同的ECMA-262標(biāo)準(zhǔn)中返回的屬性或索引值順序是不同的邦尊,不一定按照對(duì)象構(gòu)造時(shí)的屬性順序或數(shù)組索引值的大小順序。但是這里拷貝數(shù)組則不用擔(dān)心优烧,雖然不按順序返回索引值蝉揍,但最終還是遍歷了整個(gè)數(shù)組。 這樣對(duì)象分支和數(shù)組分支就合并在了同一個(gè)for in語(yǔ)句中畦娄,代碼變得更為簡(jiǎn)潔又沾。


參考網(wǎng)站: MDN-JavaScript

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市熙卡,隨后出現(xiàn)的幾起案子杖刷,更是在濱河造成了極大的恐慌,老刑警劉巖驳癌,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滑燃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喂柒,警方通過(guò)查閱死者的電腦和手機(jī)不瓶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門禾嫉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蚊丐,你說(shuō)我怎么就攤上這事熙参》驯洌” “怎么了玩祟?”我有些...
    開(kāi)封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)暴凑。 經(jīng)常有香客問(wèn)我凛篙,道長(zhǎng)黍匾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任呛梆,我火速辦了婚禮锐涯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘填物。我一直安慰自己纹腌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布滞磺。 她就那樣靜靜地躺著升薯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪击困。 梳的紋絲不亂的頭發(fā)上涎劈,一...
    開(kāi)封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音阅茶,去河邊找鬼蛛枚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛目派,可吹牛的內(nèi)容都是我干的坤候。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼企蹭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼白筹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起谅摄,我...
    開(kāi)封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤徒河,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后送漠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽照,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了代兵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尼酿。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖植影,靈堂內(nèi)的尸體忽然破棺而出裳擎,到底是詐尸還是另有隱情,我是刑警寧澤思币,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布鹿响,位于F島的核電站,受9級(jí)特大地震影響谷饿,放射性物質(zhì)發(fā)生泄漏惶我。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一博投、第九天 我趴在偏房一處隱蔽的房頂上張望绸贡。 院中可真熱鬧,春花似錦毅哗、人聲如沸恃轩。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至松忍,卻和暖如春蒸殿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸣峭。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工宏所, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人摊溶。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓爬骤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親莫换。 傳聞我的和親對(duì)象是個(gè)殘疾皇子霞玄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 《ijs》速成開(kāi)發(fā)手冊(cè)3.0 官方用戶交流:iApp開(kāi)發(fā)交流(1) 239547050iApp開(kāi)發(fā)交流(2) 10...
    葉染柒丶閱讀 5,086評(píng)論 0 7
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile麗語(yǔ)閱讀 3,828評(píng)論 0 6
  • 第5章 引用類型(返回首頁(yè)) 本章內(nèi)容 使用對(duì)象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,216評(píng)論 0 4
  • 1. 等閑變卻故人心,卻道故人心易變拉岁。 來(lái)年陌生的坷剧,是昨日最親的某某某。 2. 記得以前臨近畢業(yè)的時(shí)候喊暖,班里總是會(huì)...
    我在和誰(shuí)說(shuō)晚安閱讀 539評(píng)論 1 2
  • 北大新任校長(zhǎng)王恩哥甫上任惫企,便向?qū)W生提出十句話,在全校引起熱議陵叽,有學(xué)生形容是新的校訓(xùn)狞尔。這十句話傳播神速丛版,在不少年輕人...
    瑩火石頭dy閱讀 257評(píng)論 0 2