1. 程式人生 > >How Javascript works (Javascript工作原理) (一) 引擎,運行時,函數調用棧

How Javascript works (Javascript工作原理) (一) 引擎,運行時,函數調用棧

由於 溢出 最好 介紹 error 堆棧溢出 git actual AR

個人總結: 這篇文章對JS底層的工作原理進行了介紹。

原文:https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf

一、引擎,運行時,調用堆棧

這是 JavaScript 工作原理的第一章。本章會對語言引擎,運行時,調用棧做一個概述。

事實上,有很多開發者在每天日常開發中都會使用 JavaScript 但是卻不了解其底層的知識。

概述

幾乎所有人都已經聽說過 V8 引擎的概念,並且很多人知道 JavaScript 是單線程的或者說是使用回調隊列的。

在本章中,我將會詳細地過一下這些概念並解釋 JavaScript 的工作原理。有賴於了解這些細節,通過合理地使用提供的 APIs 你將可能寫出更好的,非阻塞的程序。

如果你是新手,本文將會幫助你理解為什麽和其它語言比較 JavaScript 是不可思議的。

如果你是一個經驗豐富的 JavaScript 開發者,但願,它將會讓你更加深入地了解 JavaScript 運行時工作原理。

JavaScript 引擎

谷歌 V8 引擎是流行的 JavaScript 引擎之一。V8 引擎在諸如 Chrome 和 Node.js 內部使用。這裏有一個簡單的視圖來描繪其大概。

技術分享圖片

引擎包括兩個主要組件:

  • 動態內存管理 - 在這裏分配內存
  • 調用棧-這裏代碼執行即是你的堆棧結構

運行時

幾乎每個 JavaScript 開發者都使用過一些瀏覽器 API(比如 setTimeout)。然而這些 API並不是引擎所提供的。

那麽它們從何而來?

事實上這個情況有點復雜呃。。

技術分享圖片

所以,除了引擎但是實際上還有更多其它方面的東西。有被稱為 Web API 的東西,這些 Web API 是由瀏覽器提供的,比如 DOM,AJAX,setTimeout 以及其它。

於是乎,就有了流行的事件循環和回調隊列。

調用棧

JavaScript 只是一個單線程的編程語言,這意味著它只有一個調用棧。這樣它只能一次做一件事情。

調用棧是一種數據結構,裏面會記錄我們在程序中的大概位置。當執行進入一個函數,把它置於棧的頂部。如果從函數中返回則從棧頂部移除函數。這就是調用棧所能夠做的事情。

舉個栗子。查看如下代碼:

function multiply(x, y) {
  return x * y;
}

function printSquare(x) {
  var s = multiply(x, x);
  console.log(s);
}

printSquare(5);

當引擎開始執行這段代碼的時候,調用棧會被清空。之後,產生如下步驟:

技術分享圖片

調用棧中的每個入口被稱為堆棧結構。

當拋出異常的時候這正好是堆棧追蹤是如何被構造出來的-當發生異常的時候這基本上是調用棧的狀態。看下如下代碼:

function foo() {
  throw new Error(‘SessionStack will help you resolve crashes:)‘);
}

function bar() {
  foo();
}

function start() {
  bar();
}

start();

如果在 Chrome 中執行(假設代碼在 foo.js 的文件中),將會產生如下的堆棧追蹤:

技術分享圖片

"堆棧溢出"-當你達到最大調用棧大小的時候發生。這種情況相當容易發生,特別是當你使用遞歸而沒有仔細地檢查代碼的時候。查看下如下代碼:

function foo() {
  foo();
}

foo();

當引擎開始執行這段代碼的時候,它開始調用 foo 函數。這個函數,然而,會遞歸並開始調用其自身而沒有任何結束條件。所以在每步執行過程中,調用堆棧會反復地添加同樣的函數。執行過程如下所示:

技術分享圖片

在某一時刻,然而,調用堆棧中的函數調用次數超過了調用堆棧的實際大小,這樣瀏覽器決定拋出錯誤的動作,如下所示:

技術分享圖片

在單線程中運行代碼會相當輕松因為你不用處理多線程環境中產生的一些復雜情況,比如死鎖。

但是在單線程運行代碼也會有相當的限制。由於 JavaScript 只有一個調用棧,如果運行很慢會發生什麽?

並發和事件循環

當你在調用棧中有函數為了完成運行需要消耗大量的時間的時候會發生什麽?例如,想象一下你想要在瀏覽器用 JavaScript 來執行一些復雜的圖像轉化。

你或許會問-為什麽這也是個問題?問題是這樣的當調用棧有函數需要執行,瀏覽器實際上不能做其它任何事-它被阻塞了。這意味著瀏覽器不能夠執行渲染,它不能夠運行其它代碼,它卡住了。如果你想要在 app 中擁有酷炫的流暢 UI 體驗,這將會是個問題。

這不會是唯一的問題。一旦瀏覽器開始在調用棧中執行如此多的任務,瀏覽器將會在相當一段時間內停止交互。大多數瀏覽器會拋出一個錯誤,詢問你是否關閉網頁。

技術分享圖片

現在,這並不是最好的用戶體驗,難道不是嗎?

因此,如何不阻塞 UI 且不讓瀏覽器停止響應來執行運行緩慢的代碼呢?使用異步回調。

這將會在 『JavaScript 工作原理』 第二章:『在V8 引擎中如何寫最佳代碼的 5 條小技巧』中進行詳細闡述。

How Javascript works (Javascript工作原理) (一) 引擎,運行時,函數調用棧