目次⇒ 1.変数と定数とは? 2.varについて 3.命名規則と予約語 4.なぜ、まずconst? 5.再代入と再宣言 6.constでも中身は変更できる 7.オブジェクトの固定方法 補足:ジェネレーター関数 TOP
この記事では、変数と定数とは何かについて詳しく解説します。
解説:
変数と定数は、プログラム上でデータ入れるための箱のことです。
この箱には名前をつけて(変数宣言)、中身(データ)を1つ入れることができます。
以下に例を示します。
// 変数の宣言
let a = 1; // a という変数に 1 を入れる
let b = 3; // b という変数に 3 を入れる
// 定数の宣言
const c = 5; // c という定数に 5 を入れる
const d = 7; // d という定数に 7 を入れる
変数 (let) は、あとから中身を変更(再代入)できますが、
定数 (const) は、あとから中身を変更(再代入)できません。
// 代入時の挙動の違い
let x = 10;
x = 20; // OK
const y = 30;
y = 40; // エラー(再代入できない)
ちなみに、意外と知らない人も多いですが、constで定義した配列やオブジェクトの中身は変更できます!
このあたりは、最初に戸惑うポイントなので、ぜひおさえておきましょう!
// 配列やオブジェクトの中身の変更例
// 配列の例
const list = [1, 2, 3];
list.push(4); // OK 配列の中身の一部の変更は、できます
list[0] = 5; // OK 配列の中身の一部の変更は、できます
list = [5, 6]; // NG 配列を丸々代入は、できません
// オブジェクトの例
const table = [
{ x: 10, y: 20 },
{ x: 30, y: 40 },
];
table[0].x = 50; // OK オブジェクトの中身の一部の変更は、できます
table = [ { x: 100, y: 200 } ]; // NG オブジェクトを丸々代入は、できません
基本的には「const」を使用して、必要な時に「let」を使用するのが安全な書き方と言えます。
解説:
varは、letと同じJavaScriptで使用できる変数のことです。
ただ、letとは違う良くない点がたくさんあるため、現在では、使用しないことが推奨されます。
※ES2015で、「let」や「const」が導入されるまでは、「var」しか使えませんでした。
1つ目の理由は、変数の宣言を忘れても自動的にグローバル変数になってしまう点にあります。
// 宣言の省略例
greet = "Hello World!"; // 変数の宣言を省略
console.log(greet); // 結果 : Hello World!
// グローバル変数とは?
scope = "global"; // varやletを付けずに変数を使用している
function value() {
scope = "local"; // グローバル変数の値を上書きしてしまう
console.log(scope);
}
value(); // 結果 : local
console.log(scope); // 結果 : local
// 変数名の重複を許してしまう
var value1 = 1;
var value1 = 2; // 変数valueの値の更新
console.log(value1); // 結果 : 2
let value2 = 1;
let value2 = 2; // 同名の変数のため宣言が許可されない
console.log(value2); // 結果 : エラー
// ブロックスコープの認識
if (true) {
var value1 = 1; // ブロックスコープは認識されていない
}
console.log(value1); // 結果 : 1
if (true) {
let value2 = 2; // ブロックスコープは認識されている
}
console.log(value2); // 結果 : エラー
解説:
変数と定数の名前に使用できる文字は、以下の通りです。
英字「a~z、A~Z」
数字「0~9」
_(アンダースコア)
上記の文字(アルファベット・数字・アンダースコア)のみを使用していれば、変数名や関数名として自由に命名できます。
ただし、数字から始まる名前は文法上認められておらず、構文エラー(SyntaxError)になります。
○:array1
✕:1array
JavaScriptでは、変数や関数名に使用できない「予約語」が多数定義されています。以下はその代表的な例です。
予約語は、JavaScript内で何らかの他の処理に使用している単語で、これを命名に使用するとエラーとなります。
以下を命名の際に、誤って使わないように気を付けましょう!
| 予約語 | 用法の説明 |
|---|---|
| await | 非同期関数内でPromiseの結果を待つ ※only inside async functions and modules |
| break | ループやswitch文から抜け出す |
| case | switch文の中で条件を指定する |
| catch | 例外が投げられた時に処理をキャッチする |
| class | クラスを宣言する |
| const | 再代入不可能な定数を宣言する |
| continue | ループの次の反復へ進む |
| debugger | デバッグ中に実行を一時停止する |
| default | switch文で、いずれのcaseにも一致しない場合の処理を定義する |
| delete | オブジェクトのプロパティを削除する |
| do | do...whileループを制御する |
| else | if文の条件が偽の場合に実行する |
| enum | 将来的な拡張のために予約されているが、JavaScriptでは未使用 |
| export | モジュールから値や関数をエクスポートする |
| extends | クラスが他のクラスを継承する |
| false | 論理値の偽(false)を表す |
| finally | 例外の有無に関わらず実行される処理を定義する |
| for | ループ処理を制御する |
| function | 関数を宣言する |
| if | 条件が真の場合に実行される |
| import | モジュールから値や関数をインポートする |
| in | 左辺のプロパティが右辺のオブジェクト内に存在するかを確認する |
| instanceof | オブジェクトが特定のクラスのインスタンスかを判定する |
| new | 新しいインスタンスを生成する |
| null | null値を表す |
| return | 関数から値を返す |
| super | 親クラスのコンストラクタやメソッドを参照する |
| switch | 複数の条件分岐を管理する |
| this | 現在のオブジェクトを参照する |
| throw | 例外を投げる |
| true | 論理値の真(true)を表す |
| try | 例外が発生する可能性のあるコードブロックを試行する |
| typeof | 値の型を返す |
| var | 変数を宣言する |
| void | 式の結果を無視してundefinedを返す |
| while | 条件が真の間ループする |
| with | スコープチェーンにオブジェクトを一時的に追加する |
| yield | ジェネレータ関数内で中断し値を外部に送出する |
JSには strict mode(厳格モード)というものがあります。
これを有効にすると、予約語が追加されます(以下の語が命名に使えなくなる)。
※公式の分類では strict mode 時にのみ予約語とされていますが、let はstrict modeでなくても予約語のように扱われ、命名に使えません。
| 予約語 | 用法の説明 |
|---|---|
| let | ブロックスコープの変数を宣言する |
| static | クラスの静的メソッドやプロパティを定義する |
| implements | クラスが特定のインターフェースを実装することを宣言する(使用されていないが予約されている) |
| interface | インターフェースを定義する(使用されていないが予約されている) |
| package | コードをモジュールとしてグループ化する(使用されていないが予約されている) |
| private | クラスのプライベートメンバーを定義する(使用されていないが予約されている) |
| protected | クラスの保護されたメンバーを定義する(使用されていないが予約されている) |
| public | クラスの公開メンバーを定義する(使用されていないが予約されている) |
以下は、文法上は予約語ではないため命名に使用できますが、JavaScriptの構文やブラウザの挙動に影響することがあるため、実務上は避けた方が無難です。
| 予約語 | 用法の説明 |
|---|---|
| as | 型変換の際にエイリアス名を定義する(TypeScriptで使われる) |
| async | 非同期関数を定義する |
| from | モジュールの特定の部分をインポートする際に使用する |
| get | オブジェクトのゲッター関数を定義する |
| meta | import.metaはモジュールの情報を提供する(新しいJavaScript機能) |
| of | for...ofループで使用され、反復可能オブジェクトの値をループする |
| set | オブジェクトのセッター関数を定義する |
| target | イベントのターゲットを指す(主にDOMで使用される) |
// ジェネレーター関数の中では使用不可
function* iterate (i) {
const yield = 0; // SyntaxError: Unexpected identifier 'yield'
yield i;
yield i += 1;
}
const it = iterate(0);
console.log(it.next()); // 結果 : 出力されない
// Async関数の中では使用不可
async function test() {
const await = 'hello';
console.log(await);
}
test(); // 結果 : SyntaxError: Unexpected identifier 'await'
// ジェネレーター関数の外では使用可能
const yield = 'yield!' // OK
console.log(yield) // 結果 : `yield!`
// Async関数の外では使用可能
function notAsync() {
const await = 123; // OK
console.log(await); // 結果 : 123
}
// letの使用例
function let() {
return 'function let!'
}
console.log(let()) // 結果 : 'function let!'
var let = 'var let!'
console.log(let) // 結果 : 'var let!'
// Mathの例
console.log(Math.max(1, 2, 3)) //出力:3
const Math = 'const Math!'; // ここで、「Math」は「const Math!」と上書きしている
console.log(Math) // 結果 : 'const Math!' ←左のように、ただの「定数」という形になる
console.log(Math.max(1, 2, 3)) // 結果 : TypeError: Math.max is not a function
// ↑上の様子からObjectの「Math」としての機能は使えなくなっていることが分かる
解説: JavaScript / TypeScript では、変数を宣言する方法が3つあります。
// JavaScriptにおける変数と定数の宣言方法の種類
const age = 25; // 定数
let name = "太郎"; // 変数
var message = "こんにちは"; // 変数
この中で、基本的にconstを使用する事が推奨されているのは、以下の理由からです。
理由①:意図しない再代入を防げます。
const は「定数」なので、一度値を入れたら変更できません。
これにより、「間違えて上書きしてしまった!」というバグを防ぐことができます。
// 意図しない再代入を防ぐ例
const pi = 3.14;
pi = 3.14159; // 再代入しようとするとエラーになる(変更できない = 値が変わらないので安全!)
理由②:読みやすく、意図が明確になります。
const を使うことで、「この値は変わらないんだな」とコードを読む人にも伝わります。
→ 読みやすく、保守もしやすいコードになります。
理由③:const でも必要なことはだいたいできます。
後述しますが、配列やオブジェクトの中身は変更可能です。
そのため「const じゃ足りない」という場面は実はほとんどありません。
// 配列やオブジェクトの中身は変更可能な例
const fruits = ["りんご", "バナナ"];
fruits.push("ぶどう"); // 値の追加OK!(エラーにならない)
結論:まずは const。必要なら let。
開発現場でも「まずは const。再代入が必要なときだけ let に変更」という方針が一般的です。
このルールで書くと、安全でバグの少ないコードになります。
解説:
変数や定数を扱うときに知っておきたいのが「再宣言」と「再代入」の違いです。
この2つは似た言葉に見えますが、実際の意味や挙動はまったく異なります。
再代入(さいだいにゅう) → 一度作った変数に、新しい値を入れなおすこと
再宣言(さいせんげん) → 同じ名前の変数をもう一度作成しようとすること
// 再代入の例
let name = "太郎";
name = "花子"; // ← 再代入(OK)
// 再宣言の例
let name = "太郎";
let name = "花子"; // ← 再宣言(エラーになってしまいます。)
| 宣言方法 | 再代入 | 再宣言 |
|---|---|---|
| const | できない ❌ | できない ❌ |
| let | できる ✅ | できない ❌ |
| var | できる ✅ | できる ✅ |
解説:
「constは定数なので、変更はできないはず!」……そう思いますよね。
実を言うと、私もそう思っていました。
しかし、実際には、オブジェクトと配列の中身は変更可能なんです。
// オブジェクトの中身変更の例
const user = {
name: "太郎",
age: 20
};
user.age = 21; // OK:プロパティの変更
user.city = "東京"; // OK:新しいプロパティの追加
ただし、下記のようにconst で宣言した“変数”そのものを上書きすることはできません。
正確には「定数」なので、再代入できないのです。
user = { name: "花子" }; // エラー:定数への再代入となるため不可
配列も同様に中身の変更は、可能です。
// 配列の中身変更の例
const colors = ["赤", "青"];
colors.push("緑"); // OK:要素の追加
colors[0] = "黄色"; // OK:要素の変更
これも、「colorsという変数の参照先」は変わっていないため、エラーにはなりません。
解説:
前の章で、constで宣言したオブジェクトや配列の中身は変更できると説明しました。
では「中身も含めて完全に変更できないようにしたい」場合は、どうすればよいでしょうか?
そんなときに使えるのが、Object.freeze() という便利なメソッドです。
// オブジェクトを完全に固定する例
const config = {
debug: true
};
Object.freeze(config); // 中身を変更されたくないオブジェクトに使用します
config.debug = false; // ❌ エラーにはならないが、変更されません
console.log(config.debug); // 結果 : true(のまま)
Object.freeze()は、オブジェクトの中身を読み取り専用にします。
書き換えようとしても無視され、実際の値は変わりません。…できればエラーになってほしいところですね。笑
ただし、注意点もあります。
// ネストされた中の値は固定されない例
const user = {
name: "太郎",
options: {
darkMode: true // ネスト(入れ子)された内容
}
};
Object.freeze(user);
user.options.darkMode = false; // ✅ これは変更されてしまいます
console.log(user.options.darkMode); // 結果 : false
上のように、Object.freeze()は浅い固定(shallow freeze)しか行いません。
ネストされたオブジェクトの中身まで固定したい場合は、Object.freeze()を使用して再帰的な関数(自身を呼び出して処理を繰り返す関数)を作成する必要があります。
TypeScriptでは型の力で「Readonly」を使い、開発時点で「これは変更しないでね」と警告を出すこともできます。
こちらについても、今後詳しく解説します!
まとめ:
・constは「変数(参照先)を固定」する
・Object.freeze()は「中身(プロパティの値)も固定」する
→ ただし、ネストされた構造には注意が必要!
解説: JavaScriptのジェネレーター関数とは、一時停止 (yield) と再開ができる特殊な関数のことです。 通常の関数は1回呼ばれたら一気に最後まで実行されますが、ジェネレーターは途中で一時停止して値を返すことができるのが特徴です。
// ジェネレーター関数の例
function* example() {
yield 1;
yield 2;
yield 3;
}
const it = example();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
// ジェネレーター関数の例
function* gen() {
const yield = 1; // 結果 : SyntaxError(構文エラー)
}