MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - 変数宣言のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - 変数宣言
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == JavaScriptにおける変数宣言には、<code>var</code>、<code>let</code>、<code>const</code> の3つのキーワードが存在する。<br> <br> <code>var</code> はJavaScript誕生当初から存在するキーワードであり、関数スコープまたはグローバルスコープで変数を宣言する。<br> 一方、<code>let</code> と <code>const</code> は2015年に策定されたES2015 (ES6) で新たに導入されたキーワードであり、ブロックスコープを持つ。<br> <br> これら3つのキーワードは、スコープ、ホイスティング (変数の巻き上げ)、再宣言・再代入の可否という3つの観点で大きく異なる。<br> <code>var</code> はブロックスコープを持たないため、ループや条件分岐の内部で宣言した変数が外部に漏れ出し、予期しない動作を引き起こす問題がある。<br> また、宣言前のアクセスが <code>undefined</code> を返すという挙動も、バグの温床となりやすい。<br> <br> <code>let</code> と <code>const</code> はこれらの問題を解消するために設計されており、現代的なJavaScript開発ではこの2つを用いることが標準となっている。<br> 原則として <code>const</code> をデフォルトの選択肢として使用し、再代入が必要な場面でのみ <code>let</code> を使用することが広く推奨されている。<br> <br> <u><code>var</code> は新規開発では使用せず、既存のレガシーコードを読み解く時の知識として理解しておくことが求められる。</u><br> <br><br> == let == ==== 基本的な使用方法 ==== <code>let</code> はブロックスコープを持ち、再代入可能な変数を宣言するキーワードである。<br> 初期値なしで宣言することも、宣言と同時に初期値を設定することも可能である。<br> <br> <syntaxhighlight lang="javascript"> let name; let name = value; let name1 = value1, name2 = value2; </syntaxhighlight> <br> 使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> let count = 0; count = 1; // 再代入可能 console.log(count); // 1 let message; message = "Hello, World!"; console.log(message); // "Hello, World!" </syntaxhighlight> <br> ==== 再代入と再宣言 ==== <code>let</code> は再代入が可能であるが、同一スコープ内での再宣言は <u>SyntaxError</u> となる。<br> <br> <syntaxhighlight lang="javascript"> let x = 1; x = 2; // OK : 再代入は可能 console.log(x); // 2 let x = 3; // SyntaxError: Identifier 'x' has already been declared </syntaxhighlight> <br> ただし、異なるブロックスコープでは同じ名前の変数を宣言できる。<br> <br> <syntaxhighlight lang="javascript"> const x = "outer"; { const x = "inner"; // OK : 別のブロックスコープ console.log(x); // "inner" } console.log(x); // "outer" </syntaxhighlight> <br> ==== ブロックスコープ ==== <code>let</code> はブロックスコープを持つ。<br> <code>if</code>、<code>for</code>、<code>while</code>、<code>{}</code> 等のブロック内で宣言した変数は、そのブロック外からアクセスできない。<br> <br> <syntaxhighlight lang="javascript"> function letTest() { let x = 1; { let x = 2; // 別のブロックスコープの変数 console.log(x); // 2 } console.log(x); // 1 } for (let i = 0; i < 3; i++) { console.log(i); // 0, 1, 2 } console.log(i); // ReferenceError: i is not defined </syntaxhighlight> <br> また、<code>let</code> はトップレベルで宣言してもグローバルオブジェクト (<code>globalThis</code>) のプロパティにはならない。<br> <br> <syntaxhighlight lang="javascript"> var globalVar = "var"; let globalLet = "let"; console.log(globalThis.globalVar); // "var" console.log(globalThis.globalLet); // undefined </syntaxhighlight> <br><br> == const == ==== 基本的な使用方法 ==== <code>const</code> はブロックスコープを持ち、再代入不可の定数を宣言するキーワードである。<br> <br> <code>const</code> は宣言と同時に必ず初期値を指定しなければならない。<br> 初期化を省略すると <u>SyntaxError</u> となる。<br> <br> <syntaxhighlight lang="javascript"> const name = value; const PI = 3.14159; const MAX_SIZE = 100; const APP_NAME = "MyApp"; const FOO; // SyntaxError: Missing initializer in const declaration </syntaxhighlight> <br> ==== 再代入の禁止 ==== <code>const</code> で宣言した変数への再代入は <u>TypeError</u> となる。<br> <br> <syntaxhighlight lang="javascript"> const PI = 3.14159; PI = 3.14; // TypeError: Assignment to constant variable. </syntaxhighlight> <br> 同一スコープ内での再宣言も <u>SyntaxError</u> となる。<br> <br> <syntaxhighlight lang="javascript"> const y = 1; const y = 2; // SyntaxError: Identifier 'y' has already been declared </syntaxhighlight> <br> ==== constとオブジェクト (参照の不変性) ==== <code>const</code> が保証するのは変数への参照の不変性のみである。<br> 参照先のオブジェクトや配列の中身 (プロパティや要素) は変更できる。<br> <br> <syntaxhighlight lang="javascript"> const obj = { name: "Alice", age: 30 }; obj.name = "Bob"; // OK : プロパティの変更は可能 obj.age = 31; // OK obj = { name: "Charlie" }; // TypeError - 再代入は不可 const arr = [1, 2, 3]; arr[0] = 99; // OK : 要素の変更は可能 arr.push(4); // OK : メソッドの呼び出しは可能 arr = [4, 5, 6]; // TypeError : 再代入は不可 </syntaxhighlight> <br> ===== Object.freeze ===== オブジェクトの中身を変更させたくない場合は、<code>Object.freeze()</code> を使用する。<br> <code>Object.freeze()</code> を適用すると、プロパティの追加・削除・変更がすべて禁止される。<br> <br> ただし、<code>Object.freeze()</code> は浅いフリーズ (Shallow Freeze) であるため、ネストされたオブジェクトや配列は保護されない。<br> <br> <syntaxhighlight lang="javascript"> const frozenObj = Object.freeze({ name: "Alice", age: 30 }); frozenObj.name = "Bob"; // 失敗 (strict mode では TypeError) frozenObj.newProp = "value"; // 失敗 : プロパティの追加も不可 console.log(frozenObj.name); // "Alice" // 浅いフリーズの制限: ネストされたオブジェクトは変更可能 const obj = Object.freeze({ level1: { level2: "value" } }); obj.level1.level2 = "newValue"; // OK : ネストされたオブジェクトは保護されない console.log(obj.level1.level2); // "newValue" </syntaxhighlight> <br> ネストされたオブジェクトも含めて完全に不変にするには、再帰的にフリーズする深いフリーズ (Deep Freeze) を実装する。<br> <br> <syntaxhighlight lang="javascript"> function deepFreeze(object) { const propNames = Reflect.ownKeys(object); for (const name of propNames) { const value = object[name]; if ((value && typeof value === "object") || typeof value === "function") { deepFreeze(value); } } return Object.freeze(object); } const deepFrozenObj = deepFreeze({ level1: { level2: "value" } }); deepFrozenObj.level1.level2 = "newValue"; // 失敗 console.log(deepFrozenObj.level1.level2); // "value" </syntaxhighlight> <br> ==== ブロックスコープ ==== <code>const</code> のスコープは、<code>let</code> と同様にブロックスコープである。<br> <br> <syntaxhighlight lang="javascript"> const MY_FAV = 7; if (MY_FAV === 7) { const MY_FAV = 20; // 新しいブロックスコープ内の別の変数 console.log(MY_FAV); // 20 } console.log(MY_FAV); // 7 </syntaxhighlight> <br><br> == var == ==== 基本的な使用方法 ==== <code>var</code> は関数スコープまたはグローバルスコープで変数を宣言するキーワードである。<br> <br> ES2015以前から存在する古いキーワードであり、全てのWebブラウザで広くサポートされている。<br> <br> <syntaxhighlight lang="javascript"> var name; var name = value; var name1 = value1, name2 = value2; </syntaxhighlight> <br> <code>var</code> は同一スコープ内での再宣言が可能であり、エラーにはならない。<br> <br> <syntaxhighlight lang="javascript"> var a = 1; var a = 2; // エラーにならない console.log(a); // 2 </syntaxhighlight> <br> ==== 関数スコープ ==== <code>var</code> は関数スコープを持つ。<br> <br> ブロック (<code>if</code>、<code>for</code>、<code>while</code> 等) は、<code>var</code> のスコープを形成しない。<br> <br> ブロック内で宣言した <code>var</code> 変数は、そのブロックの外からもアクセスできる。<br> <br> <syntaxhighlight lang="javascript"> function functionScope() { if (true) { var x = "inside"; } console.log(x); // "inside" - if ブロック外でもアクセス可能 } functionScope(); for (var i = 0; i < 3; i++) { console.log(i); // 0, 1, 2 } console.log(i); // 3 - ループ外でもアクセス可能 </syntaxhighlight> <br> また、トップレベルでの <code>var</code> 宣言はグローバルオブジェクト (<code>globalThis</code>) のプロパティとして追加される。<br> <br> <syntaxhighlight lang="javascript"> var x = 1; console.log(globalThis.x); // 1 delete x; // 削除できない (strict modeでは、TypeError) console.log(globalThis.x); // 1 </syntaxhighlight> <br> ==== varの問題点 ==== <code>var</code> の使用はいくつかの問題を引き起こすため、現代的なJavaScript開発では使用が推奨されない。<br> <br> ループでのクロージャ問題を以下に示す。<br> <code>var</code> はブロックスコープを持たないため、ループ内でクロージャを使用すると、全ての関数が同じ変数を参照してしまう。<br> <br> <syntaxhighlight lang="javascript"> // 問題のあるコード (varを使用) var funcs = []; for (var i = 0; i < 3; i++) { funcs.push(function() { console.log(i); }); } funcs[0](); // 3 (期待値: 0) funcs[1](); // 3 (期待値: 1) funcs[2](); // 3 (期待値: 2) // 解決策: letを使用 let funcs2 = []; for (let i = 0; i < 3; i++) { funcs2.push(function() { console.log(i); }); } funcs2[0](); // 0 funcs2[1](); // 1 funcs2[2](); // 2 </syntaxhighlight> <br> 変数の巻き上げによる予期しない動作を以下に示す。<br> <code>var</code> はホイスティングにより <code>undefined</code> で初期化されるため、宣言前にアクセスしてもエラーにならず、デバッグが困難になる。<br> <br> <syntaxhighlight lang="javascript"> function unexpected() { console.log(x); // undefined (エラーにならない) if (false) { var x = 5; // このブロックは実行されないが、宣言は巻き上げられる } console.log(x); // undefined } </syntaxhighlight> <br> グローバルオブジェクトへのプロパティ追加を以下に示す。<br> トップレベルの <code>var</code> 宣言はグローバルオブジェクトにプロパティとして追加されて、グローバル汚染を引き起こす可能性がある。<br> <br> <syntaxhighlight lang="javascript"> var message = "global"; console.log(window.message); // "global" : ブラウザ環境 let safe = "no pollution"; console.log(window.safe); // undefined </syntaxhighlight> <br><br> == スコープの比較 == 下表に、<code>let</code>、<code>const</code>、<code>var</code> のスコープの違いを示す。<br> <br> <center> {| class="wikitable" |+ スコープ比較表 ! キーワード !! スコープの種類 !! ブロック内での宣言 !! グローバルオブジェクトへの追加 |- | <code>var</code> || 関数スコープ / グローバルスコープ || ブロック外からアクセス可能 || あり |- | <code>let</code> || ブロックスコープ || ブロック外からアクセス不可 || なし |- | <code>const</code> || ブロックスコープ || ブロック外からアクセス不可 || なし |} </center> <br> スコープの挙動を示すコード例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // var: 関数スコープ function testVar() { if (true) { var x = "var"; } console.log(x); // "var": アクセス可能 } // let: ブロックスコープ function testLet() { if (true) { let y = "let"; } console.log(y); // ReferenceError: y is not defined } // const: ブロックスコープ function testConst() { if (true) { const z = "const"; } console.log(z); // ReferenceError: z is not defined } </syntaxhighlight> <br><br> == ホイスティング == ホイスティング (変数の巻き上げ) とは、JavaScriptエンジンがプログラムの実行前に変数・関数・クラスの宣言を処理する仕組みのことである。<br> <br> 変数の使用箇所よりも前に宣言が処理されるが、<code>var</code> と <code>let</code> / <code>const</code> では挙動が大きく異なる。<br> <br> ==== varのホイスティング ==== <code>var</code> の宣言は、関数またはグローバルスコープの先頭に巻き上げられ、自動的に <code>undefined</code> で初期化される。<br> そのため、宣言前に変数にアクセスしてもエラーにはならず、<code>undefined</code> が返される。<br> <br> <syntaxhighlight lang="javascript"> console.log(x); // undefined (エラーではない) var x = 5; console.log(x); // 5 </syntaxhighlight> <br> 上記のコードは、JavaScriptエンジンによって内部的に以下のように処理される。<br> <br> <syntaxhighlight lang="javascript"> var x; // ホイスティング: undefinedで初期化 console.log(x); // undefined x = 5; // 代入 console.log(x); // 5 </syntaxhighlight> <br> ==== let / constのホイスティングとTDZ ==== <code>let</code> と <code>const</code> も技術的にはホイストされるが、<code>undefined</code> では初期化されない。<br> 代わりに、ブロックの開始から宣言文に到達するまでの間、Temporal Dead Zone (TDZ : 一時的デッドゾーン) と呼ばれる状態に置かれる。<br> <br> TDZの期間中に変数にアクセスすると <u>ReferenceError</u> が発生する。<br> <br> <syntaxhighlight lang="javascript"> // letのTDZ { console.log(x); // ReferenceError: Cannot access 'x' before initialization let x = 5; } // constのTDZ { console.log(y); // ReferenceError: Cannot access 'y' before initialization const y = 10; } // varとの比較 { console.log(z); // undefined (エラーではない) var z = 15; } </syntaxhighlight> <br> TDZは、外側のスコープに同名の変数が存在する場合にも注意が必要である。<br> 内側のブロックで <code>let</code> / <code>const</code> を宣言すると、そのブロック全体がTDZの影響を受ける。<br> <br> <syntaxhighlight lang="javascript"> function test() { var foo = 33; if (foo) { let foo = foo + 55; // ReferenceError // 右辺の foo は内側のブロックの TDZ 内にあるためアクセスできない } } </syntaxhighlight> <br> 下表に、ホイスティングの挙動比較を示す。<br> <br> <center> {| class="wikitable" |+ ホイスティング挙動比較表 ! キーワード !! ホイスティング !! 初期値 !! 宣言前のアクセス |- | <code>var</code> || あり || undefined || <u>undefined</u> を返す |- | <code>let</code> || あり (TDZ) || 初期化されない || <u>ReferenceError</u> |- | <code>const</code> || あり (TDZ) || 初期化されない || <u>ReferenceError</u> |} </center> <br><br> == let / const / var の比較 == <code>let</code>、<code>const</code>、<code>var</code> の特性を総合的に比較した表を以下に示す。<br> <br> <center> {| class="wikitable" |+ let / const / var の総合比較表 ! 特性 !! <code>var</code> !! <code>let</code> !! <code>const</code> |- | スコープ || 関数 / グローバル || ブロック || ブロック |- | ホイスティング || あり (undefined で初期化) || あり (TDZ) || あり (TDZ) |- | 宣言前のアクセス || undefined を返す || ReferenceError || ReferenceError |- | 再代入 || 可能 || 可能 || 不可 |- | 再宣言 || 可能 || 不可 || 不可 |- | 初期化の省略 || 可能 || 可能 || 不可 (SyntaxError) |- | グローバルオブジェクトへの追加 || あり || なし || なし |- | 導入バージョン || ES1 (初期) || ES2015 (ES6) || ES2015 (ES6) |} </center> <br><br> == 推奨される使用方法 == <u>現代的なJavaScript開発では、以下の原則に従って変数宣言のキーワードを選択することが推奨される。</u><br> <br> * <u><code>const</code> をデフォルトとして使用する。</u> *: 変数が再代入される可能性がない場合は常に <code>const</code> を使用する。 *: これにより、意図しない再代入を防ぎ、コードの意図を明確に表現できる。 *: 多くのスタイルガイド (Airbnb、Google等) でも <code>const</code> の優先使用が推奨されている。 *: <br> * <u>再代入が必要な場合のみ、<code>let</code> を使用する</u> *: カウンタ変数、条件によって値が変わる変数等、再代入が必要な場合にのみ <code>let</code> を使用する。 *: <br> * <u><code>var</code> は使用しない。</u> *: 新規開発では <code>var</code> を使用しない。 *: レガシーコードや古い環境向けのコードを読む際の知識として理解しておく。 <br> 推奨される使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> // const をデフォルトとして使用 const PI = 3.14159; const API_URL = "https://api.example.com"; const config = { debug: true, timeout: 5000 }; // 再代入が必要な場面でletを使用 let count = 0; while (count < 10) { count++; } let result; if (someCondition) { result = "value1"; } else { result = "value2"; } // ループのカウンタは let を使用 for (let i = 0; i < arr.length; i++) { console.log(arr[i]); } </syntaxhighlight> <br> ESLintを使用している場合、<u>no-var</u> ルールで <code>var</code> の使用を禁止し、<u>prefer-const</u> ルールで <code>const</code> の優先使用を自動的に検出することができる。<br> <br><br> == 関連情報 == * [[JavaScriptの基礎 - プリミティブ型]] *: 7つのプリミティブ型、typeof演算子、型の自動変換 * [[JavaScriptの基礎 - 数値と算術演算子]] *: Number型の特性、算術演算子、Mathオブジェクト、BigInt * [[JavaScriptの基礎 - 文字列]] *: テンプレートリテラル、文字列メソッド、タグ付きテンプレートリテラル * [[JavaScriptの基礎 - 比較演算子と論理演算子]] *: ===/==の違い、短絡評価、Null合体演算子、オプショナルチェーン <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - 変数宣言
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse