色综合图-色综合图片-色综合图片二区150p-色综合图区-玖玖国产精品视频-玖玖香蕉视频

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

JavaScript 引擎基礎(chǔ):原型優(yōu)化

瀏覽:67日期:2023-11-10 15:08:32

本文就所有 JavaScript 引擎中常見(jiàn)的一些關(guān)鍵基礎(chǔ)內(nèi)容進(jìn)行了介紹——這不僅僅局限于 V8 引擎。作為一名 JavaScript 開(kāi)發(fā)者,深入了解 JavaScript 引擎是如何工作的將有助于你了解自己所寫(xiě)代碼的性能特征。

在前一篇文章中,我們討論了 JavaScript 引擎是如何通過(guò) Shapes 和 Inline Caches 來(lái)優(yōu)化對(duì)象與數(shù)組的訪問(wèn)的。本文將介紹優(yōu)化流程中的權(quán)衡與取舍,并對(duì)引擎在優(yōu)化原型屬性訪問(wèn)方面的工作進(jìn)行介紹。

原文 JavaScript engine fundamentals: optimizing prototypes ,作者 @Benedikt 和 @Mathias ,譯者 hijiangtao 。以下開(kāi)始正文。

如果你傾向看視頻演講,請(qǐng)移步 YouTube 查看更多。

1. 優(yōu)化層級(jí)與執(zhí)行效率的取舍

前一篇文章介紹了現(xiàn)代 JavaScript 引擎通用的處理流程:

JavaScript 引擎基礎(chǔ):原型優(yōu)化

我們也指出,盡管從高級(jí)抽象層面來(lái)看,引擎之間的處理流程都很相似,但他們?cè)趦?yōu)化流程上通常都存在差異。為什么呢? 為什么有些引擎的優(yōu)化層級(jí)會(huì)比其他引擎更多? 事實(shí)證明,在快速獲取可運(yùn)行的代碼與花費(fèi)更多時(shí)間獲得最優(yōu)運(yùn)行性能的代碼之間存在取舍與平衡。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

解釋器可以快速生成字節(jié)碼,但字節(jié)碼通常效率不高。 相比之下,優(yōu)化編譯器雖然需要更長(zhǎng)的時(shí)間,但最終會(huì)產(chǎn)生更高效的機(jī)器碼。

這正是 V8 在使用的模型。V8 的解釋器叫 Ignition,(就原始字節(jié)碼執(zhí)行速度而言)它是所有引擎中最快的解釋器。V8 的優(yōu)化編譯器名為 TurboFan,它最終會(huì)生成高度優(yōu)化的機(jī)器碼。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

啟動(dòng)延遲與執(zhí)行速度之間的這些權(quán)衡便是一些 JavaScript 引擎決定是否在流程中加入優(yōu)化層的原因。例如,SpiderMonkey 在解釋器和完整的 IonMonkey 優(yōu)化編譯器之間添加了一個(gè) Baseline 層:

JavaScript 引擎基礎(chǔ):原型優(yōu)化

解釋器可以快速生成字節(jié)碼,但字節(jié)碼執(zhí)行起來(lái)相對(duì)較慢。Baseline 生成代碼需要花費(fèi)更長(zhǎng)的時(shí)間,但能提供更好的運(yùn)行時(shí)性能。最后,IonMonkey 優(yōu)化編譯器花費(fèi)最長(zhǎng)時(shí)間來(lái)生成機(jī)器碼,但該代碼運(yùn)行起來(lái)非常高效。

讓我們通過(guò)一個(gè)具體的例子,看看不同引擎中的優(yōu)化流程都有哪些差異。這是一些在循環(huán)中會(huì)經(jīng)常重復(fù)的代碼。

let result = 0;for (let i = 0; i < 4242424242; ++i) {result += i;}console.log(result);

