首頁 >web前端 >js教程 >理解並優化javascript程式碼

理解並優化javascript程式碼

尚
轉載
2020-03-05 16:07:162686瀏覽

身為前端工程師,對於javascript大家都不陌生,這篇文章從更深層的方向-JS引擎去理解javascript到底是怎麼運作的,從而進行最佳化。

理解並優化javascript程式碼

JS Engine—— JS 引擎介紹

一、基本介紹

##js引擎是專門運行javascript的解釋器(interpreter)。目前比較主流的js 引擎和介紹,大家可以簡單了解一下:

  • V8 —  由Google使用C 開源的V8引擎,也是我們常聽到的一個引擎

  • Rhino — 由Mozilla Foundation完全用Java管理的一個開源引擎

  • SpiderMonkey — 第一代js引擎,曾經運行在Netscape Navigator 瀏覽器中,現在是Firefox

  • JavaScriptCore — Safari的開源js引擎

  • KJS — KDE 引擎,由Harri Porten開發

  • Chakra (JScript9) — Internet Explorer 引擎

  • #Chakra (JavaScript) — Microsoft Edge 引擎

  • #JerryScript —輕量級的js引擎,主要用於IOT

二、V8引擎運作流程

現在由於NodeJS和Google瀏覽器的普及,在此就主要介紹V8引擎的運作機制,如果大家對其他引擎有興趣,可以自行查看,基本上就是大同小異的

理解並優化javascript程式碼

#我們可以看到,JS引擎的處理過程是先解析轉換為AST語法樹,然後由解釋器主要做兩件事,第一個是轉換成機器語言bytecode,第二個是交給編輯器(optimizting compiler)進行最佳化,中間還有一個資料分析過程(profilling data) ,主要目的是為了進行最佳化JS的運行,優化完畢後的程式碼,再轉換為機器語言。

而V8引起的核心元件就分別是Ignition和TurboFan了

理解並優化javascript程式碼

JS Code—— Talk is cheap

#看到這,你一定會想,我知道這有啥用,Talk is cheap,有沒有程式碼可以分析的。那麼大家請看以下範例程式碼,透過分析程式碼後,我再詳細介紹其中的原理

範例程式碼一:

// first case
var a = {}
var b = {}
 
console.time()
for (let k = 0; k < 9999999; k++) {
    a[k] = 0
}
 
for (let i = 0; i < 9999999; i++) {
    b[i] = 0
}
console.timeEnd()
 
// second case
var a = {}
var b = {}
 
console.time()
for (let k = 0; k < 9999999; k++) {
    a[k] = 0
}
 
for (let i = 10000000; i < 19999999; i++) {
    b[i] = 0
}
console.timeEnd()
 
// third case
var a = {}
var b = {}
 
console.time()
for (let k = 0; k < 9999999; k++) {
    a[k] = 0
}
 
for (let i = 9999999; i < 0; i--) {
    b[i] = 0
}
console.timeEnd()

看完以上程式碼,內容很簡單,就是定義object a和b 然後不斷添加屬性,唯一區別的是,first case是a和b重複添加相同的屬性,second case是a和b添加不同的屬性,third case是a和b重複添加相同的屬性,但處理b的時候是相反順序的。

那麼問題來了:三塊程式碼,運行速度有沒有快慢之分,分別又大不大呢? (不用去確認循環次數,都是一樣滴!)

答案來了:用時時間大概是3 (500ms)

V8 Engine —— Hidden Class

我們知道,js是動態腳本語言,什麼意思呢,就是你可以很簡單的給object添加/刪除屬性,或改變其類型,大部分的js解釋器(interpreter)使用字典結構,在記憶體中儲存變數屬性值的位址,這種方式比起java和C#(非動態語言,當然了C#的dynamic類型另當別論,不在此贅述)要低效率的多,因為js的類型是可以隨時轉換的,本來使用字典結構結合固定的類型進行判斷,可以較容易的找到變數屬性值的位置,而在js中就難以實現了。

所以V8引擎就用了一個高效率的方法叫做Hidden Class。其他的引擎也有類似的方法,有叫Map的, Structures的,Hidden Class的等等,在這裡我們用Shape來定義它,這樣方便大家理解。

當我們定義一個object的時候,它會包含以下內容:

理解並優化javascript程式碼

理解並優化javascript程式碼

每個屬性的意義可以見上表。

那麼結合Shape,當定義一個object的時候,JS引擎會建立一個Shape (可以理解為連續的快取buffer),它的位置0和1儲存了x和y值,如下圖:

理解並優化javascript程式碼

如果我定義了兩個object,都是包含x和y的,那麼它就會共用一個shape,如下圖:

理解並優化javascript程式碼

#所以到這裡我們可以想像的到,我們只要定義相同屬性的object,那麼都會共用一個shape,當我們要呼叫任意一個shape的屬性值的時候,都可以透過同一個shape的offset來獲取到了。

那麼,當我們為object新增屬性的時候呢? V8引擎會根據類別過渡(class transition)原理創建新的shape來標記位置,如下圖

理解並優化javascript程式碼

也就是說,我們創建了3個shape,透過過渡鏈(transition chain)來實作一個object的追溯。

看到這裡,大家肯定在想,每多一個shape肯定就會多佔用一塊內存,那麼我為了優化的話,盡量在初始化的時候把屬性都定義好就能優化了,Bingo~我們可以參考下圖來驗證:

理解並優化javascript程式碼

#透過以上的原理解釋,相信大家肯定能夠推算解釋出,我們的範例程式碼的執行速度的區別了。

First Case: 

1. Shape (empty) for a和b

2. Shape 1....9999999 for a

3. Shape 1 ....9999999 for b

a和b都共用了相同的shape,可以重複使用

Second Case:

1. Shape (empty) for a和b

2. Shape 1....9999999 for a

3. Shape 10000000....19999999 for b

#一共定義了1 到199999999的shape,那麼second case肯定##時間要比first case要花多一倍的時間了

Thrid Case:

#1. Shape (empty) for a和b

2. Shape 1... .9999999 for a

3. Shape 9999999 ....1 for b

源碼:https://github.com/likeconan/Alipay_Wechat_Integration

更多JavaScript知識請關注PHP中文網js特效大全欄位。

以上是理解並優化javascript程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除