この記事は古いスクリプトを理解するためのものです
この記事の情報は、古いスクリプトを理解するのに役立ちます。
それは新しいコードを書く方法ではありません。
変数に関する最初の章で、変数宣言の 3 つの方法について説明しました。
let
const
var
var
宣言はlet
と似ています。ほとんどの場合、 let
var
に置き換えたり、その逆を行うことで、うまくいくことが期待できます。
var message = "こんにちは"; アラート(メッセージ); // こんにちは
しかし、内部的にはvar
非常に異なる獣であり、非常に古い時代に起源を持っています。一般に、現代のスクリプトでは使用されませんが、古いスクリプトには依然として潜んでいます。
そのようなスクリプトに対応する予定がない場合は、この章をスキップするか、延期しても構いません。
一方、古いスクリプトをvar
からlet
に移行する場合は、奇妙なエラーを避けるために違いを理解することが重要です。
var
で宣言された変数は、関数スコープまたはグローバルスコープのいずれかです。それらはブロックを通して見ることができます。
例えば:
if (true) { var テスト = true; // "let" の代わりに "var" を使用します } アラート(テスト); // true、変数は if 以降も存続します
var
コード ブロックを無視するため、グローバル変数test
が得られます。
var test
代わりにlet test
使用した場合、変数は次のif
にのみ内部で表示されます。
if (true) { テスト = true にします。 // 「let」を使用します } アラート(テスト); // ReferenceError: テストが定義されていません
ループについても同じです。var var
ブロックローカルまたはループローカルにすることはできません。
for (var i = 0; i < 10; i++) { 変数 1 = 1; // ... } アラート(i); // 10、ループの後に「i」が表示されます。これはグローバル変数です アラート(1); // 1、ループの後に「one」が表示されます。これはグローバル変数です
コード ブロックが関数内にある場合、 var
関数レベルの変数になります。
関数sayHi() { if (true) { var フレーズ = "こんにちは"; } アラート(フレーズ); // 動作します } こんにちは(); アラート(フレーズ); // ReferenceError: フレーズが定義されていません
ご覧のとおり、 var
if
、 for
、またはその他のコード ブロックを貫通します。それは、昔の JavaScript ではブロックに語彙環境がなかったためであり、 var
その名残です。
同じスコープ内でlet
を使用して同じ変数を 2 回宣言すると、エラーになります。
ユーザーに許可します。 ユーザーに許可します。 // SyntaxError: 'user' はすでに宣言されています
var
使用すると、変数を何度でも再宣言できます。すでに宣言されている変数とともにvar
使用した場合、それは単に無視されます。
var ユーザー = "ピート"; var ユーザー = "ジョン"; // この「var」は何もしません(すでに宣言されています) // ...エラーは発生しません アラート(ユーザー); // ジョン
var
宣言は、関数の開始時 (またはグローバルの場合はスクリプトの開始時) に処理されます。
つまり、 var
変数は、定義がどこにあるかに関係なく、関数の先頭から定義されます (定義が入れ子の関数内にないことを前提としています)。
したがって、このコードは次のようになります。
関数sayHi() { フレーズ = "こんにちは"; アラート(フレーズ); var フレーズ; } こんにちは();
…技術的にはこれと同じです (上記のvar phrase
を移動):
関数sayHi() { var フレーズ; フレーズ = "こんにちは"; アラート(フレーズ); } こんにちは();
…または、次のようにしても (コード ブロックは無視されることに注意してください):
関数sayHi() { フレーズ = "こんにちは"; // (*) if (偽) { var フレーズ; } アラート(フレーズ); } こんにちは();
すべてのvar
関数の先頭に「ホイスト」 (引き上げ) されるため、このような動作を「ホイスティング」 (引き上げ) とも呼びます。
したがって、上記の例では、 if (false)
ブランチは実行されませんが、それは問題ではありません。中のvar
関数の先頭で処理されるので、 (*)
の時点では変数が存在します。
宣言はホイストされますが、割り当てはホイストされません。
これは、次の例で最もよく分かります。
関数sayHi() { アラート(フレーズ); var フレーズ = "こんにちは"; } こんにちは();
var phrase = "Hello"
という行には 2 つのアクションが含まれています。
変数宣言var
変数の割り当て=
。
宣言は関数実行の開始時に処理されます (「ホイスト」) が、代入は常にそれが出現した場所で機能します。したがって、コードは基本的に次のように動作します。
関数sayHi() { var フレーズ; // 宣言は最初に機能します... アラート(フレーズ); // 未定義 フレーズ = "こんにちは"; // ...代入 - 実行がそれに到達したとき。 } こんにちは();
すべてのvar
宣言は関数の開始時に処理されるため、任意の場所で参照できます。ただし、変数は代入されるまで未定義です。
上記の両方の例では、変数phrase
が存在するため、 alert
エラーなしで実行されます。ただし、その値はまだ割り当てられていないため、 undefined
表示されます。
以前はvar
しか存在せず、ブロックレベルの可視性がなかったため、プログラマはそれをエミュレートする方法を発明しました。彼らが行ったことは、「即時に呼び出される関数式」 (IIFE と略称) と呼ばれていました。
これは今日では使用すべきものではありませんが、古いスクリプトでは見つけることができます。
IIFE は次のようになります。
(関数() { var message = "こんにちは"; アラート(メッセージ); // こんにちは })();
ここでは、関数式が作成され、すぐに呼び出されます。したがって、コードはすぐに実行され、独自のプライベート変数を持ちます。
関数式は括弧(function {...})
で囲まれています。これは、JavaScript エンジンがメイン コード内で"function"
に遭遇すると、それを関数宣言の始まりとして理解するためです。ただし、関数宣言には名前が必要なので、この種のコードではエラーが発生します。
// 関数を宣言してすぐに呼び出そうとします function() { // <-- SyntaxError: 関数ステートメントには関数名が必要です var message = "こんにちは"; アラート(メッセージ); // こんにちは }();
「よし、名前を追加しましょう」と言っても、JavaScript では関数宣言をすぐに呼び出すことができないため、それは機能しません。
// 以下の括弧が原因で構文エラーが発生します 関数 go() { }(); // <-- 関数宣言をすぐに呼び出すことはできません
したがって、関数を囲む括弧は、その関数が別の式のコンテキストで作成され、したがってそれが関数式であることを JavaScript に示すためのトリックです。名前は必要なく、すぐに呼び出すことができます。
JavaScript に関数式を意味することを伝える方法は、括弧以外にも存在します。
// IIFE の作成方法 (関数() { alert("関数を囲む括弧"); })(); (関数() { alert("全体を括弧で囲んでいます"); }()); !関数() { alert("ビット単位の NOT 演算子が式を開始します"); }(); +関数() { alert("単項プラスは式を開始します"); }();
上記のすべてのケースで、関数式を宣言し、それをすぐに実行します。もう一度注意してください。現在では、そのようなコードを記述する理由はありません。
let/const
と比較したvar
の主な違いは 2 つあります。
var
変数にはブロック スコープがありません。その可視性は現在の関数、または関数の外で宣言されている場合はグローバルにスコープされます。
var
宣言は関数の開始時 (グローバルの場合はスクリプトの開始時) に処理されます。
グローバル オブジェクトに関連する非常に小さな違いがもう 1 つあります。これについては次の章で説明します。
これらの違いにより、ほとんどの場合、 var
let
よりも悪くなります。ブロックレベル変数はとても素晴らしいものです。そのため、 let
ずっと前に標準に導入され、現在では ( const
と並んで) 変数を宣言する主要な方法となっています。