您好,登錄后才能下訂單哦!
如何理解javascript代碼并提出優化?相信有很多人都不太了解,今天小編為了讓大家更加了解,所以給大家總結了以下內容,一起往下看吧。
作為一名前端工程師,對于javascript大家都不陌生,這篇文章從更深層次的方向——JS引擎去理解javascript到底是怎么運行的,從而進行優化。
JS Engine—— JS 引擎介紹
一、基本介紹
js引擎是一個專門運行javascript的解釋器(interpreter)。目前比較主流的js 引擎和介紹,大家可以簡單了解一下:
V8?—? 由谷歌使用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和谷歌瀏覽器的普及,在此就主要介紹V8引擎的運行機制,如果大家對其他引擎感興趣,可以自行查看,基本上是大同小異的
我們可以看到,JS引擎的處理過程是先解析轉換為AST語法樹,然后由解釋器主要做兩件事,第一個是轉化為機器語言bytecode,第二個是交給編輯器(optimizting compiler)進行優化,中間還有一個數據分析過程(profilling data),主要目的是為了進行優化JS的運行,優化完畢后的代碼,再轉化為機器語言。
而V8引起的核心組件就分別是Ignition和TurboFan了
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)< 1 (1000ms) < 2 (2000ms),幾乎就是2倍的速度差了。
V8 Engine —— Hidden Class
我們知道,js是動態腳本語言,什么意思呢,就是你可以很簡單的給object添加/刪除屬性,或者改變其類型,大部分的js解釋器(interpreter)使用字典結構,在內存中存儲變量屬性值的地址,這種方式比起java和C#(非動態語言,當然了C#的dynamic類型另當別論,不在此贅述)要低效率的多,因為js的類型是可以隨時轉換的,本來使用字典結構結合固定的類型進行判斷,可以較容易的找到變量屬性值的位置,而在js中就難以實現了。
所以V8引擎就使用了一種高效率的方法叫Hidden Class。其他的引擎也有類似的方法,有叫Map的, Structures的,Hidden Class的等等,在這里我們用Shape來定義它,這樣方便大家理解。
當我們定義一個object的時候,它會包含以下內容:
每個屬性的意義可以見上表。
那么結合Shape,當定義一個object的時候,JS引擎會創建一個Shape (可以理解為連續的緩存buffer),它的位置0和1存儲了x和y值,如下圖:
如果我定義了兩個object,都是包含x和y的,那么它就會共用一個shape,如下圖:
所以到這里我們可以想象的到,我們只要定義相同屬性的object,那么都會共用一個shape,當我們要調用任意一個shape的屬性值的時候,都可以通過同一個shape的offset來獲取到了。
那么,當我們給object添加屬性的時候呢?V8引擎會根據類過渡(class transition)原理創建新的shape來標記位置,如下圖
也就是說,我們創建了3個shape,通過過渡鏈(transition chain)來實現一個object的追溯。
看到這里,大家肯定在想,每多一個shape肯定就會多占用一塊內存,那么我為了優化的話,盡量在初始化的時候把屬性都定義好就能優化了,Bingo~ 我們可以參照下圖來驗證:
通過以上的原理解釋,相信大家肯定能夠推算解釋出,我們的示例代碼的執行速度的區別了。
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 到 19999999的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
以上就是如何理解javascript代碼并提出優化的簡略介紹,當然詳細使用上面的不同還得要大家自己使用過才領會。如果想了解更多,歡迎關注億速云行業資訊頻道哦!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。