V8開(kāi)始在 Ignition 解釋器中運(yùn)行字節(jié)碼。從某些方面來(lái)看,代碼是否足夠 hot 由引擎決定,引擎話(huà)負(fù)責(zé)調(diào)度 TurboFan 前端,它是 TurboFan 中負(fù)責(zé)處理集成分析數(shù)據(jù)和構(gòu)建代碼在機(jī)器層面表示的一部分。這部分結(jié)果之后會(huì)被發(fā)送到另一個(gè)線程上的 TurboFan 優(yōu)化器被進(jìn)一步優(yōu)化。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

當(dāng)優(yōu)化器運(yùn)行時(shí),V8 會(huì)繼續(xù)在 Ignition 中執(zhí)行字節(jié)碼。 當(dāng)優(yōu)化器處理完成后,我們獲得可執(zhí)行的機(jī)器碼,執(zhí)行流程便會(huì)繼續(xù)下去。

SpiderMonkey 引擎也開(kāi)始在解釋器中運(yùn)行字節(jié)碼。但它有一個(gè)額外的 Baseline 層,這意味著比較 hot 的代碼會(huì)首先被發(fā)送到 Baseline。 Baseline 編譯器在主線程上生成 Baseline 代碼,并在完成后繼續(xù)后面的執(zhí)行。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

如果 Baseline 代碼運(yùn)行了一段時(shí)間,SpiderMonkey 最終會(huì)激活 IonMonkey 前端,并啟動(dòng)優(yōu)化器 - 這與 V8 非常相似。當(dāng) IonMonkey 進(jìn)行優(yōu)化時(shí),代碼在 Baseline 中會(huì)一直運(yùn)行。當(dāng)優(yōu)化器處理完成后,被執(zhí)行的是優(yōu)化后的代碼而不是 Baseline 代碼。

Chakra 的架構(gòu)與 SpiderMonkey 非常相似,但 Chakra 嘗試并行處理更多內(nèi)容以避免阻塞主線程。Chakra 不在主線程上運(yùn)行編譯器,而是將不同編譯器可能需要的字節(jié)碼和分析數(shù)據(jù)復(fù)制出來(lái),并將其發(fā)送到一個(gè)專(zhuān)用的編譯器進(jìn)程。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

當(dāng)代碼準(zhǔn)備就緒,引擎便開(kāi)始運(yùn)行 SimpleJIT 代碼而不是字節(jié)碼。 對(duì)于 FullJIT 來(lái)說(shuō)流程也是同樣如此。這種方法的好處是,與運(yùn)行完整的編譯器(前端)相比,復(fù)制所產(chǎn)生的暫停時(shí)間通常要短得多。但這種方法的缺點(diǎn)是這種 啟發(fā)式復(fù)制 可能會(huì)遺漏某些優(yōu)化所需的某些信息,因此它在一定程度上是用代碼質(zhì)量來(lái)?yè)Q時(shí)間的消耗。

在 JavaScriptCore 中,所有優(yōu)化編譯器都與主 JavaScript 執(zhí)行 完全并發(fā)運(yùn)行 ;根本沒(méi)有復(fù)制階段!相反,主線程僅僅是觸發(fā)了另一個(gè)線程上的編譯作業(yè)。然后,編譯器使用復(fù)雜的加鎖方式從主線程中獲取到要訪問(wèn)的分析數(shù)據(jù)。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

這種方法的優(yōu)點(diǎn)在于它減少了主線程上由 JavaScript 優(yōu)化引起的抖動(dòng)。 缺點(diǎn)是它需要處理復(fù)雜的多線程問(wèn)題并為各種操作付出一些加鎖的成本。

我們已經(jīng)討論過(guò)在使用解釋器快速生成代碼或使用優(yōu)化編譯器生成可高效執(zhí)行代碼之間的一些權(quán)衡。但還有另一個(gè)權(quán)衡: 內(nèi)存使用 !為了說(shuō)明這一點(diǎn),來(lái)看一個(gè)兩個(gè)數(shù)字相加的簡(jiǎn)單 JvaScript 程序。

