happyjam

happyjam

JS編譯原理

JS 編譯原理#

var name = "rose";

上面這行程式碼在 JS 中會這樣呈現:

var name; // 編譯階段處理
name = "rose"; // 執行階段處理

JS 編譯主要分為兩個階段:編譯階段和執行階段

編譯階段#

此階段主角為編譯器。

  • JS 找遍作用域,看是否存在 name 的變數
    • 如果已經存在,則什麼都不做,直接忽略var name這個宣告,繼續編譯下去;
    • 如果沒有,則在當前作用域中新增一個name變數
  • 編譯器會為引擎生成運行時所需的程式碼,程式就進入了執行階段。

執行階段#

此階段主角為JS 引擎。

  • JS 引擎在執行時,會先找遍當前作用域,看是否有一個叫name的變數。
    • 如果有,直接賦值
    • 如果沒有,則為當前作用域沒有。則去父級作用域看是否有,如果無,則去上一級作用域中查找。
    • 如果最終沒有找到,則拋異常。

      作用域套作用域,即作用域鏈。

作用域#

變數最基本的能力就是能夠存儲變數中的值,並且允許我們對此變數進行訪問和修改,而對於變數存儲、訪問的規則作用域

全局作用域#

在任何函數外或程式碼塊外的頂層作用域就是全局作用域,裡面的變數就是全局變數。

var name = "rose"; //全局作用域

function showName() {
  //函數作用域
  console.log(name);
}
{
  name = "test"; //區塊級作用域
}
showName(); //test

可以看到,全局變數在全局作用域、函數作用域、區塊級作用域中都可以正常訪問。

函數作用域#

在函數中的作用域就是函數作用域。

function showName() {
  var name = "jack"; //函數作用域
}
showName(); //方法調用
{
  console.log(name); //區塊級作用域,Uncaught ReferenceError: name is not defined
}
console.log(name); //全局作用域,Uncaught ReferenceError: name is not defined

可以看到,函數內部變數,在全局作用域及區塊級作用域中,都無法訪問,只有在函數內部,才能訪問到,所以函數內部的變數也稱為局部變數

區塊級作用域#

ES6中新出的letconst關鍵字 自帶作用域。
區塊級作用域相當於是只在這塊程式碼塊中生效,如果它被大括號{}包圍,則大括號就是一段程式碼塊,程式碼塊中使用const宣告的變數也被稱為局部變數。

 {
   let name='rose';
 }

 console.log(name);    //Uncaught ReferenceError: name is not defined

 function showName{
   console.log(name);
 }

 showName();    //Uncaught ReferenceError: name is not defined

可以看到,區塊級作用域中的變數,在程式碼塊外面就訪問不到了。

作用域鏈#

作用域和作用域的嵌套,就產生了作用域鏈。作用域鏈的查找,向外不向內。

變數提升#

name = "rose";
console.log(name); //rose
var name;

可以發現,這段程式碼可以正常執行,並且不會報錯。
在 JS 眼中程式實際上是這樣的:

var name;
name = "rose";
console.log(name); // rose

letconst程式碼:

name = "rose";
console.log(name); //Uncaught ReferenceError: Cannot access 'name' before initialization
let name;

let 、const ** 禁用變數提升。**const 宣告後必賦值。

let、const、var 的區別#

  1. 區塊級作用域:區塊級作用域由{}包括,letconst具有區塊級作用域,var不存在區塊級作用域。

區塊級作用域解決了ES5中的兩個問題:

  • 內層變數可能覆蓋外層變數
  • 用來計數的迴圈變數洩漏為全局變數
  1. 變數提升:var 存在變數提升,let 和 const 不存在變數提升,即變數只能在宣告後使用,否則會報錯。
  2. 給全局添加屬性:瀏覽器的全局物件是 window,Node 的全局變數是 global。var 宣告的變數為全局變數,並且會將該變數添加為全局物件的屬性,但是 let 和 const 不會。
  3. 重複宣告:var 宣告變數時,可以重複宣告變數,後宣告的變數會覆蓋之前宣告的變數。let 和 const 不允許在同一作用域下重複宣告變數。
  4. 暫時性死區:在使用 let、const 關鍵字宣告變數時,該變數是不可用的,這在語法上,成為暫時性死區

使用 var 宣告的變數不存在暫時性死區。

  1. 初始值設定:在變數宣告時,var 和 let 可以不用設定初始值。而 const 宣告變數必須設定初始值。
  2. 指標指向:let 和 const 都是 ES6 新增的用於創建變數的語法。let 創建的變數可以更改指標指向(可以重新賦值)。但 const 宣告的變數不允許改變指標的指向(不允許重新賦值)。

暫時性死區#

var name = "rose";

{
  name = "bob";
  let name; //Uncaught ReferenceError: Cannot access 'name' before initialization
}

如果區塊中存在 let 和 const,這個區塊對於這些關鍵字宣告的變數,從一開始就形成了封閉作用域。
因為 JS 清楚地感知到了 name 是用 let 宣告在當前這個程式碼塊內的,所以會給這個變數 name 加上了暫時性死區的限制,它就不往外探出頭了。
因此,如果我們把上面的let name;去掉,程式也能正常執行,name 的值也能被成功修改為 blob,就是正常地按照作用域鏈的規則,向外探出頭去了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。