3.

JavaScript の変数宣言|const と let の使い分け・var を避ける理由

編集
この記事の要点
  • 既定は const。再代入が要るときだけ letvar は使わない
  • const再代入を禁じるだけ。オブジェクトや配列の中身は書き換えられる
  • let / constブロックスコープvar は関数スコープで iffor を貫通する
  • 宣言前に触ると ReferenceError(TDZ)。varundefined になって気づけない
  • 宣言を省くと暗黙のグローバル変数になる。strict モードならエラーで止まる

3 つの宣言

const name = "田中";     // 再代入できない
let count = 0;           // 再代入できる
var old = 1;             // 古い書き方

count = 1;               // OK
// name = "鈴木";        // TypeError: Assignment to constant variable.

const price;             // SyntaxError: Missing initializer in const declaration
let x;                   // OK(undefined になる)
スコープ再代入再宣言宣言前の参照
constブロック××ReferenceError
letブロック×ReferenceError
var関数undefined

const は中身を凍らせない

const user = { name: "田中" };
user.name = "鈴木";        // OK(プロパティの書き換えは許される)
user.age = 30;             // OK
// user = {};              // TypeError(再代入は不可)

const items = [1, 2];
items.push(3);             // OK
console.log(items);        // [1, 2, 3]

// 中身も変えたくないなら freeze する
const config = Object.freeze({ debug: false });
config.debug = true;       // 黙って無視される(strict モードでは TypeError)
console.log(config.debug); // false

// freeze は浅い。入れ子は凍らない
const nested = Object.freeze({ inner: { a: 1 } });
nested.inner.a = 2;        // 通ってしまう

const が保証するのは「この名前が別のものを指すようにならない」ことだけです。だからこそ、配列やオブジェクトでも積極的に const を使って構いません。

スコープの違い

if (true) {
  var a = 1;
  let b = 2;
}
console.log(a);   // 1          ← ブロックの外に漏れる
console.log(b);   // ReferenceError: b is not defined

function f() {
  var x = 1;
}
console.log(x);   // ReferenceError   var も関数の外には出ない

ループでの違い(最も差が出る場面)

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3 3 3   ← i は 1 つしかなく、実行時には 3 になっている

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 0 1 2   ← 繰り返しごとに新しい i が作られる

イベントハンドラーを for の中で登録すると必ず出てくる問題です。let を使えば期待どおりに動きます。

巻き上げと TDZ

console.log(a);   // undefined   ← エラーにならないので気づけない
var a = 1;

console.log(b);   // ReferenceError: Cannot access 'b' before initialization
let b = 1;

// 関数宣言は全体が巻き上げられる
greet();          // 動く
function greet() { console.log("hi"); }

// 関数式は巻き上げられない
greet2();         // TypeError: greet2 is not a function
var greet2 = function () {};

let / const は宣言までの区間で一時的死角(TDZ)に入り、触ると即エラーになります。var のように undefined が返って先へ進んでしまうより、はるかに安全です。

宣言を忘れると

function f() {
  total = 100;      // 宣言していない
}
f();
console.log(window.total);   // 100   ← グローバルに漏れている

// strict モード(モジュールでは自動)なら止まる
"use strict";
function g() {
  total = 100;      // ReferenceError: total is not defined
}

他のスクリプトと同じ名前を使うと、互いに値を壊し合います。type="module" で読み込む"use strict" を書いて、この事故を防いでください。

分割代入

const user = { name: "田中", age: 30, city: "東京" };

const { name, age } = user;              // 必要なものだけ取り出す
const { city: town } = user;             // 別名で受ける
const { zip = "000-0000" } = user;       // 無ければ既定値
const { name: n, ...rest } = user;       // 残りをまとめる

const [first, second = 0, ...others] = [1];
console.log(first, second, others);      // 1 0 []

// 入れ替え
let a = 1, b = 2;
[a, b] = [b, a];

// 関数の引数でも使える
function show({ name, age = 0 }) {
  console.log(name, age);
}
show(user);

存在しないプロパティは undefined になります。ネストした分割代入で途中が undefined だと TypeError になるので、既定値を用意してください。

const res = {};
// const { data: { items } } = res;          // TypeError
const { data: { items } = {} } = res;        // OK(items は undefined)

命名

対象書き方
変数・関数camelCaseuserName getTotal
クラス・コンストラクタPascalCaseUserProfile
定数(変わらない設定値)UPPER_SNAKEMAX_RETRY
真偽値is / has / can で始めるisActive hasError
DOM 要素用途が分かる名前submitButtonel だけにしない)
// 使える文字: 英数字 _ $(先頭は数字以外)
const $el = document.body;     // jQuery の慣習で $ を付けることがある
const 名前 = "田中";            // 動くが避ける

// 予約語は使えない
// const class = 1;            // SyntaxError

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 記述方法
  2. コメント
  3. 変数の宣言
  4. 関数
  5. 演算子
  6. 条件文
  7. 配列
  8. 連想配列
  9. ループ処理
  10. 非同期処理
  11. 同期処理
  12. 確認ウィンドウを表示する方法
  13. 文字の置換
  14. base urlを取得する方法
  15. formのsubmit前にjavascriptを呼び出す方法
  16. undefinedのイコール判定
  17. Javascript のみで form を post で submit する方法

最近更新/作成されたページ