作用域那點事
- 2020 年 8 月 8 日
- 筆記
- javascript
作用域
用來聲明,訪問和修改變數的上下文,定義了變數的訪問許可權和查找機制。
作用域分類:
- 全局作用域 (整個JS運行環境,最頂層作用域,其聲明的函數、變數等都是全局的)
- 函數作用域 (函數執行時會創建作用域)
- 塊級作用域 ({ }大括弧在 let、const關鍵字特性產生的作用域)
JS屬於編譯語言,逐行執行;編譯的過程分為三部分:
- 分詞/詞法分析。
- 解析/把詞法分析轉換成AST(抽象語法樹)。
- 程式碼生成/把AST轉成可執行程式碼。
示例:
var a = 1;
編譯過程:
- 分成var a、a=1;兩部分進行分析。
- 查看當前作用域是否有a,如果有就忽略,如果沒有就創建變數a。
(var、function聲明的變數會在當前作用域下進行變數提升) - 賦值操作,首先查看當前作用域下是否有變數a,要是不存在變數a就會報錯,要是存在進行賦值;其次如果是作用域嵌套的情況,當前作用域下不存在變數a,就會向外層作用域查找,直到全局作用域,如果不存在變數a就會報錯。
執行過程:
- 執行var a語句進行查找a變數,這個過程叫做LHS(左側為查找目標)。
- 執行a = 1賦值操作,過程叫做RHS(右側為目標查找的目的)。
- 進行RHS必然會進行LHS。
注意:取值和賦值都是RHS,變數聲明和形參是LHS;RHS和LHS發生在執行過程中。
示例:
function foo(a) {
var b = a;
return a + b
}
var c = foo(2)
3處LHS查詢:
- var c 聲明
- var b 聲明
- 形參 a 聲明
4處RHS查詢:
- foo(2) 取值foo並執行
- var b = a語句,取值a
- a + b語句,取值a
- a + b語句,取值b
作用域嵌套:
當一個塊或函數嵌套在另一個塊或函數里,就發生了作用域嵌套。
作用域嵌套下變數的查找規則:
查找變數時如果當前作用域里沒有找到,就會向外層作用域查找,直到找到該變數或到全局作用域為止,如果沒找到就會報錯。
作用域嵌套查找變數的特點:
從內往外,LHS和RHS都會在當前作用域進行,LHS只有當前作用域下沒有找到所需變數,才會向外層作用域查找。
變數提升對LHS的影響:
- 變數提升發生在編譯過程,一個沒有用var聲明的變數不會進行變數提升,在該變數之前進行RHS,會報ReferenceError異常is not defined。
- 用var聲明的變數會進行變數提升,提升到當前作用域的最頂部,其值是undefined,因此在變數前進行取值不會報錯。
- 非嚴格模式下對沒有用var關鍵字聲明的變數語句之前進行RHS,報ReferenceError異常。如果是之後進行RHS,會先進行LHS,如果當前作用域還是全局作用域下都沒有找到,會自動創建一個全局變數並返回,嚴格模式下LHR查詢失敗時,並不會創建一個全局變數並返回,報ReferenceError異常。
變數的訪問許可權問題:
- 塊級作用域里的變數外層作用域是無法訪問,變數是指由let,const。
- 函數的形參和變數,內部函數是外層作用域無法訪問,屬於局部變數。變數是指由let,const,var,function聲明的。
部分參考:
《你不知道的JavaScript》