失效的詞法作用域查詢規則?
今天在工作中遇到這樣一個問題:
var a = 1; //一個常量值,本意是在後面多個函式中引用該常量值
function test1() {
var a = a;
console.log(a);
}
我想當然的認為函式 test1()
會列印出 1
,然而結果是 undefined
。後來我意識到,可能是 JS 中的“變數提升”機制在作怪。
在《你不知道的 JavaScript (上卷)》中,作者對“變數提升”機制進行了剖析。在 JS 的編譯階段(是的,JS也需要編譯),首先會蒐集變數與函式宣告。無論在什麼位置進行的宣告,都會在這一階段收集到,並且放置到最前面。
如下面程式碼所示:
function test() {
a = 1;
console.log(a);
var a;
}
test(); //1
console.log(a); //ReferenceError
函式 test
相當於:
function test() {
var a;
a = 1;
console.log(a);
}
所以呢,在 test1
中,第一條語句相當於:
var a;
a = a;
由於在該函式作用域內已經可以通過向左查詢查詢到變數 a
,那麼就不需要向外面的作用域去尋找了。雖然找到了名稱為 a
的變數,但是沒有給它進行初始化,因此第二句賦值語句的結果是 a === undefined
。
而對於使用 let
關鍵字宣告的變數來說,不存在變數提升問題,並且在函式開始到該宣告之前,都不允許對該變數進行使用,這段區域被稱為“暫時死區”(TDZ)。
關於暫時死區,參考如下解釋:
當程式的控制流程在新的作用域(module, function或block作用域)進行例項化時,在此作用域中的用let/const宣告的變數會先在作用域中被建立出來,但因此時還未進行詞法繫結,也就是對宣告語句進行求值運算,所以是不能被訪問的,訪問就會丟擲錯誤。所以在這執行流程一進入作用域建立變數,到變數開始可被訪問之間的一段時間,就稱之為TDZ(暫時死區)。
簡單來說就是,在進入變數所宣告的作用域到變數初始化之前的程式執行時間裡,變數都是不可用的。
例如:
function test2() {
let a = a;
console.log(a); //ReferenceError
}
在 ECMAScript 規範的 13.3.1 章節說明了變數是如何進行初始化的。
所以test2
並不等價於:
function test2_2() {
let a;
a = a;
console.log(a); //undefined
}
在 test2
中,雖然編譯器會首先建立變數 a
,但是不會對其進行初始化,而是試圖使用 a
來對其進行初始化。但是因為彼時 a
處於 TDZ 中,所以會丟擲 ReferenceError
錯誤。test2_2
則不然,語句 let a;
會使編譯器建立變數 a
,同時會賦予它 undefined
值。
同理可以解釋:
var a = 1;
function test2(a) {
let a = a;
console.log(a);
}
test2(a); //ReferenceError
而對於
var a = 1;
function test1(a) {
var a = a;
console.log(a); //1
}
來說,由於形式引數 a
與變數宣告同處於一個作用域內, var a
相當於重複宣告。例如:
var a = 1;
var a;
console.log(a); //1
參考資料:
- 《你不知道的 JavaScript(上卷)》第4章
- https://segmentfault.com/a/1190000008213835
- https://www.ecma-international.org/ecma-262/6.0/#sec-let-and-const-declarations
相關文章
- JS語法作用域與詞法作用域JS
- javascript 詞法作用域JavaScript
- 詞法作用域和動態作用域
- js 的詞法作用域和 thisJS
- JavaScript 深入之詞法作用域和動態作用域JavaScript
- 深入學習js之——詞法作用域和動態作用域JS
- 深入學習js之——詞法作用域和動態作用域#2JS
- 深入理解javascript作用域系列第二篇——詞法作用域和動態作用域JavaScript
- 深入理解閉包之前置知識→作用域與詞法作用域
- JavaScript夯實基礎系列(一):詞法作用域JavaScript
- 7-3 名字查詢與類的作用域
- Python 中的作用域規則和閉包簡析Python
- 正規表示式查詢相似單詞的方法
- C語言學習四 — 函式與作用域規則C語言函式
- 細讀《你不知道的JavaScript·上卷》1-2 詞法作用域JavaScript
- Sql Server 聯合查詢的排序規則衝突SQLServer排序
- 時序資料庫 TDengine SQL 查詢語法規則彙總,官方教程奉上!資料庫SQL
- 《GraphQL 名詞 101:解析 GraphQL 的查詢語法》【譯】
- 變數的作用域最小化原則變數
- SYS查詢不到的同義詞
- medz/cors 跨域突然失效的解決辦法!CORS跨域
- js的作用域、作用域鏈JS
- Perl語法的基本規則
- Sass 語法規則
- laravel 定義模型的區域性查詢作用域方法沒有PhpStorm沒有程式碼提示Laravel模型PHPORM
- js的作用域和作用域鏈JS
- js的作用域與作用域鏈JS
- c++ 類作用域中的名字查詢C++
- JNI/NDK開發指南(2):JVM查詢java native方法的規則JVMJava
- JSON語法規則JSON
- JSON 語法規則JSON
- XML 語法規則概述XML
- CSS @page語法規則CSS
- JAVA語法規則 (轉)Java
- SQL Server 跨域查詢SQLServer跨域
- ES 入門 - 基於詞項的查詢
- js查詢字串中字元最多的單詞JS字串字元
- 優化擁有謂詞or的子查詢優化