Banyak artikel menerangkan perbezaan antara var, let dan const menggunakan istilah sepertihoisting,Temporal Dead Zone(TDZ),functionaldanskop sekatan., selalunya tanpa skop blok., dsb. . Beberapa istilah tersebut tidak termasuk dalam standard bahasa. Tidak mengapa untuk menerangkan topik tanpa merujuk standard bahasa. Walau bagaimanapun, saya menerangkan topik tersebut dengan merujuknya kepada mereka yang ingin menggali lebih mendalam, kerana memahami standard ECMAScript adalah penting untuk memahami JavaScript yang komprehensif.
Banyak organisasi mempunyai rujukan mereka untuk JavaScript, seperti MDN Web Docs, javascript.info dan lain-lain. Walau bagaimanapun, terdapat satu organisasi piawaian yang tujuan utamanya adalah untuk menyeragamkan dan mendokumentasikan sistem komputer. Organisasi ini ialah Ecma International, pihak berkuasa yang boleh dipercayai dalam bidang ini. Organisasi ini mengekalkan piawaian yang dipanggil ECMA-262, nombor dalaman syarikat untuk mengenal pasti piawaian tersebut. Ecma International mentakrifkan standard ini sebagai tulang belakang bahasa pengaturcaraan tujuan umum ECMAScript, yang biasa kami panggil JavaScript. Memahami standard ini adalah kunci untuk memahami bahasa itu sendiri. Standard terkini setakat Ogos 2024 ialah edisi ke-15 ECMAScript, juga dikenali sebagaiECMAScript 2024.
Untuk memahami perbezaan antara var, let dan const, adalah penting untuk memahami konsep Konteks Pelaksanaan.
Konteks Pelaksanaan ialah struktur abstrak yang ditakrifkan dalam standard ECMAScript. Ia adalah persekitaran di mana kod semasa dilaksanakan. Untuk memudahkan perkara, kita boleh menganggap Konteks Pelaksanaan Global dan Konteks Pelaksanaan Berfungsi wujud.
// 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()
Untuk menjejaki pelaksanaan kod, Konteks Pelaksanaan termasuk beberapa komponen, dikenali sebagai komponen keadaan. Antaranya, LexicalEnvironment dan VariableEnvironment adalah penting apabila memahami gelagat kata kunci var, let dan const.
Kedua-dua LexicalEnvironment dan VariableEnvironment adalah Rekod Persekitaran. Rekod Persekitaran juga merupakan struktur data abstrak yang ditakrifkan dalam standard ECMAScript. Ia mewujudkan perkaitan Pengecam kepada pembolehubah dan fungsi tertentu. Pengecam ialah nama yang merujuk nilai, fungsi, kelas dan struktur data lain dalam JavaScript. Dalam contoh berikut, biarkan pembolehubah = 42, pembolehubah ialah nama pembolehubah (Pengecam) yang menyimpan nilai nombor 42.
Setiap kali kod dilaksanakan, Konteks Perlaksanaan mencipta Rekod Persekitaran baharu. Selain menyimpan Rekod Persekitaran Pengecam mempunyai medan [[OuterEnv]], sama ada batal atau rujukan kepada Rekod Persekitaran luar.
Secara grafik, Konteks Pelaksanaan dan Rekod Persekitaran daripada contoh sebelumnya boleh diwakili seperti ini:
// 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 }
Satu lagi perkara penting yang perlu diingat tentang Konteks Pelaksanaan ialah ia mempunyai dua fasa yang berbeza:Fasa PenciptaandanFasa Pelaksanaan. Kedua-dua fasa ini penting dalam memahami perbezaan antara var dan let atau const.
Dalam perenggan 14.3.1 Let and Const Deklarasi standard ECMAScript perkara berikut dinyatakan:
pengisytiharan let dan const mentakrifkan pembolehubah yang diskop kepada LexicalEnvironment konteks pelaksanaan yang sedang berjalan. Pembolehubah dicipta apabila Rekod Persekitaran yang mengandunginya digunakan tetapi tidak boleh diakses dalam apa jua cara sehingga LexicalBinding pembolehubah dinilai. Pembolehubah yang ditakrifkan oleh LexicalBinding dengan Initializer diberikan nilai Initializer's AssignmentExpression apabila LexicalBinding dinilai, bukan apabila pembolehubah dicipta. Jika LexicalBinding dalam perisytiharan let tidak mempunyai Initializer, pembolehubah diberikan nilai yang tidak ditentukan apabila LexicalBinding dinilai.
Untuk memahami kenyataan ini, saya akan menerangkannya ayat demi ayat.
pengisytiharan biarkan dan const mentakrifkan pembolehubah yang diskop kepada Persekitaran LexicalEnvironment konteks pelaksanaan yang sedang berjalan.
Ini bermakna pembolehubah yang dibuat dengan kata kunci let atau const diskop ke blok di mana ia ditakrifkan. Blok kod ialah sebarang kod JavaScript di dalam pendakap kerinting.
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 }
Pembolehubah dicipta apabila Rekod Persekitaran yang mengandunginya dibuat seketika tetapi mungkin tidak boleh diakses dalam apa-apa cara sehingga LexicalBinding pembolehubah itu dinilai.
As previously mentioned, the Execution Context has two phases. This statement means that during theCreation Phaseof 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(theExecution Phaseof the Execution context). The state of the variable being uninitialised is also known as aTemporary 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 theExecution Phaseof 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 theExecution Phaseof 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:LetOrConstBindingList;
- 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:BindingIdentifierInitializer
- It is a Syntax Error if Initializer is not present and IsConstantDeclaration of the LexicalDeclaration containing this LexicalBinding is true.
LetOrConstis a grammar rule which specifies that variable declarations can start with the let or const keywords.
BindingListis a list of variables declared with let or const keywords. We could imagineBindingListas 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
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 theCreation Phaseof the Execution Context variables are assigned the undefined value. The process of assigning the undefined to a variable during theCreation Phaseis often referred to as"hoisting"ordeclaration hoisting. It is worth mentioning that the terms"hoisting"ordeclaration hoistingare 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 theCreation Phaseof 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 theExecution 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 }
To sum up the article, I would like to highlight the following differences:
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.
During the Execution Context'sCreation 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 aTemporal Dead Zone or TDZ. Also, the behaviour of var being assigned the undefined value is usually known as“hoisting”.
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 變數不能重新分配其初始化器。
Das obige ist der detaillierte Inhalt vonDie Unterschiede zwischen var, let und const durch das Prisma des ECMAScript-Standards.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!