function add(x, y) {return x + y;}add(1, 2);

這是我們使用 V8 中的 Ignition 解釋器為 add 函數(shù)生成的字節(jié)碼:

StackCheckLdar a1Add a0, [0]Return

不要在意這些字節(jié)碼 - 你真的不需要閱讀它。關(guān)鍵是它只是 四條指令!

當(dāng)代碼變得 hot,TurboFan 便會(huì)生成以下高度優(yōu)化的機(jī)器碼:

leaq rcx,[rip+0x0]movq rcx,[rcx-0x37]testb [rcx+0xf],0x1jnz CompileLazyDeoptimizedCodepush rbpmovq rbp,rsppush rsipush rdicmpq rsp,[r13+0xe88]jna StackOverflowmovq rax,[rbp+0x18]test al,0x1jnz Deoptimizemovq rbx,[rbp+0x10]testb rbx,0x1jnz Deoptimizemovq rdx,rbxshrq rdx, 32movq rcx,raxshrq rcx, 32addl rdx,rcxjo Deoptimizeshlq rdx, 32movq rax,rdxmovq rsp,rbppop rbpret 0x18

這么 一大堆 代碼,尤其是與四條字節(jié)碼相比!通常,字節(jié)碼比機(jī)器碼更緊湊,特別是優(yōu)化過(guò)的機(jī)器碼。但另一方面,字節(jié)碼需要解釋器才能執(zhí)行,而優(yōu)化過(guò)機(jī)器碼可以由處理器直接執(zhí)行。

這就是為什么 JavaScript 引擎不簡(jiǎn)單粗暴”優(yōu)化一切”的主要原因之一。正如我們之前所見(jiàn),生成優(yōu)化的機(jī)器碼也需要很長(zhǎng)時(shí)間,而最重要的是,我們剛剛了解到優(yōu)化的機(jī)器碼也需要更多的內(nèi)存。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

小結(jié):JavaScript 引擎之所以具有不同優(yōu)化層,就在于使用解釋器快速生成代碼或使用優(yōu)化編譯器生成高效代碼之間存在一個(gè)基本權(quán)衡。通過(guò)添加更多優(yōu)化層可以讓你做出更細(xì)粒度的決策,但是以額外的復(fù)雜性和開(kāi)銷(xiāo)為代價(jià)。此外,在優(yōu)化級(jí)別和生成代碼所占用的內(nèi)存之間也存在折衷。這就是為什么 JavaScript 引擎僅嘗試優(yōu)化比較 hot 功能的原因所在。

2. 原型屬性訪問(wèn)優(yōu)化

之前的文章解釋了 JavaScript 引擎如何使用 Shapes 和 Inline Caches 優(yōu)化對(duì)象屬性加載?;仡櫼幌?,引擎將對(duì)象的 Shape 與對(duì)象值分開(kāi)存儲(chǔ)。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

Shapes 可以實(shí)現(xiàn)稱(chēng)為 Inline Caches 或簡(jiǎn)稱(chēng) ICs 的優(yōu)化。通過(guò)組合,Shapes 和 ICs 可以加快代碼中相同位置的重復(fù)屬性訪問(wèn)速度。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

2.1 Class 和基于原型的編程

既然我們知道如何在 JavaScript 對(duì)象上快速進(jìn)行屬性訪問(wèn),那么讓我們看一下最近添加到 JavaScript 中的特性:class。JavaScript 中 class 的語(yǔ)法如下所示:

class Bar {constructor(x) {this.x = x;}getX() {return this.x;}}

盡管這看上去是 JavaScript 中的一個(gè)全新概念,但它僅僅是基于原型編程的語(yǔ)法糖:

function Bar(x) {this.x = x;}Bar.prototype.getX = function getX() {return this.x;};

