通過引用調(diào)用(call by reference)
公司主營業(yè)務(wù):網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出市中免費(fèi)做網(wǎng)站回饋大家。
譯注:通過引用調(diào)用可能有點(diǎn)繞,你就當(dāng)做"用引用作為參數(shù)調(diào)用"。
JavaScript通過引用傳遞所有的東西。如果你不知道這句話的意思,那么看下這個(gè)例子:
function?mutate(obj)?{ ?obj.a?=?true; } const?obj?=?{a:?false}; mutate(obj) console.log(obj.a);?//?true 復(fù)制代碼
函數(shù)mutate更改了作為參數(shù)傳遞的對象。在"通過值調(diào)用(call by value)"的環(huán)境中,函數(shù)會得到傳遞過來的值,也就是一個(gè)拷貝,這個(gè)函數(shù)使用這個(gè)值來一起工作。函數(shù)對對象所做的任何更改在該函數(shù)之外都不可見。但是在像JavaScript這樣的"通過引用調(diào)用"環(huán)境中,函數(shù)會得到一個(gè)——你已經(jīng)猜到了——引用,并會改變實(shí)際對象本身。因此最后的console.log會打印出true。
但是在有些時(shí)候,你可能想保持你的原始對象并且為對應(yīng)的函數(shù)創(chuàng)建一個(gè)副本。
譯:對于這個(gè)還不是很了解的朋友可以看下在下的相關(guān)資源
You Don't Know Js -- types&grammer -- cp2 values
原始類型和引用類型的區(qū)別
淺拷貝:Object.assign()
拷貝一個(gè)對象的一種辦法是使用Object.assign(target, sources…)。他接受任意數(shù)量的源對象,枚舉他們自己所有的屬性并將這些屬性分配給target。如果我們用一個(gè)新的空的對象作為target,我們基本上是在進(jìn)行拷貝。
const?obj?=?/*?...?*/; const?copy?=?Object.assign({},?obj); 復(fù)制代碼
然而,這是一個(gè)淺拷貝。如果我們的對象包含一個(gè)對象,那他們將會保持共享引用,這不是我們想要的結(jié)果:
function?mutateDeepObject(obj)?{ ?obj.a.thing?=?true; } const?obj?=?{a:?{thing:?false}}; const?copy?=?Object.assign({},?obj); mutateDeepObject(copy) console.log(obj.a.thing);?//?true 復(fù)制代碼
另一個(gè)潛在的是Object.assign()將getter轉(zhuǎn)換為一個(gè)單純的屬性。
譯:和getter有什么關(guān)系?來試試:
var?myObject?=?{ get?say()?{ return?'Hi,?xiaohesong'; } }; console.log('before?assign',?Object.getOwnPropertyDescriptor(myObject,?'say')) var?obj?=?Object.assign({},?myObject) console.log('after?assign',?Object.getOwnPropertyDescriptor(obj,?'say')); 復(fù)制代碼
動(dòng)手試試,是不是變成了單純的屬性?那再動(dòng)手試試setter唄?
那么現(xiàn)在呢?事實(shí)證明,有兩種辦法可以創(chuàng)建對象的深拷貝。
注意:有人詢問關(guān)于對象拓展運(yùn)算符。其實(shí)對象拓展也是創(chuàng)建了一個(gè)淺拷貝。
譯:關(guān)于拓展運(yùn)算符,可以看看在下之前介紹的幾個(gè)特性。
JSON.parse
有個(gè)最古老的方法去創(chuàng)建對象拷貝的是將對象轉(zhuǎn)換為字符串表示形式,然后再將他解析回對象的形式。這個(gè)感覺有些沉重,但是確實(shí)可以有效:
const?obj?=?/*?...?*/; const?copy?=?JSON.parse(JSON.stringify(obj)); 復(fù)制代碼
這里有個(gè)缺點(diǎn)就是你創(chuàng)建了一個(gè)臨時(shí)的而且可能會很大的字符串,其目的只是為了轉(zhuǎn)回到解析器。另一個(gè)缺點(diǎn)是這種方法不能處理循環(huán)對象。不管你怎么認(rèn)為,這些都很容易發(fā)生。例如,在構(gòu)建樹狀數(shù)據(jù)結(jié)構(gòu)時(shí),節(jié)點(diǎn)引用其父節(jié)點(diǎn),而父節(jié)點(diǎn)又引用其子節(jié)點(diǎn)。
const?x?=?{}; const?y?=?{x}; x.y?=?y;?//?Cycle:?x.y.x.y.x.y.x.y.x... const?copy?=?JSON.parse(JSON.stringify(x));?//?throws! 復(fù)制代碼
此外,像Maps,Sets,RegExps,Dates,ArrayBuffers和其他內(nèi)置類型這樣的東西在序列化時(shí)會丟失。
譯:對JSON.stringify不了解的朋友,可以看看You Don't Know Js: Types & Grammar cp4: coercion JSON Stringification。
Structured Clone
結(jié)構(gòu)化克隆是一種現(xiàn)有的算法,用于將值從一個(gè)領(lǐng)域轉(zhuǎn)移到另一個(gè)領(lǐng)域。例如,當(dāng)你調(diào)用postMessage將消息發(fā)送到另一個(gè)窗口(window)或WebWorker時(shí),就會使用此方法。結(jié)構(gòu)化克隆的好處是它可以處理循環(huán)對象并支持大量的內(nèi)置類型。問題在于,在編寫本文時(shí),算法不會直接暴露,只能作為其他API的一部分。 所以我們必須先看看那些API,不是嗎。。。
MessageChannel
正如我所說,無論何時(shí)調(diào)用postMessage,都會使用結(jié)構(gòu)化克隆算法(譯:在針對對象的時(shí)候,是這種情況)。我們可以創(chuàng)建一個(gè)MessageChannel并發(fā)送一條消息。在接收端,消息包含原始數(shù)據(jù)對象的結(jié)構(gòu)克隆。
function?structuralClone(obj)?{ ?return?new?Promise(resolve?=>?{ ?const?{port1,?port2}?=?new?MessageChannel(); ?port2.onmessage?=?ev?=>?resolve(ev.data); ?port1.postMessage(obj); ?}); } const?obj?=?/*?...?*/; const?clone?=?await?structuralClone(obj); 復(fù)制代碼
這種方法的缺點(diǎn)是它是異步的。這也沒什么大不了的,但有時(shí)你需要以一種同步方式來深度拷貝對象。
History API
如果你曾經(jīng)使用history.pushState()來構(gòu)建SPA,那么你就會知道可以提供一個(gè)state對象來保存URL。事實(shí)證明,這個(gè)state對象在結(jié)構(gòu)的克隆上是同步進(jìn)行的。我們必須小心不要打亂任何可能使用state對象的程序邏輯,所以我們需要在完成克隆后恢復(fù)原始state。要防止觸發(fā)任何事件,請使用history.replaceState()而不是history.pushState()。
function?structuralClone(obj)?{ ?const?oldState?=?history.state; ?history.replaceState(obj,?document.title); ?const?copy?=?history.state; ?history.replaceState(oldState,?document.title); ?return?copy; } const?obj?=?/*?...?*/; const?clone?=?structuralClone(obj); 復(fù)制代碼
再一次,只是為了復(fù)制一個(gè)對象而進(jìn)入瀏覽器的引擎感覺有點(diǎn)重,但是你必須做你必須做的事情。此外,Safari將replaceState的調(diào)用數(shù)量限制在30秒內(nèi)100次。
Notification API
Twitter上發(fā)了一個(gè)推文之后,Jeremy Banks向我展示了第三種方式利用結(jié)構(gòu)化克隆:
function?structuralClone(obj)?{ ?return?new?Notification('',?{data:?obj,?silent:?true}).data; } const?obj?=?/*?...?*/; const?clone?=?structuralClone(obj); 復(fù)制代碼
短小,簡潔。我喜歡他。不過,它基本上是需要在瀏覽器中啟動(dòng)權(quán)限機(jī)制,所以我懷疑它非常慢。由于某種原因,Safari始終為數(shù)據(jù)對象返回undefined。
性能特征
我想衡量這些方法中哪一種是性能最好的。在我的第一次(天真的)嘗試中,我使用了一個(gè)小JSON對象,并通過這些不同的方法將其拷貝一千次。幸運(yùn)的是,Mathias Bynens告訴我V8有一個(gè)緩存,用于在對象中添加屬性。所以這基本是沒啥參考性了。為了確保我沒有命中緩存,我編寫了一個(gè)函數(shù),它使用隨機(jī)命名生成給定深度和寬度的對象,并重新運(yùn)行測試。
譯: 對于v8有一個(gè)緩存,也可以看看在下之前的一個(gè)文章外形和內(nèi)聯(lián)緩存
圖解!
以下是Chrome,F(xiàn)irefox和Edge中不同技術(shù)的表現(xiàn)。 越低越好。
總結(jié)
那么我們從本文得到了什么?
如果你不需要循環(huán)對象,也不需要保留內(nèi)置類型,那么你可以使用JSON.parse(JSON.stringify())在所有瀏覽器中獲得最快的克隆,我發(fā)現(xiàn)這非常令人驚訝。
如果你想要一個(gè)合適的結(jié)構(gòu)化克隆,MessageChannel是你唯一可靠的跨瀏覽器選擇。
如果我們將structuredClone()作為對應(yīng)平臺上的一個(gè)函數(shù)會不會更好?我當(dāng)然這么認(rèn)為,并重新審視了HTML規(guī)范的已存在的一個(gè)issue,以重新考慮這種方法。
網(wǎng)頁標(biāo)題:javaScript自帶的深拷貝
文章起源:http://jinyejixie.com/article14/gggode.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、響應(yīng)式網(wǎng)站、網(wǎng)頁設(shè)計(jì)公司、定制網(wǎng)站、網(wǎng)站維護(hù)、服務(wù)器托管
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)