以前看了不少關於做用域的文章和書,但是都漸漸淡忘了,這裏在從新複習做用域的時候,先去了解了js引擎編譯的大體過程,來幫助我加深對js的理解.javascript
瀏覽器的核心是兩部分:渲染引擎和javascript解釋器(引擎)
不一樣的瀏覽器有不一樣的渲染引擎,他的主要做用是生成網頁,一般分紅四個階段,由於圖片看起來更加直觀,因此把內容放在圖片裏.css
javascript引擎的主要做用就是,讀取網頁中的代碼,對其處理後運行.java
爲了提升運行速度,現代瀏覽器改成採用'即時編譯'(just in time compiler,縮寫JIT),即字節碼只在運行時編譯,用到哪行,就編譯哪一行,而且把編譯結果緩存,一般,一個程序被常常用到的,只是其中一小部分的代碼,有了緩存的編譯結果,整個程序的運行速度就會顯著提高.api
有的瀏覽器索性省略了字節碼的翻譯步驟,直接編譯成機器碼,好比CHROME瀏覽器的v8引擎.瀏覽器
字節碼不能直接運行,而是運行在一個虛擬機之上,通常也把虛擬機稱爲'javascript引擎'.由於js運行時未必有字節碼,因此js虛擬機並不徹底基於字節碼,而是部分基於源碼,即只要有可能,就經過JIT編譯器直接把源碼編譯成機器碼運行,省略字節碼步驟,這一點與其餘採用虛擬機的語言不盡相同,這樣作的目的,是爲了儘量地優化代碼,提升性能.緩存
正常的網頁加載流程:多線程
爲了不發生阻塞效應,較好的作法是將script標籤都放在底部,而不是頭部.這樣即便遇到腳本失去響應,網頁主體的渲染也已經完成了,用戶至少能夠看到內容,而不是面對一張空白的頁面.併發
若是某些腳本代碼很是重要,必定放在頁面頭部的話,最好將代碼嵌入頁面,而不是連接外部腳本文件,這樣能縮短加載時間app
將腳本放在網頁尾部加載還有一個好處,在DOM結構生成以前就調用DOM,js會報錯,若是腳本都在網頁尾部加載,就不存在這個問題,由於這時DOM確定已經生成了.dom
爲了解決腳本文件下載阻塞網頁渲染的問題,一個方法是加入defer屬性
渲染樹轉換爲網頁佈局,成爲'佈局流';佈局顯示到頁面的這個過程,稱爲'繪製' 他們都具備阻塞效應,而且會耗費不少時間和計算資源
頁面生成後,腳本操做和樣式表操做,都會觸發重流和重繪,用戶的互動,好比設置了鼠標懸停效果,頁面滾動,在輸入框中輸入文本,改變窗口大小.
重流和重繪並不必定一塊兒發生,重流必然致使重繪,重繪不必定須要重流.好比改變元素的顏色,只會致使重繪,而不會致使重流.改變元素的佈局,則會致使重流和重繪.
大多狀況下,瀏覽器會智能判斷,將重流和重繪只限制到相關的子樹上面,最小化所耗費的代價,而不會全局生成網頁
做爲開發者,應該儘可能設法下降重繪的次數和成本.好比,儘可能不要變更高層的DOM元素,而以底層DOM元素的變更代替,再好比,重繪table佈局和flex佈局,開銷都比較大.
var foo = document.getElementById(‘foobar’); foo.style.color = ‘blue’; foo.style.marginTop = ‘30px’;
上面的代碼只會致使一次重繪,由於瀏覽器會累積DOM 變更,而後一次性執行.
下面的代碼則會致使兩次重繪:
var foo = document.getElementById(‘foobar’); foo.style.color = ‘blue’; var margin = parseInt(foo.style.marginTop); foo.style.marginTop = (margin + 10) + ‘px’;優化技巧:
// 重繪代價高 function doubleHeight(element) { var currentHeight = element.clientHeight; element.style.height = (currentHeight * 2) + ‘px’; } all_my_elements.forEach(doubleHeight); // 重繪代價低 function doubleHeight(element) { var currentHeight = element.clientHeight; window.requestAnimationFrame(function () { element.style.height = (currentHeight * 2) + ‘px’; }); } all_my_elements.forEach(doubleHeight);
['1.js', '2.js'].forEach(function(src) { var script = document.createElement('script'); script.src = src; document.head.appendChild(script); });
這種方法的好處是,動態生成script標籤不會阻塞頁面渲染,也就不會形成瀏覽器假死,可是問題在於,這種方法沒法保證腳本的執行順序,哪一個腳本文件先下載完成,就先執行哪一個,
若是想避免這個問題,能夠設置async屬性爲false
['1.js', '2.js'].forEach(function(src) { var script = document.createElement('script'); script.src = src; script.async = false; document.head.appendChild(script); });
在這段代碼以後加載的腳本,要等待2.js執行完成後再執行
(function() { var script, scripts = document.getElementsByTagName('script')[0]; function load(url) { script = document.createElement('script'); script.async = true; script.src = url; scripts.parentNode.insertBefore(script, scripts); } load('//apis.google.com/js/plusone.js'); load('//platform.twitter.com/widgets.js'); load('//s.thirdpartywidget.com/widget.js'); }());
此外,動態嵌入還有一個地方須要注意,動態嵌入必須等到CSS文件加載完成後,纔會去下載外部腳本文件,靜態加載就不存在這個問題,script標籤指定的外部腳本文件,都是與css文件同時併發下載的.
同步任務,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務,
異步任務,不進入主線程,而進入任務隊列的任務.只有任務隊列通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行
異步模式主線程能夠運行更多的任務,提升了效率