在這里,我們?cè)?Bar.prototype 對(duì)象上分配一個(gè) getX 屬性。這與其他任何對(duì)象的工作方式完全相同,因?yàn)樵椭皇?JavaScript 中的對(duì)象!在基于原型的編程語(yǔ)言(如 JavaScript)中,方法通過(guò)原型共享,而字段則存儲(chǔ)在實(shí)際的實(shí)例上。

讓我們來(lái)實(shí)際看看,當(dāng)我們創(chuàng)建一個(gè)名為 foo 的 Bar 新實(shí)例時(shí),幕后所發(fā)生的事情。

const foo = new Bar(true);

通過(guò)運(yùn)行此代碼創(chuàng)建的實(shí)例具有一個(gè)帶有屬性 “x” 的 shape。 foo 的原型是屬于 class Bar 的 Bar.prototype 。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

Bar.prototype 有自己的 shape,其中包含一個(gè)屬性 ’getX’ ,取值則是函數(shù) getX ,它在調(diào)用時(shí)只返回 this.x 。 Bar.prototype 的原型是 Object.prototype ,它是 JavaScript 語(yǔ)言的一部分。由于 Object.prototype 是原型樹(shù)的根節(jié)點(diǎn),因此它的原型是 null 。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

如果你在這個(gè)類(lèi)上創(chuàng)建另一個(gè)實(shí)例,那么兩個(gè)實(shí)例將共享對(duì)象 shape。兩個(gè)實(shí)例都指向相同的 Bar.prototype 對(duì)象。

2.2 原型屬性訪問(wèn)

好的,現(xiàn)在我們知道當(dāng)我們定義一個(gè)類(lèi)并創(chuàng)建一個(gè)新實(shí)例時(shí)會(huì)發(fā)生什么。但是如果我們?cè)谝粋€(gè)實(shí)例上調(diào)用一個(gè)方法會(huì)發(fā)生什么,比如我們?cè)谶@里做了什么?

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const x = foo.getX();//^^^^^^^^^^

你可以將任何方法調(diào)用視為兩個(gè)單獨(dú)的步驟:

const x = foo.getX();// is actually two steps:const $getX = foo.getX;const x = $getX.call(foo);

第1步是加載這個(gè)方法,它只是原型上的一個(gè)屬性(其值恰好是一個(gè)函數(shù))。第2步是使用實(shí)例作為 this 值來(lái)調(diào)用該函數(shù)。讓我們來(lái)看看第一步,即從實(shí)例 foo 中加載方法 getX 。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

引擎從 foo 實(shí)例開(kāi)始,并且意識(shí)到 foo 的 shape 上沒(méi)有 ’getX’ 屬性,所以它必須向原型鏈追溯。我們到了 Bar.prototype ,查看它的原型 shape,發(fā)現(xiàn)它在偏移0處有 ’getX’ 屬性。我們?cè)?Bar.prototype 的這個(gè)偏移處查找該值,并找到我們想要的 JSFunction getX 。就是這樣!

但 JavaScript 的靈活性使得我們可以改變?cè)玩滄溄樱纾?/p>

const foo = new Bar(true);foo.getX();// → trueObject.setPrototypeOf(foo, null);foo.getX();// → Uncaught TypeError: foo.getX is not a function

在這個(gè)例子中,我們調(diào)用 foo.getX() 兩次,但每次它都具有完全不同的含義和結(jié)果。 這就是為什么盡管原型只是 JavaScript 中的對(duì)象,但優(yōu)化原型屬性訪問(wèn)對(duì)于 JavaScript 引擎而言比優(yōu)化常規(guī)對(duì)象的屬性訪問(wèn)更具挑戰(zhàn)性的原因了。

粗略的來(lái)看,加載原型屬性是一個(gè)非常頻繁的操作:每次調(diào)用一個(gè)方法時(shí)都會(huì)發(fā)生這種情況!

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const x = foo.getX();//^^^^^^^^^^

