js進階學習筆記(一) -- 做用域

以前看了不少關於做用域的文章和書,但是都漸漸淡忘了,這裏在從新複習做用域的時候,先去了解了js引擎編譯的大體過程,來幫助我加深對js的理解.javascript

渲染引擎

瀏覽器的核心是兩部分:渲染引擎和javascript解釋器(引擎)
不一樣的瀏覽器有不一樣的渲染引擎,他的主要做用是生成網頁,一般分紅四個階段,由於圖片看起來更加直觀,因此把內容放在圖片裏.css

javascript引擎的主要做用就是,讀取網頁中的代碼,對其處理後運行.java

javascript虛擬機(引擎)

  • js是解釋型語言,有必定的優缺點:

  • 早期,瀏覽器內部對js的處理過程:

  • 即時編譯

    爲了提升運行速度,現代瀏覽器改成採用'即時編譯'(just in time compiler,縮寫JIT),即字節碼只在運行時編譯,用到哪行,就編譯哪一行,而且把編譯結果緩存,一般,一個程序被常常用到的,只是其中一小部分的代碼,有了緩存的編譯結果,整個程序的運行速度就會顯著提高.api

    有的瀏覽器索性省略了字節碼的翻譯步驟,直接編譯成機器碼,好比CHROME瀏覽器的v8引擎.瀏覽器

  • 字節碼不能直接運行,而是運行在一個虛擬機之上,通常也把虛擬機稱爲'javascript引擎'.由於js運行時未必有字節碼,因此js虛擬機並不徹底基於字節碼,而是部分基於源碼,即只要有可能,就經過JIT編譯器直接把源碼編譯成機器碼運行,省略字節碼步驟,這一點與其餘採用虛擬機的語言不盡相同,這樣作的目的,是爲了儘量地優化代碼,提升性能.緩存

script標籤的工做原理

正常的網頁加載流程:多線程

  • 爲了不發生阻塞效應,較好的作法是將script標籤都放在底部,而不是頭部.這樣即便遇到腳本失去響應,網頁主體的渲染也已經完成了,用戶至少能夠看到內容,而不是面對一張空白的頁面.併發

  • 若是某些腳本代碼很是重要,必定放在頁面頭部的話,最好將代碼嵌入頁面,而不是連接外部腳本文件,這樣能縮短加載時間app

  • 將腳本放在網頁尾部加載還有一個好處,在DOM結構生成以前就調用DOM,js會報錯,若是腳本都在網頁尾部加載,就不存在這個問題,由於這時DOM確定已經生成了.dom

defer屬性

爲了解決腳本文件下載阻塞網頁渲染的問題,一個方法是加入defer屬性

  • defer屬性的做用是,告訴瀏覽器,等到DOM加載完成後,再執行指定腳本

  • 有了defer屬性,瀏覽器下載腳本文件的時候,不會阻塞頁面渲染.下載的腳本文件在DOMContentLoaded事件觸發前執行(即剛剛讀取完標籤),並且能夠保證執行順序就是他們在頁面上出現的順序
  • 對於內置而不是連接外部腳本的script標籤,以及動態生成的script標籤,defer屬性不起做用

saync

  • 解決'阻塞效應'的另外一個方法是加入async屬性

  • async屬性能夠保證腳本下載的同時,瀏覽器繼續渲染,
  • 須要注意的是,一旦採用這個屬性,就沒法保證腳本的執行順序,哪一個腳本先下載結束,就先執行那個.使用async屬性的腳本文件中,不該該使用document.write方法

重流和重繪(此部分爲轉載)

  • 渲染樹轉換爲網頁佈局,成爲'佈局流';佈局顯示到頁面的這個過程,稱爲'繪製' 他們都具備阻塞效應,而且會耗費不少時間和計算資源

  • 頁面生成後,腳本操做和樣式表操做,都會觸發重流和重繪,用戶的互動,好比設置了鼠標懸停效果,頁面滾動,在輸入框中輸入文本,改變窗口大小.

  • 重流和重繪並不必定一塊兒發生,重流必然致使重繪,重繪不必定須要重流.好比改變元素的顏色,只會致使重繪,而不會致使重流.改變元素的佈局,則會致使重流和重繪.

  • 大多狀況下,瀏覽器會智能判斷,將重流和重繪只限制到相關的子樹上面,最小化所耗費的代價,而不會全局生成網頁

  • 做爲開發者,應該儘可能設法下降重繪的次數和成本.好比,儘可能不要變更高層的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’;
    優化技巧:
    • 讀取DOM或者寫入DOM,儘可能寫在一塊兒,不要混雜;
    • 緩存DOM信息
    • 不要一項一項的改變樣式,而是使用CSS class一次性改變樣式
    • 使用document fragment操做DOM
    • 動畫時使用absolute定位或fixed定位,這樣能夠減小對其餘元素的影響
    • 只在必要時才顯示元素
    • 使用window.requestAnimationFrame(),由於它能夠把代碼推遲到下一次重流時執行,而不是當即要求頁面重流
    • 使用虛擬DOM(virtual DOM)庫
    // 重繪代價高
    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文件同時併發下載的.

單線程模型

  • js採用的是單線程模型
    • 一次只能運行一個任務,其餘任務須要等待前一個任務完成才能工做.
    • 爲何不用多線程呢?
      • 緣由是不想瀏覽器變得複雜,由於多線程須要共享資源,且有可能修改彼此的運行結果
    • h5容許多線程,可是子線程徹底受主線程控制,且不得操做DOM,因此這個新標準並沒
    • 有改變js單線程的本質.
    • 存在的問題.
      容易形成瀏覽器假死狀態;

    • js設計者意識到,CPU徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務,等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去,這種機制就是js內部採用的Event Loop

Event Loop

  • Event Loop,指的是一種內部循環,用來排列和處理事件,以及執行函數.是一種程序結果,用於等待和發送信息和事件.
  • 全部任務能夠分紅兩種,一種是同步任務,一種是異步任務,
    • 同步任務,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務,

    • 異步任務,不進入主線程,而進入任務隊列的任務.只有任務隊列通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行

  • 下圖上面淺藍色底爲同步任務,圖下淺紅色底爲異步任務。

  • 同步模式和異步模式

異步模式主線程能夠運行更多的任務,提升了效率

相關文章
相關標籤/搜索