91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

javaScript自帶的深拷貝

發布時間:2020-07-26 13:15:03 來源:網絡 閱讀:409 作者:可樂程序員 欄目:web開發

通過引用調用(call by reference)

譯注:通過引用調用可能有點繞,你就當做"用引用作為參數調用"。

JavaScript通過引用傳遞所有的東西。如果你不知道這句話的意思,那么看下這個例子:

function?mutate(obj)?{
?obj.a?=?true;
}
const?obj?=?{a:?false};
mutate(obj)
console.log(obj.a);?//?true
復制代碼

函數mutate更改了作為參數傳遞的對象。在"通過值調用(call by value)"的環境中,函數會得到傳遞過來的值,也就是一個拷貝,這個函數使用這個值來一起工作。函數對對象所做的任何更改在該函數之外都不可見。但是在像JavaScript這樣的"通過引用調用"環境中,函數會得到一個——你已經猜到了——引用,并會改變實際對象本身。因此最后的console.log會打印出true。

但是在有些時候,你可能想保持你的原始對象并且為對應的函數創建一個副本。

譯:對于這個還不是很了解的朋友可以看下在下的相關資源

  • You Don't Know Js -- types&grammer -- cp2 values

  • 原始類型和引用類型的區別

淺拷貝:Object.assign()

拷貝一個對象的一種辦法是使用Object.assign(target, sources…)。他接受任意數量的源對象,枚舉他們自己所有的屬性并將這些屬性分配給target。如果我們用一個新的空的對象作為target,我們基本上是在進行拷貝。

const?obj?=?/*?...?*/;
const?copy?=?Object.assign({},?obj);
復制代碼

然而,這是一個拷貝。如果我們的對象包含一個對象,那他們將會保持共享引用,這不是我們想要的結果:

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
復制代碼

另一個潛在的是Object.assign()將getter轉換為一個單純的屬性。

譯:和getter有什么關系?來試試:

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'));
復制代碼

動手試試,是不是變成了單純的屬性?那再動手試試setter唄?

那么現在呢?事實證明,有兩種辦法可以創建對象的拷貝。

注意:有人詢問關于對象拓展運算符。其實對象拓展也是創建了一個淺拷貝。

譯:關于拓展運算符,可以看看在下之前介紹的幾個特性。

JSON.parse

有個最古老的方法去創建對象拷貝的是將對象轉換為字符串表示形式,然后再將他解析回對象的形式。這個感覺有些沉重,但是確實可以有效:

const?obj?=?/*?...?*/;
const?copy?=?JSON.parse(JSON.stringify(obj));
復制代碼

這里有個缺點就是你創建了一個臨時的而且可能會很大的字符串,其目的只是為了轉回到解析器。另一個缺點是這種方法不能處理循環對象。不管你怎么認為,這些都很容易發生。例如,在構建樹狀數據結構時,節點引用其父節點,而父節點又引用其子節點。

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!
復制代碼

此外,像Maps,Sets,RegExps,Dates,ArrayBuffers和其他內置類型這樣的東西在序列化時會丟失。

譯:對JSON.stringify不了解的朋友,可以看看You Don't Know Js: Types & Grammar cp4: coercion JSON Stringification。

Structured Clone

結構化克隆是一種現有的算法,用于將值從一個領域轉移到另一個領域。例如,當你調用postMessage將消息發送到另一個窗口(window)或WebWorker時,就會使用此方法。結構化克隆的好處是它可以處理循環對象并支持大量的內置類型。問題在于,在編寫本文時,算法不會直接暴露,只能作為其他API的一部分。 所以我們必須先看看那些API,不是嗎。。。

MessageChannel

正如我所說,無論何時調用postMessage,都會使用結構化克隆算法(譯:在針對對象的時候,是這種情況)。我們可以創建一個MessageChannel并發送一條消息。在接收端,消息包含原始數據對象的結構克隆。

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);
復制代碼

這種方法的缺點是它是異步的。這也沒什么大不了的,但有時你需要以一種同步方式來深度拷貝對象。

History API

如果你曾經使用history.pushState()來構建SPA,那么你就會知道可以提供一個state對象來保存URL。事實證明,這個state對象在結構的克隆上是同步進行的。我們必須小心不要打亂任何可能使用state對象的程序邏輯,所以我們需要在完成克隆后恢復原始state。要防止觸發任何事件,請使用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);
復制代碼

再一次,只是為了復制一個對象而進入瀏覽器的引擎感覺有點重,但是你必須做你必須做的事情。此外,Safari將replaceState的調用數量限制在30秒內100次。

Notification API

Twitter上發了一個推文之后,Jeremy Banks向我展示了第三種方式利用結構化克隆:

function?structuralClone(obj)?{
?return?new?Notification('',?{data:?obj,?silent:?true}).data;
}
const?obj?=?/*?...?*/;
const?clone?=?structuralClone(obj);
復制代碼

短小,簡潔。我喜歡他。不過,它基本上是需要在瀏覽器中啟動權限機制,所以我懷疑它非常慢。由于某種原因,Safari始終為數據對象返回undefined。

性能特征

我想衡量這些方法中哪一種是性能最好的。在我的第一次(天真的)嘗試中,我使用了一個小JSON對象,并通過這些不同的方法將其拷貝一千次。幸運的是,Mathias Bynens告訴我V8有一個緩存,用于在對象中添加屬性。所以這基本是沒啥參考性了。為了確保我沒有命中緩存,我編寫了一個函數,它使用隨機命名生成給定深度和寬度的對象,并重新運行測試。

譯: 對于v8有一個緩存,也可以看看在下之前的一個文章外形和內聯緩存

圖解!

以下是Chrome,Firefox和Edge中不同技術的表現。 越低越好。

javaScript自帶的深拷貝


javaScript自帶的深拷貝


總結

那么我們從本文得到了什么?

  • 如果你不需要循環對象,也不需要保留內置類型,那么你可以使用JSON.parse(JSON.stringify())在所有瀏覽器中獲得最快的克隆,我發現這非常令人驚訝。

  • 如果你想要一個合適的結構化克隆,MessageChannel是你唯一可靠的跨瀏覽器選擇。

如果我們將structuredClone()作為對應平臺上的一個函數會不會更好?我當然這么認為,并重新審視了HTML規范的已存在的一個issue,以重新考慮這種方法。

javaScript自帶的深拷貝



向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

越西县| 崇礼县| 黄浦区| 临武县| 宾阳县| 慈溪市| 积石山| 江华| 凤山县| 渝北区| 资讯| 河西区| 株洲县| 仙游县| 石棉县| 武山县| 三门县| 潍坊市| 旌德县| 平顶山市| 正阳县| 渑池县| 弋阳县| 万盛区| 上林县| 涟水县| 柏乡县| 秀山| 龙川县| 南通市| 垣曲县| 岳阳市| 灌南县| 建水县| 启东市| 叙永县| 阳泉市| 襄城县| 荣昌县| 台中市| 青阳县|