js進階學習筆記(一) -- js部分原理

之前看了一些關(guān)于作用域的文章和書,可是都漸漸淡忘了,這里我在重新復習作用域之前,先去了解一下js引擎編譯的大致過程,來幫助我加深對js的理解.

渲染引擎

瀏覽器的核心是兩部分:渲染引擎和javascript解釋器(引擎)得糜;不同的瀏覽器有不同的渲染引擎,他的主要作用是生成網(wǎng)頁,通常分成四個階段,因為圖片看起來更加直觀,所以把內(nèi)容放在圖片里.


渲染引擎工作.jpg

javascript虛擬機(引擎)

  • js是解釋型語言,有一定的優(yōu)缺點:
js引擎.jpg
  • 早期,瀏覽器內(nèi)部對js的處理過程:
瀏覽器對JS的處理過程.png
  • 即時編譯

為了提高運行速度,現(xiàn)代瀏覽器改為采用'即時編譯'(just in time compiler,縮寫JIT),即字節(jié)碼只在運行時編譯,用到哪行,就編譯哪一行,并且把編譯結(jié)果緩存,通常,一個程序被經(jīng)常用到的,只是其中一小部分的代碼,有了緩存的編譯結(jié)果,整個程序的運行速度就會顯著提升.

有的瀏覽器索性省略了字節(jié)碼的翻譯步驟,直接編譯成機器碼,比如CHROME瀏覽器的v8引擎.
  • 字節(jié)碼不能直接運行,而是運行在一個虛擬機之上,一般也把虛擬機稱為'javascript引擎'.因為js運行時未必有字節(jié)碼,所以js虛擬機并不完全基于字節(jié)碼,而是部分基于源碼,即只要有可能,就通過JIT編譯器直接把源碼編譯成機器碼運行,省略字節(jié)碼步驟,這一點與其他采用虛擬機的語言不盡相同,這樣做的目的,是為了盡可能地優(yōu)化代碼,提高性能.

script標簽的工作原理

正常的網(wǎng)頁加載流程:

script.jpg
  • 為了避免發(fā)生阻塞效應,較好的做法是將script標簽都放在底部,而不是頭部.這樣即使遇到腳本失去響應,網(wǎng)頁主體的渲染也已經(jīng)完成了,用戶至少可以看到內(nèi)容,而不是面對一張空白的頁面.

  • 如果某些腳本代碼非常重要,一定放在頁面頭部的話,最好將代碼嵌入頁面,而不是鏈接外部腳本文件,這樣能縮短加載時間

  • 將腳本放在網(wǎng)頁尾部加載還有一個好處,在DOM結(jié)構(gòu)生成之前就調(diào)用DOM,js會報錯,如果腳本都在網(wǎng)頁尾部加載,就不存在這個問題,因為這時DOM肯定已經(jīng)生成了.

defer屬性

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

  • defer屬性的作用是,告訴瀏覽器,等到DOM加載完成后,再執(zhí)行指定腳本
defer.jpg
  • 有了defer屬性,瀏覽器下載腳本文件的時候,不會阻塞頁面渲染.下載的腳本文件在DOMContentLoaded事件觸發(fā)前執(zhí)行(即剛剛讀取完</html>標簽),而且可以保證執(zhí)行順序就是他們在頁面上出現(xiàn)的順序
  • 對于內(nèi)置而不是鏈接外部腳本的script標簽,以及動態(tài)生成的script標簽,defer屬性不起作用

async

  • 解決'阻塞效應'的另一個方法是加入async屬性
saync.jpg
  • async屬性可以保證腳本下載的同時,瀏覽器繼續(xù)渲染,
  • 需要注意的是,一旦采用這個屬性,就無法保證腳本的執(zhí)行順序,哪個腳本先下載結(jié)束,就先執(zhí)行那個.使用async屬性的腳本文件中,不應該使用document.write方法

重流和重繪(此部分為轉(zhuǎn)載)

  • 渲染樹轉(zhuǎn)換為網(wǎng)頁布局,成為'布局流';布局顯示到頁面的這個過程,稱為'繪制' 他們都具有阻塞效應,并且會耗費很多時間和計算資源

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

  • 重流和重繪并不一定一起發(fā)生,重流必然導致重繪,重繪不一定需要重流.比如改變元素的顏色,只會導致重繪,而不會導致重流.改變元素的布局,則會導致重流和重繪.

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

  • 作為開發(fā)者,應該盡量設(shè)法降低重繪的次數(shù)和成本.比如,盡量不要變動高層的DOM元素,而以底層DOM元素的變動代替,再比如,重繪table布局和flex布局,開銷都比較大.

    var foo = document.getElementById(‘foobar’);
    
    foo.style.color = ‘blue’;
    foo.style.marginTop = ‘30px’;
    

    上面的代碼只會導致一次重繪,因為瀏覽器會累積DOM 變動,然后一次性執(zhí)行.
    下面的代碼則會導致兩次重繪:

    var foo = document.getElementById(‘foobar’);
    
    foo.style.color = ‘blue’;
    var margin = parseInt(foo.style.marginTop);
    foo.style.marginTop = (margin + 10) + ‘px’;
    

    優(yōu)化技巧:

    • 讀取DOM或者寫入DOM,盡量寫在一起,不要混雜;
    • 緩存DOM信息
    • 不要一項一項的改變樣式,而是使用CSS class一次性改變樣式
    • 使用document fragment操作DOM
    • 動畫時使用absolute定位或fixed定位,這樣可以減少對其他元素的影響
    • 只在必要時才顯示元素
    • 使用window.requestAnimationFrame(),因為它可以把代碼推遲到下一次重流時執(zhí)行,而不是立即要求頁面重流
    • 使用虛擬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);
    

