ECMAScript 標準のプリズムによる var、let、const の違い。

王林
リリース: 2024-08-24 22:30:32
オリジナル
349 人が閲覧しました

The Differences Between var, let, and const Through the Prism of the ECMAScript Standard.

多くの記事では、ホイスティングTemporal Dead Zone(TDZ)関数型ブロックスコープなどの用語を使用して、var、let、constの違いを説明していますが、その多くは標準を参照していません。 。これらの用語の中には、言語標準にさえ含まれていないものもあります。言語標準を参照せずにトピックを説明することはまったく問題ありません。ただし、JavaScript を包括的に理解するには ECMAScript 標準を理解することが重要であるため、もう少し詳しく知りたい人のためにこのトピックを参照して説明します。

ECMAScript

多くの組織には、MDN Web Docs、javascript.info などの JavaScript のリファレンスがあります。ただし、コンピュータ システムの標準化と文書化を唯一の目的とする標準化団体が 1 つあります。この組織は、この分野の信頼できる権威である Ecma International です。この組織は、ECMA-262 と呼ばれる標準を維持しており、これは標準を識別するための社内番号です。 Ecma International は、この標準を、一般に JavaScript と呼ばれる ECMAScript 汎用プログラミング言語のバックボーンとして定義しています。この標準を理解することは、言語自体を理解するための鍵となります。 2024 年 8 月現在の最新標準は、ECMAScript 2024としても知られる ECMAScript の第 15 版です。

実行コンテキスト

var、let、const の違いを理解するには、実行コンテキストの概念を理解することが不可欠です。

実行コンテキストは、ECMAScript 標準で定義された抽象構造です。現在のコードが実行される環境です。話を単純化するために、グローバル実行コンテキストと関数実行コンテキストが存在すると仮定します。

リーリー

コードの実行を追跡するために、実行コンテキストには状態コンポーネントとして知られるいくつかのコンポーネントが含まれています。このうち、LexicalEnvironment と VariableEnvironment は、var、let、const キーワードの動作を理解する際に重要です。

LexicalEnvironment と VariableEnvironment は両方とも環境レコードです。環境レコードは、ECMAScript 標準で定義された抽象データ構造でもあります。これにより、識別子の特定の変数および関数への関連付けが確立されます。識別子は、JavaScript の値、関数、クラス、その他のデータ構造を参照する名前です。次の例では、variable = 42 とします。variable は、数値 42 の値を格納する変数の名前 (識別子) です。

コードが実行されるたびに、実行コンテキストによって新しい環境レコードが作成されます。識別子の保存に加えて、環境レコードには [[OuterEnv]] フィールド (null または外部環境レコードへの参照) があります。

前の例の実行コンテキストと環境レコードは、次のようにグラフで表すことができます:

リーリー

実行コンテキストについて覚えておくべきもう 1 つの重要な点は、作成フェーズ実行フェーズという 2 つの異なるフェーズがあることです。これら 2 つのフェーズは、var と let または const の違いを理解する上で非常に重要です。
Let 宣言と Const 宣言

ECMAScript 標準の段落 14.3.1 Let および Const 宣言では次のように述べられています:

let 宣言と const 宣言は、実行中の実行コンテキストの LexicalEnvironment をスコープとする変数を定義します。変数は、変数を含む環境レコードがインスタンス化されるときに作成されますが、変数の LexicalBinding が評価されるまではいかなる方法でもアクセスできません。 Initializer を使用した LexicalBinding によって定義された変数には、変数の作成時ではなく、LexicalBinding の評価時に Initializer の AssignmentExpression の値が割り当てられます。 let 宣言内の LexicalBinding に Initializer がない場合、LexicalBinding が評価されるときに、変数には未定義の値が割り当てられます。

この文を理解するために、一文ずつ説明します。

let 宣言と const 宣言は、実行中の実行コンテキストの LexicalEnvironment をスコープとする変数を定義します。

これは、 let または const キーワードを使用して作成された変数のスコープが、それらが定義されたブロックに限定されることを意味します。コード ブロックは、中括弧内の任意の JavaScript コードです。

リーリー


変数は、変数を含む環境レコードがインスタンス化されるときに作成されますが、変数の LexicalBinding が評価されるまではいかなる方法でもアクセスできません。

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
ログイン後にコピー

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 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 }
ログイン後にコピー

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'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”.

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.

Penamaan pembolehubah

Pembolehubah yang dibuat dengan kata kunci var boleh mempunyai nama pendua kerana semuanya merujuk pembolehubah yang sama. Walau bagaimanapun, pembolehubah let dan const tidak boleh mempunyai nama pendua — berbuat demikian mengakibatkan Ralat Sintaks.

Penugasan semula Pembolehubah

Pembolehubah yang dibuat dengan kata kunci let dan var boleh menetapkan semula Pemula (nilai) awalnya kepada yang berbeza. Tetapi, pembolehubah const tidak boleh menetapkan Initializer mereka semula.

以上がECMAScript 標準のプリズムによる var、let、const の違い。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!