之前,我們討論了引擎如何通過(guò)使用 Shapes 和 Inline Caches 來(lái)優(yōu)化訪問(wèn)常規(guī)屬性的。 我們?nèi)绾卧诰哂邢嗨?shape 的對(duì)象上優(yōu)化原型屬性的重復(fù)訪問(wèn)呢? 我們?cè)谏厦嬉呀?jīng)看過(guò)是如何訪問(wèn)屬性的。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

為了在這種特殊情況下實(shí)現(xiàn)快速重復(fù)訪問(wèn),我們需要知道這三件事:

foo 的 shape 不包含 ’getX’ 并且沒(méi)有改變過(guò)。這意味著沒(méi)有人通過(guò)添加或刪除屬性或通過(guò)更改其中一個(gè)屬性來(lái)更改對(duì)象 foo 。 foo 的原型仍然是最初的 Bar.prototype 。這意味著沒(méi)有人通過(guò)使用 Object.setPrototypeOf() 或通過(guò)賦予特殊的 _proto_ 屬性來(lái)更改 foo 的原型。 Bar.prototype 的形狀包含 ’getX’ 并且沒(méi)有改變。這意味著沒(méi)有人通過(guò)添加或刪除屬性或更改其中一個(gè)屬性來(lái)更改 Bar.prototype 。

一般情況下,這意味著我們必須對(duì)實(shí)例本身執(zhí)行1次檢查,并對(duì)每個(gè)原型進(jìn)行2次檢查,直到找到我們正在尋找的屬性所在原型。 1 + 2N 次檢查(其中 N 是所涉及的原型的數(shù)量)對(duì)于這種情況聽(tīng)起來(lái)可能不太糟糕,因?yàn)檫@里原型鏈相對(duì)較淺 - 但是引擎通常必須處理更長(zhǎng)的原型鏈,就像常見(jiàn)的 DOM 類(lèi)一樣。這是一個(gè)例子:

const anchor = document.createElement(’a’);// → HTMLAnchorElementconst title = anchor.getAttribute(’title’);

我們有一個(gè) HTMLAnchorElement ,在其上調(diào)用 getAttribute() 方法。這個(gè)簡(jiǎn)單的錨元素原型鏈就已經(jīng)涉及6個(gè)原型!大多數(shù)有趣的 DOM 方法并不是直接存在于 HTMLAnchorElement 原型中,而是在原型鏈的更高層。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

我們可以在 Element.prototype 上找到 getAttribute() 方法。這意味著我們每次調(diào)用 anchor.getAttribute() 時(shí),JavaScript引擎都需要……

’getAttribute’HTMLAnchorElement.prototypeHTMLElement.prototype’getAttribute’Element.prototype’getAttribute’

總共有7次檢測(cè)!由于這是 Web 上一種非常常見(jiàn)的代碼,因此引擎會(huì)應(yīng)用技巧來(lái)減少原型上屬性加載所需的檢查次數(shù)。

回到前面的例子,我們?cè)?foo 上訪問(wèn) ’getX’ 時(shí)總共執(zhí)行了3次檢查:

class Bar {constructor(x) { this.x = x; }getX() { return this.x; }}const foo = new Bar(true);const $getX = foo.getX;

在直到我們找到攜帶目標(biāo)屬性的原型之前,我們需要對(duì)原型鏈上的每個(gè)對(duì)象進(jìn)行 shape 的缺失檢查。如果我們可以通過(guò)將原型檢查折疊到缺失檢查來(lái)減少檢查次數(shù),那就太好了。而這基本上就是引擎所做的: 引擎將原型鏈在 Shape 上,而不是直接鏈在實(shí)例上。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