腳本的動態(tài)嵌入(此部分為轉(zhuǎn)載)


['1.js', '2.js'].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  document.head.appendChild(script);
});

這種方法的好處是,動態(tài)生成script標簽不會阻塞頁面渲染,也就不會造成瀏覽器假死,但是問題在于,這種方法無法保證腳本的執(zhí)行順序,哪個腳本文件先下載完成,就先執(zhí)行哪個,

如果想避免這個問題,可以設(shè)置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執(zhí)行完成后再執(zhí)行

(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');
}());

此外,動態(tài)嵌入還有一個地方需要注意,動態(tài)嵌入必須等到CSS文件加載完成后,才會去下載外部腳本文件,靜態(tài)加載就不存在這個問題,script標簽指定的外部腳本文件,都是與css文件同時并發(fā)下載的.

單線程模型

單線程.jpg
  • js采用的是單線程模型
    • 一次只能運行一個任務,其他任務需要等待前一個任務完成才能工作.
    • 為什么不用多線程呢?
      • 原因是不想瀏覽器變得復雜,因為多線程需要共享資源,且有可能修改彼此的運行結(jié)果
  • h5允許多線程,但是子線程完全受主線程控制,且不得操作DOM,所以這個新標準并沒
    • 有改變js單線程的本質(zhì).
    • 存在的問題.
      容易造成瀏覽器假死狀態(tài);
單線程問題.jpg
  • js設(shè)計者意識到,CPU完全可以不管IO設(shè)備,掛起處于等待中的任務,先運行排在后面的任務,等到IO設(shè)備返回了結(jié)果,再回過頭,把掛起的任務繼續(xù)執(zhí)行下去,這種機制就是js內(nèi)部采用的Event Loop

Event Loop

  • Event Loop,指的是一種內(nèi)部循環(huán),用來排列和處理事件,以及執(zhí)行函數(shù).是一種程序結(jié)果,用于等待和發(fā)送信息和事件.
  • 所有任務可以分成兩種,一種是同步任務,一種是異步任務,
    • 同步任務,在主線程上排隊執(zhí)行的任務,只有前一個任務執(zhí)行完畢,才能執(zhí)行后一個任務,

    • 異步任務,不進入主線程,而進入任務隊列的任務.只有任務隊列通知主線程,某個異步任務可以執(zhí)行了,該任務才會進入主線程執(zhí)行

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

同步異步.jpg
  • 同步模式和異步模式
模式.jpg

異步模式主線程可以運行更多的任務,提高了效率

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诵闭,一起剝皮案震驚了整個濱河市最欠,隨后出現(xiàn)的幾起案子吼畏,更是在濱河造成了極大的恐慌姑丑,老刑警劉巖让网,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呀忧,死亡現(xiàn)場離奇詭異,居然都是意外死亡寂祥,警方通過查閱死者的電腦和手機荐虐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門七兜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丸凭,“玉大人,你說我怎么就攤上這事腕铸∠” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵狠裹,是天一觀的道長虽界。 經(jīng)常有香客問我,道長涛菠,這世上最難降的妖魔是什么莉御? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮俗冻,結(jié)果婚禮上礁叔,老公的妹妹穿的比我還像新娘。我一直安慰自己迄薄,他們只是感情好琅关,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著讥蔽,像睡著了一般涣易。 火紅的嫁衣襯著肌膚如雪画机。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天新症,我揣著相機與錄音步氏,去河邊找鬼。 笑死徒爹,一個胖子當著我的面吹牛戳护,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瀑焦,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼腌且,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了榛瓮?” 一聲冷哼從身側(cè)響起铺董,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎禀晓,沒想到半個月后精续,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡粹懒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年重付,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凫乖。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡确垫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帽芽,到底是詐尸還是另有隱情删掀,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布导街,位于F島的核電站披泪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搬瑰。R本人自食惡果不足惜款票,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泽论。 院中可真熱鬧艾少,春花似錦、人聲如沸佩厚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至潮瓶,卻和暖如春陶冷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毯辅。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工埂伦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人思恐。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓沾谜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胀莹。 傳聞我的和親對象是個殘疾皇子基跑,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,796評論 25 707
  • 從三月份找實習到現(xiàn)在,面了一些公司描焰,掛了不少媳否,但最終還是拿到小米、百度荆秦、阿里篱竭、京東、新浪步绸、CVTE掺逼、樂視家的研發(fā)崗...
    時芥藍閱讀 42,213評論 11 349
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)瓤介,斷路器吕喘,智...
    卡卡羅2017閱讀 134,633評論 18 139
  • Web前端技術(shù)由html、css和 javascript 三大部分構(gòu)成惑朦,是一個龐大而復雜的技術(shù)體系兽泄,其復雜程度不低...
    WEB攻程獅閱讀 1,875評論 2 116
  • LZC ,跟你認識了一年半了漓概,我到現(xiàn)在還不知道你真正的名字漾月,你只是在一開始相識的時候告訴過我,你叫LZC! 我記住...
    P尐c閱讀 427評論 0 0