許多文章使用諸如提升、臨時死區(TDZ)、功能和塊作用域等,通常不引用標準。其中一些術語甚至沒有包含在語言標準中。在不參考語言標準的情況下解釋該主題是完全可以的。不過,我透過引用來解釋這個主題,以便那些想要更深入研究的人,因為理解 ECMAScript 標準對於全面掌握 JavaScript 至關重要。
許多組織都有 JavaScript 參考資料,例如 MDN Web Docs、javascript.info 等。然而,有一個標準組織的唯一目的是標準化和記錄電腦系統。這個組織就是 Ecma International,該領域的可靠權威。該組織維護一個名為 ECMA-262 的標準,這是識別該標準的公司內部編號。 Ecma International 將此標準定義為 ECMAScript 通用程式語言(我們通常稱為 JavaScript)的支柱。理解這個標準是理解語言本身的關鍵。截至 2024 年 8 月的最新標準是 ECMAScript 第 15 版,也稱為
ECMAScript 2024。
要理解 var、let 和 const 之間的區別,有必要了解執行上下文的概念。
執行上下文是 ECMAScript 標準中定義的抽象結構。它是當前程式碼執行的環境。為了簡化事情,我們可以假設存在全域執行上下文和功能執行上下文。
// Global Execution Context let globalVariable = 'This is a global variable' function outerFunction() { // Outer Function Execution Context let outerVariable = 'This is an outer variable' function innerFunction() { // Inner Function Execution Context let innerVariable = 'This is an inner variable' } innerFunction() } outerFunction()為了追蹤程式碼的執行,執行上下文包含多個元件,稱為狀態元件。其中,詞法環境和變數環境對於理解 var、let 和 const 關鍵字的行為至關重要。
LexicalEnvironment 和 VariableEnvironment 都是環境記錄。環境記錄也是 ECMAScript 標準中定義的抽象資料結構。它建立了標識符與特定變數和函數的關聯。標識符是 JavaScript 中引用值、函數、類別和其他資料結構的名稱。在下面的例子中,讓variable = 42,variable是儲存數字42的值的變數名稱(識別碼)。
每次執行程式碼時,執行上下文都會建立一個新的環境記錄。除了儲存標識符之外,環境記錄還有一個 [[OuterEnv]] 字段,可以為 null 或對外部環境記錄的引用。
以圖形方式,上一個範例中的執行上下文和環境記錄可以表示如下:
// Global Execution Context { // Environment Record { identifier: 'globalVariable' value: 'This is a global variable' } { identifier: 'outerFunction' value: Function } [[OuterEnv]]: null } // Outer Function Execution Context { // Environment Record { identifier: 'outerVariable' value: 'This is an outer variable' } { identifier: 'innerFunction' value: Function } [[OuterEnv]]: Global Execution Context } // Inner Function Execution Context { // Environment Record { identifier: 'innerVariable' value: 'This is an inner variable' } [[OuterEnv]]: Outer Function Execution Context }關於執行情境需要記住的另一個重要點是它有兩個不同的階段:
建立階段和執行階段。這兩個階段對於理解 var 和 let 或 const 之間的差異至關重要。
ECMAScript 標準的第 14.3.1 Let 和 Const 聲明中規定了以下內容:
let 和 const 宣告定義了作用域為執行執行上下文的 LexicalEnvironment 的變數。這些變數是在實例化其包含的環境記錄時創建的,但在評估變數的 LexicalBinding 之前不能以任何方式存取。由帶有初始化程序的 LexicalBinding 定義的變數在計算 LexicalBinding 時(而不是在創建變數時)被分配其初始化程序的賦值表達式的值。如果 let 宣告中的 LexicalBinding 沒有初始化程序,則在對 LexicalBinding 求值時,該變數將被賦予未定義的值。為了理解這句話,我會逐句解釋。
let 和 const 宣告定義作用域為執行執行上下文的 LexicalEnvironment 的變數。這表示使用 let 或 const 關鍵字建立的變數的作用域是定義它們的區塊。程式碼區塊是大括號內的任意 JavaScript 程式碼。
let condition = true if (condition) { let blockScopedVariable = 'This is a block-scoped variable' console.log(blockScopedVariable) // This is a block-scoped variable } console.log(blockScopedVariable) // ReferenceError: blockScopedVariable is not defined // Global Execution Context { // Environment Record { identifier: 'condition' value: true } [[OuterEnv]]: null // Block Environment Record { identifier: 'variable' value: 'This is a block-scoped variable' } [[OuterEnv]]: Global Execution Context }
變數在實例化其包含的環境記錄時創建,但在評估變數的 LexicalBinding 之前不能以任何方式存取。
As previously mentioned, the Execution Context has two phases. This statement means that during the Creation Phase of the Execution Context, variables are stored in their corresponding Environment Record but have not yet been assigned any value. They are uninitialised.
console.log(varaible) // ReferenceError: Cannot access 'varaible' before initialization let varaible = 42 // Global Execution Context Creation Phase { // Environment Record { identifier: 'variable' value: uninitialised } [[OuterEnv]]: null }
Because the variable is already created (instantiated) in the Environment Record, the Execution Context knows about it but can't access it before evaluation(the Execution Phase of the Execution context). The state of the variable being uninitialised is also known as a Temporary Dead Zone(TDZ). We would have a different error if the variable hadn't been created in the Environment Record.
console.log(varaible) // ReferenceError: varaible is not defined // Global Execution Context Creation Phase { // Environment Record { } [[OuterEnv]]: null }
A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer's AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created.
LexicalBinding is a form of the Identifier, which represents the variable's name. The Initializer is the variable's value, and AssignmentExpression is the expression used to assign that value to the variable's name, such as the '=' sign in let variable = 42. Therefore, the statement above means that variables created with let or const keywords are assigned their value during the Execution Phase of the Execution Context.
let variable = 42 // Global Execution Context Creation Phase { // Environment Record { identifier: 'variable' value: uninitialised } [[OuterEnv]]: null } // Global Execution Context Execution Phase { // Environment Record { identifier: 'variable' value: 42 } [[OuterEnv]]: null }
If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.
This means that if a let variable is created without an initial value, undefined is assigned to it during the Execution Phase of the Execution Context. Variables declared with the const keyword behave differently. I will explain it in a few paragraphs later.
let variable // Global Execution Context Creation Phase { // Environment Record { identifier: 'variable' value: uninitialised } [[OuterEnv]]: null } // Global Execution Context Execution Phase { // Environment Record { identifier: 'variable' value: undefined } [[OuterEnv]]: null }
The standard also defines a subsection called 14.3.1.1 'Static Semantics: Early Errors,' which explains other essential aspects of variables defined with the let and const keywords.
LexicalDeclaration: LetOrConst BindingList;
- It is a Syntax Error if the BoundNames of BindingList contains "let".
- It is a Syntax Error if the BoundNames of BindingList contains any duplicate entries. LexicalBinding : BindingIdentifier Initializer
- It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true.
LetOrConst is a grammar rule which specifies that variable declarations can start with the let or const keywords.
BindingList is a list of variables declared with let or const keywords. We could imagine BindingList as a data structure like this:
let a = 1 let b = 2 let c = 3 const d = 4 const e = 5 BindingList: [ { identifier: 'a', value: 1 }, { identifier: 'b', value: 2 }, { identifier: 'c', value: 3 }, { identifier: 'd', value: 4 }, { identifier: 'e', value: 5 } ]
A Syntax Error is an error that breaks the language's grammatical rules. They occur before the code's execution. Let's analyse the first Syntax Error.
- It is a Syntax Error if the BoundNames of BindingList contains "let".
The BoundNames of BindingList are the names of variables declared with let or const keywords.
let a = 1 let b = 2 let c = 3 const d = 4 const e = 5 BoundNames: ['a', 'b', 'c', 'd', 'e']
A Syntax Error will occur when the BoundNames list contains “let”.
let let = 1 // SyntaxError: let is disallowed as a lexically bound name const let = 1 // SyntaxError: let is disallowed as a lexically bound name
- It is a Syntax Error if the BoundNames of BindingList contains any duplicate entries.
It means we can't use the same names for variables declared with the let or const keywords if they are already used in that scope.
let a = 1 let a = 2 // SyntaxError: Identifier 'a' has already been declared
- It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true.
IsConstantDeclaration is an abstract operation in the standard that checks if the variable is declared with the const keyword. This rule could be decrypted like that: if IsConstantDeclaration is true and the variable doesn't have an Initializer, a Syntax Error will be returned.
const x; // SyntaxError: Missing initializer in const declaration
Another vital thing only related to the const keyword: variables declared with the const keyword can't be reassigned. It is not stated explicitly in the standard, but we can get it from the IsConstantDeclaration operation and the syntax rule that variables declared with the const keyword should always be initialised with the Initializer
const variable = 42 variable = 46 // TypeError: Assignment to constant variable
Variable Statement
Before 2015, when the ECMAScript 2015 wasn't released yet, only the var keyword was available to create a variable in JavaScript.
In the paragraph 14.3.2 Variable Statement of ECMAScript standard the following is stated:
A var statement declares variables scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Environment Record is instantiated and are initialized to undefined when created. Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collectively define only one variable. A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer's AssignmentExpression when the VariableDeclaration is executed, not when the variable is created.
I again explain it sentence by sentence.
A var statement declares variables scoped to the running execution context's VariableEnvironment.
This means that variables declared with the var keyword are either function-scoped if declared inside a function or global-scoped if declared outside any function.
let condition = true if (condition) { var globalVariable = 'This is a global variable' } console.log(globalVariable ) // This is a global variable function outerFunction() { // Outer Function Execution Context var outerVariable = 'This is an outer variable' } outerFunction() // Global Execution Context { // Environment Record { identifier: 'condition' value: true } { identifier: 'globalVariable' value: 'This is a global variable' } { identifier: 'outerFunction' value: Function } [[OuterEnv]]: null } // Outer Function Execution Context { // Environment Record { identifier: 'outerVariable' value: 'This is an outer variable' } [[OuterEnv]]: Global Execution Context }
Var variables are created when their containing Environment Record is instantiated and are initialized to undefined when created.
During the Creation Phase of the Execution Context variables are assigned the undefined value. The process of assigning the undefined to a variable during the Creation Phase is often referred to as "hoisting" or declaration hoisting. It is worth mentioning that the terms "hoisting" or declaration hoisting are not included in the standard. However, it is a convention used by many developers to explain the availability of variables "before" they were declared.
console.log(globalVariable) // undefined var globalVariable = 'This is a global variable' // Global Execution Context Creation Phase { // Environment Record { identifier: 'globalVariable' value: undefined } [[OuterEnv]]: null }
Sometimes, it is explained that the code example above is possible because variables declared with the var keyword are "moved" to the top of the scope. However, nothing is moved anywhere; it is only possible by assigning the undefined value to the variable during the Creation Phase of Execution Context.
Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collectively define only one variable.
BindingIdentifier is a more specific type of the Identifier. We used the Identifier term before to explain the name of a variable. While Identifier also refers to the variable's name, BindingIdentifier is only used in the context of the declaration of variables (function or other data structure).
let variable = 42 // BindingIdentifier console.log(variable ) // Identifier
Now, let's go back to explaining the sentence's meaning.
BindingIdentifier may appear in more than one VariableDeclaration
In the same scope, we can create multiple variables with the same name using the var keyword, whilst all these "variables" reference only one variable.
var variable = 42 var variable = 66 var variable = 2015 // Execution context { // Environment Record { identifier: 'variable ' value: 2015 } [[OuterEnv]]: null }
It may appear we declared three variables with the BindingIdentifier variable, but we just reassigned the original variable variable twice. First, we reassigned it from 42 to 66, then from 66 to 2015
A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer's AssignmentExpression when the VariableDeclaration is executed, not when the variable is created.
The variable's value (Initializer) is assigned to it during the Execution Phase, not the Creation Phase of the Execution Context. Variables declared with the let and const keywords behave identically.
var variable = 42 // Global Execution Context Creation Phase { // Environment Record { identifier: variable value: undefined } [[OuterEnv]]: null } // Global Execution Context Execution Phase { // Environment Record { identifier: variable value: 42 } [[OuterEnv]]: null }
Diffrences
To sum up the article, I would like to highlight the following differences:
Scope
The first difference between variables created with var, let, and const keywords is how they are scoped. Variables created with let and const are scoped to the LexicalEnvironment, meaning they are available in the Environment Record of a block, function, or the Global Execution Context. In contrast, variables created with var are scoped to the VariableEnvironment, meaning they are only available in the Environment Record of a function or the Global Execution Context.
Creation Phase of the Execution Context
During the Execution Context's Creation Phase, variables created with let and const are uninitialised, whilst var variables are assigned the undefined value. The state of let and const being uninitialised is sometimes referenced as a Temporal Dead Zone or TDZ. Also, the behaviour of var being assigned the undefined value is usually known as “hoisting”.
Default Initializer value
Variables created with let and var keywords are assigned the undefined value if Initializer is not provided. Meanwhile, const variables must always have Initializer.
變數命名
使用 var 關鍵字建立的變數可以具有重複的名稱,因為它們都引用相同的變數。但是,let 和 const 變數不能有重複的名稱 - 這樣做會導致語法錯誤。
變數初始化器重新分配
使用 let 和 var 關鍵字建立的變數可以將其初始初始化器(值)重新指派給不同的初始化器。但是,const 變數不能重新分配其初始化器。
以上是透過 ECMAScript 標準的棱鏡了解 var、let 和 const 之間的差異。的詳細內容。更多資訊請關注PHP中文網其他相關文章!

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver Mac版
視覺化網頁開發工具