每個(gè) shape 都指向原型。這也意味著每次 foo 原型發(fā)生變化時(shí),引擎都會(huì)轉(zhuǎn)換到一個(gè)新 shape。 現(xiàn)在我們只需要檢查一個(gè)對(duì)象的 shape,這樣既可以斷言某些屬性的缺失,也可以保護(hù)原型鏈鏈接。

通過(guò)這種方法,我們可以將檢查次數(shù)從 1 + 2N 降到 1 + N ,以便在原型上更快地訪問(wèn)屬性。但這仍相當(dāng)昂貴,因?yàn)樗谠玩湹拈L(zhǎng)度上仍然是線性的。 為了進(jìn)一步將檢查次數(shù)減少到一個(gè)常量級(jí)別,引擎采用了不同的技巧,特別是對(duì)于相同屬性訪問(wèn)的后續(xù)執(zhí)行。

2.3 Validity cells

V8專(zhuān)門(mén)為此目的處理原型的 shape。每個(gè)原型都具有一個(gè)不與其他對(duì)象(特別是不與其他原型共享)共享且獨(dú)特的 shape,且每個(gè)原型的 shape 都具有與之關(guān)聯(lián)的一個(gè)特殊 ValidityCell 。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

只要有人更改相關(guān)原型或其祖先的任何原型,此 ValidityCell 就會(huì)失效。讓我們來(lái)看看它是如何工作的。

為了加速原型的后續(xù)訪問(wèn),V8 建立了一個(gè) Inline Cache,其中包含四個(gè)字段:

JavaScript 引擎基礎(chǔ):原型優(yōu)化

在第一次運(yùn)行此代碼預(yù)熱 inline cache 時(shí),V8 會(huì)記住目標(biāo)屬性在原型中的偏移量,找到屬性的原型(本例中為 Bar.prototype ),實(shí)例的 shape(在這種情況下為 foo 的 shape),以及與實(shí)例 shape 鏈接的 直接原型 中 ValidityCell 的鏈接(在本例中也恰好是 Bar.prototype )。

下次 inline cache 命中時(shí),引擎必須檢查實(shí)例的 shape 和 ValidityCell 。如果它仍然有效,則引擎可以直接到達(dá) Prototype 上的 Offset 位置,跳過(guò)其他查找。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

當(dāng)原型改變時(shí),shape 將重新分配,且先前的 ValidityCell 失效。因此,Inline Cache 在下次執(zhí)行時(shí)會(huì)失效,從而導(dǎo)致性能下降。

回到之前的 DOM 示例,這意味著對(duì) Object.prototype 的任何更改不僅會(huì)使 Object.prototype 本身的 inline cache 失效,而且還會(huì)使其下游的所有原型失效,包括 EventTarget.prototype , Node.prototype , Element.prototype 等,直到 HTMLAnchorElement.prototype 為止。

JavaScript 引擎基礎(chǔ):原型優(yōu)化

實(shí)際上,在運(yùn)行代碼時(shí)修改 Object.prototype 意味著完全拋棄性能上的考慮。不要這樣做!

讓我們用一個(gè)具體的例子來(lái)探討這個(gè)問(wèn)題。 假設(shè)我們有一個(gè)類(lèi)叫做 Bar ,并且我們有一個(gè)函數(shù) loadX ,它調(diào)用 Bar 對(duì)象上的方法。 我們用同一個(gè)類(lèi)的實(shí)例多調(diào)用這個(gè) loadX 函數(shù)幾次。

class Bar { /* … */ }function loadX(bar) {return bar.getX(); // IC for ’getX’ on `Bar` instances.}loadX(new Bar(true));loadX(new Bar(false));// IC in `loadX` now links the `ValidityCell` for// `Bar.prototype`.Object.prototype.newMethod = y => y;// The `ValidityCell` in the `loadX` IC is invalid// now, because `Object.prototype` changed.

loadX 中的 inline cache 現(xiàn)在指向 Bar.prototype 的 ValidityCell 。 如果你之后執(zhí)行了類(lèi)似于改變 Object.prototype (這是 JavaScript 中所有原型的根節(jié)點(diǎn))的操作,則 ValidityCell 將失效,且現(xiàn)有的 inline cache 會(huì)在下次命中時(shí)丟失,從而導(dǎo)致性能下降。

修改 Object.prototype 被認(rèn)為是一個(gè)不好的操作,因?yàn)樗挂嬖诖酥盀樵驮L問(wèn)準(zhǔn)備的所有 inline cache 都失效。 這是另一個(gè) 不推薦 的例子:

