克履到琛(也就是拷貝)是javascript中很重要也很常見的問題拳昌。克隆就是將一個(gè)對(duì)象里的屬性钠龙、方法等復(fù)制到另一個(gè)對(duì)象中炬藤,且互不影響(即克隆之后,對(duì)一個(gè)對(duì)象進(jìn)行改動(dòng)碴里,不會(huì)影響到另一對(duì)象)沈矿。我們今天就來討論一下原生js中克隆的問題。
action~
現(xiàn)在有一個(gè)對(duì)象
我們現(xiàn)在想把obj里的每一個(gè)屬性拷貝到一個(gè)空對(duì)象var obj1 = {}中咬腋,那么需要寫一個(gè)克隆方法羹膳,首先遍歷一下obj對(duì)象,然后把里面的每一個(gè)屬性都拷貝過去根竿。代碼如下:
在控制臺(tái)中查看obj1陵像,會(huì)發(fā)現(xiàn)實(shí)現(xiàn)了對(duì)obj的拷貝。
我們?cè)賹?duì)這個(gè)方法進(jìn)行一些完善:有可能用戶在執(zhí)行clone方法時(shí)寇壳,只傳origin一個(gè)參數(shù)醒颖,然后將函數(shù)執(zhí)行結(jié)果賦給對(duì)象obj1,所以我們需要在方法最下方加一個(gè)返回值target壳炎,返回克隆結(jié)果图贸,相應(yīng)的也要在函數(shù)體里聲明var target = {}。
還有一種情況obj1對(duì)象里事先有其他屬性,那么函數(shù)體里寫var target = {}顯然是不符合預(yù)期的疏日,所以要完善成var target = target || {};確保萬無一失偿洁。最終該方法完善為:
下面對(duì)obj進(jìn)行拷貝,且obj1本身有自己的屬性沟优,執(zhí)行代碼如下:
查看一下拷貝結(jié)果
完美拷貝了obj的所有屬性涕滋,又保留了自身屬性。
到這就結(jié)束了么挠阁?要知道js有六大數(shù)據(jù)類型(本文不討論ES6新增的Symbol類型):number宾肺,string,boolean侵俗,undefined锨用,null,object隘谣。
其中number增拥,string,boolean寻歧,undefined掌栅,null歸為原始值一類,而object屬于引用值码泛,具體包括狹義的object猾封,array,function噪珊。
下面往obj對(duì)象里加點(diǎn)引用值晌缘,用我們已經(jīng)寫出來的克隆方法試試:
貌似拷貝成功了
當(dāng)我們對(duì)obj對(duì)象進(jìn)行一波這樣的操作:
再查看obj1會(huì)發(fā)現(xiàn)
在obj1里也多了美容卡和一個(gè)兒子,函數(shù)沒受影響痢站。
我們可以得出這樣的結(jié)論:對(duì)于數(shù)組和對(duì)象枚钓,用上面那種克隆方法是不行的。因?yàn)閷?duì)于數(shù)組和對(duì)象而言瑟押,拷貝的是地址搀捷,他們指向的都是同一個(gè)空間,通過一個(gè)對(duì)象在這個(gè)空間里面加了東西多望,另一個(gè)對(duì)象必然也會(huì)受到影響嫩舟。
而對(duì)于函數(shù)而言,通過上面這種普通的賦值拷貝怀偷,就可以實(shí)現(xiàn)家厌,且互不影響,因?yàn)楹瘮?shù)的克隆會(huì)在內(nèi)存中單獨(dú)開辟一塊空間椎工。
我們管上面寫的這種克隆方法叫淺度克隆饭于,它可應(yīng)用于不包含對(duì)象(狹義的)和數(shù)組的對(duì)象之間的拷貝蜀踏。有點(diǎn)繞哈~
下面我們來解決一下狹義的對(duì)象和數(shù)組的拷貝問題,即我們需要另一種萬全之策——深度克隆掰吕。
先來整理下思路:
1.遍歷待拷貝的對(duì)象果覆;
2.判斷每個(gè)元素是不是原始值,若是殖熟,則通過淺度克隆的手段進(jìn)行拷貝局待;
3.若是引用值,則需要繼續(xù)判斷是對(duì)象還是數(shù)組菱属;
4.再分別建立空數(shù)組或?qū)ο笥脕硎⒎爬锩婕磳⒖截惗鴣淼膶傩灾担?/p>
5.數(shù)組和對(duì)象里面的若是原始值钳榨,則淺拷貝即可實(shí)現(xiàn),若還有引用值纽门,則還需要重復(fù)進(jìn)行上述一系列的判斷薛耻。
上述每一步思路怎么用代碼實(shí)現(xiàn)呢:
1.使用for in進(jìn)行遍歷。但需要注意的是赏陵,for in方法會(huì)把對(duì)象原型里的屬性也一起遍歷了饼齿,所以要與hasOwnProperty()方法進(jìn)行聯(lián)用,hasOwnProperty()方法可以判斷某屬性是不是該對(duì)象自己的屬性瘟滨,從而過濾掉原型中的屬性。
2.用typeof()返回值來進(jìn)行判斷能颁,數(shù)組和對(duì)象的typeof返回值是'object'杂瘸。
3.判斷對(duì)象還是數(shù)組有多種方法,舉出常見的三種:分別是constructor伙菊、instanceof和toString()方法败玉。這里最好用toString()方法,因?yàn)樵谟懈缸佑蛑g拷貝的情況镜硕,constructor和instanceof這兩種是不好用的运翼。
4.就是[]和{}唄。
5.重復(fù)判斷兴枯,自然想到遞歸血淌。
深度克隆方法如下:
注意到origin[prop] !== null這句了么?為啥要加上它财剖,因?yàn)閠ypeof(null)也是'object'悠夯。
來試試吧,我們把obj變得復(fù)雜一點(diǎn)躺坟,給隔壁老王的老婆增加倆兒子:王小寶和王二寶沦补。執(zhí)行過程如下:
看看結(jié)果,沒毛病咪橙。
對(duì)obj的引用值增加一些屬性試試夕膀。
發(fā)現(xiàn)obj1紋絲不動(dòng)虚倒,完美拷貝實(shí)現(xiàn)!
綜上产舞,克隆方法白話完了魂奥,實(shí)戰(zhàn)開發(fā)中針對(duì)實(shí)際需要采取不同的克隆手段。有不足之處歡迎在評(píng)論區(qū)進(jìn)行指正庞瘸,感激不盡~向大神們學(xué)習(xí)捧弃!