你不知道的JavaScript--作用域(一)

第一部分:作用域是什麼?

  • 編譯原理
  • 理解作用域
  • 作用域嵌套
  • 異常
  • 小結
編譯原理

編譯過程:

  • 分詞/詞法分析
  • 解析/語法分析
  • 代碼生成

1)分詞/詞法分析:

  • 這個過程會將字符組成的字符串分解成有意義的 代碼塊 ,這些代碼塊被稱爲 詞法單元。
  • eg:var a = 2; 這段程序通常會被分解成 var 、a 、= 、2 、;
  • 空格是否會被當作詞法單元,取決於空格在這門語言中是否有意義。
  • 分詞和詞法分析之間的主要差異在於 詞法單元的識別是通過 有狀態還是無狀態的 方式進行的。
  • 詞法分析:詞法單元生成器在判斷a是一個獨立的詞法單元還是其他詞法單元的一部分時,調用的是有狀態的解析規則,這個過程就被稱爲詞法分析。

2)解析/語法分析

  • 將詞法單元流(數組)轉換成一個 “抽象語法樹”。AST
  • 這棵樹是有元素逐級嵌套所組成的 代表程序語法結構的樹。

3)代碼生成

  • 將 AST 轉換成可執行代碼的過程
  • 創建並分配內存,並完成賦值操作
除了以上過程,js在語法分析和代碼生成階段有特定的步驟來對運行性能進行優化,包括對冗餘元素進行優化。
js代碼在執行前都要進行編譯。js編譯器首先會對代碼片段進行編譯,然後做好執行它的準備,並且馬上就會執行它。
理解作用域
  • 作用域是根據名稱找變量的一套規則
  • 引擎會對變量進行LHS、RHS查詢。分別是賦值操作的左側和右側。查找過程通過作用域進行協助。
  • LHS(左側):試圖找到變量的容器本身
  • RHS(右側):查找某個變量的值
  • 當變量出現在賦值操作的左側時進行LHS查詢(a=2),出現在右側是進行RHS查詢(console.log(a);)。
a=2;    //爲=2這個操作找到一個目標
console.log(a);  //關心的是這個值


function foo(a){
    console.log(a);
}
foo(2);
// 2次RHS  1次LHS

找到LHS、RHS查詢的次數?。
function foo(a){
    var b = a;
    return a+b;
}
var c = foo(2);
作用域嵌套
  • 當一個塊函數嵌套在另一個塊函數中時,就發生了作用域的嵌套。
  • 遍歷嵌套作用域鏈的規則很簡單: 引擎從當前的執行作用域開始查找變量, 如果找不到,就向上一級繼續查找。 當抵達最外層的全局作用域時, 無論找到還是沒找到, 查找過程都會停止。(當前作用域->上級作用域->…->全局作用域)
異常
  • RHS查詢在所有嵌套的作用域中遍尋不到所需的變量,引擎就會拋出ReferenceError的異常。
  • LHS
    • 非嚴格模式:在全局作用域中也無法找到目標變量,就會創建一個,並將它返回給引擎
    • 嚴格模式:禁止自動或隱式地創建全局變量,引擎拋出ReferenceError的異常。
  • ReferenceError:同作用域判別失敗相關
  • TypeError:作用域判別成功,但是對結果操作是非法和不合理的。(eg:對一個非函數類型的值進行函數調用,或者引用null或undefined類型的值中的屬性)
//RHS
console.log(a) //ReferenceError

var a;
a();
console.log(a)//TypeError

//LHS
"use strict";
function foo(a) {
    // console.log(a+b);
    b=a;
}
foo(2);//ReferenceError

function foo(a) {
    // console.log(a+b);
    b=a;
}
foo(2);
小結

1.作用域是一套規則,用於確定在何處以及如何查找變量(標識符)

  • 如果查找的目的是對變量進行賦值,那麼會使用LHS查詢
  • 如果查找的目的是獲取變量的值,就會使用RHS查詢
  • 賦值操作符會導致LHS查詢

2.在同一作用域中,對於同一個變量的多個聲明,編譯器只會編譯第一個聲明,其他的都會忽略
3.在編譯階段,編譯器會完成聲明和賦值兩個操作。

// 答案:4次RHS 3次LHS

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章