Object.prototype.foo = function() { /* … */ };// Run critical code:someObject.foo();// End of critical code.delete Object.prototype.foo;

我們擴(kuò)展了 Object.prototype ,它使引擎在此之前存儲(chǔ)的所有原型 inline cache 均無(wú)效了。然后我們運(yùn)行一些用到新原型方法的代碼。引擎此時(shí)則需要從頭開(kāi)始,并為所有原型屬性的訪問(wèn)設(shè)置新的 inline cache。最后,我們刪除了之前添加的原型方法。

清理,這聽(tīng)起來(lái)像個(gè)好主意,對(duì)吧?然而在這種情況下,它只會(huì)讓情況變得更糟!刪除屬性會(huì)修改 Object.prototype ,因此所有 inline cache 會(huì)再次失效,而引擎又必須從頭開(kāi)始。

總結(jié):雖然原型只是對(duì)象,但它們由 JavaScript 引擎專(zhuān)門(mén)處理,以?xún)?yōu)化在原型上查找方法的性能表現(xiàn)。把你的原型放在一旁!或者,如果你確實(shí)需要修改原型,請(qǐng)?jiān)谄渌a運(yùn)行之前執(zhí)行此操作,這樣至少不會(huì)讓引擎所做的優(yōu)化付諸東流。

5. Take-aways

我們已經(jīng)了解了 JavaScript 引擎是如何存儲(chǔ)對(duì)象與類(lèi)的, Shapes 、 Inline Caches 和 ValidityCells 是如何幫助優(yōu)化原型的?;谶@些知識(shí),我們認(rèn)為存在一個(gè)實(shí)用的 JavaScript 編碼技巧,可以幫助提高性能:不要隨意修改原型對(duì)象(即便你真的需要,那么請(qǐng)?jiān)谄渌a運(yùn)行之前做這件事)。

(完)

來(lái)自:https://hijiangtao.github.io/2018/08/21/Prototypes/

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 很黄很暴力深夜爽爽无遮挡 | 97影院在线午夜 | 欧美xxxx性xxxxx高清视频 | 日韩黄在线观看免费视频 | 亚洲国产区 | 在线观看va| 久久视频精品线视频在线网站 | 国产高清一 | 国产亚洲一区二区在线观看 | 激情丝袜美女视频二区 | 欧美成人免费 | 成人入口 | 成人午夜久久精品 | 色综合久久88色综合天天提莫 | 国产91免费在线 | 一本大道香蕉大vr在线吗视频 | 香蕉久久精品国产 | 成人中文字幕一区二区三区 | 午夜精品视频在线观看美女 | 一区二区三区精品国产 | 91精品手机国产露脸 | 在线观看自拍视频 | 91免费国产高清观看 | 国产男女交性视频播放免费bd | 性高湖久久久久久久久aaaaa | 亚洲一区视频 | 久久亚洲国产高清 | 亚洲成a人片在线v观看 | 国产成人一级 | 久草网站| 在线观看日本免费视频大片一区 | 香蕉超级碰碰碰97视频在线观看 | 高清午夜线观看免费 | 成人a毛片手机免费播放 | 色伦网 | 欧美精品亚洲精品日韩专区 | 7777在线| 日本欧美久久久久免费播放网 | 国产成人免费片在线观看 | 欧美日本俄罗斯一级毛片 | 国产日b视频 |