【JavaScriptクイズ】第1問:変数を宣言するには

2023/03/26に公開

JavaScriptの文法や便利な使い方について、気軽なクイズ形式で解説する記事を書いていきます。

今回のテーマは「変数の宣言」です。では、さっそく問題です!

問題

配列に格納された数値の合計を表示する、次のようなJavaScriptのプログラムがあります。

quiz.js
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // この数値の合計を……
sum = 0; // この変数に格納する

// 合計を計算
for (x of numbers) {
  sum += x;
}

// 結果を表示
console.log("sum = " + sum);

このプログラムは、例えばNode.jsを使って次のように実行できます。

コマンドライン
node quiz.js
実行結果
sum = 55

1から10までの合計は55なので、正しい計算結果になっていることが分かります。

さて、JavaScriptには「厳格モード(strict mode)」と呼ばれるモードがありまして、これを使えば実行が速くなるかもしれないなどのメリットがあります。そこで次のように、プログラムの1行目に"use strict"を追加してみました。これで、プログラムは「厳格モード」で実行されるようになります。

quiz.js
"use strict";

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // この数値の合計を……
sum = 0; // この変数に格納する

// 合計を計算
for (x of numbers) {
  sum += x;
}

// 結果を表示
console.log("sum = " + sum);

すると、プログラムがエラーで動かなくなってしまいました。実行しようとすると、次のようなエラーメッセージが表示されるのです。

エラーメッセージ
ReferenceError: numbers is not defined

どのように直せば正常に動作するプログラムになるか、分かりますか?

ヒントを見る?

「厳格モード」は、新しい変数が意図せず作られないように配慮されています。まだ「宣言」されていない変数に代入しようとしたら、エラーとして扱われます。

答えを見る?

次のようにletを付けて変数を宣言すれば、エラーは発生しなくなります。

quiz.js
"use strict";

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // この数値の合計を……
let sum = 0; // この変数に格納する

// 合計を計算
for (let x of numbers) {
  sum += x;
}

// 結果を表示
console.log("sum = " + sum);

または次のように、値が変わらない変数にはconstを付けるのでもOKです。

quiz.js
"use strict";

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // この数値の合計を……
let sum = 0; // この変数に格納する

// 合計を計算
for (const x of numbers) {
  sum += x;
}

// 結果を表示
console.log("sum = " + sum);

解説

問題のエラーメッセージは……

ReferenceError: numbers is not defined

日本語に訳すと次のような意味です。

参照エラー: numbersは定義されていません

ここで、numbersはプログラム中に出てくる変数名です。このプログラムでは、以下の3つの変数が使われています。

  • numbers
  • sum
  • x

「厳格モード」では、変数が最初に登場するところでletconstを付けて「宣言」しなければなりません。問題のプログラムでは、どの変数も宣言されていなかったためエラーになっていました。プログラムはnumbersのエラーが出た時点で停止してしまいましたが、実際にはsumxにもエラーがあるということです。

letは、通常の変数を表します。次のように、値を変更してOKです。

"use strict";

let x = 1;
x = 2; // letの変数は、値を変更してOK

constは定数を表します。初期化したあとで値を変更しようとすると、エラーになります。

"use strict";

const x = 1;
x = 2; // エラー:constは定数なので、値を変更できない

では、3つの変数にletconstを付けていきましょう。

numbersはどっち?

numbersは、今回の「答え」ではletとしました。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

でも、この変数は初期化したあとに値が変わらないので、定数だと考えることもできます。つまり、次のようにconstを付けても正解です。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

constを付けた場合は、次のように配列全体を定義し直そうとするとエラーになります。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers = [11, 12, 13, 14, 15]; // エラー:constなので変更できない

ところが、少し意外かもしれませんが、次のように配列の内容に変更を加えるのはOKです。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers[0] = -1; // constでも、これはOK
numbers.push(99); // これもOK

さてさて、上記をふまえて、letconstをどのように使い分ければいいのかというのは議論が絶えないところかなと思います。

原則としては、「constにできるのなら、そうすべき」と考えればいいでしょう。この方針にしたがう場合、プログラム中に表れる変数は、かなりのものがconstになるのではないかと思います。

これに対して、今回の「答え」では「とくに定数っぽいものだけconstにする」という方針でletを採用しました。「円周率は3.14だ」とか、「曜日の種類は7つだ」というような、明らかな定数にconstを付ける考え方です。

どちらの方針が正しいかは決められませんが、プログラムをどちらの方針で書くかは決めておくのがいいかと思います。

sumはどっち?

sumは、ループの内側で値が書き換えられています。

// 合計を計算
for (let x of numbers) {
  sum += x; // ここで値が変わる
}

したがって、letを付けて宣言しなければなりません。constだと、エラーが発生します。

let sum = 0;

xはどっち?

xは、今回の「答え」ではletとしました。これは、for-ofループが回るたびにxの値が変わるように見えるからなのですが……

// 合計を計算
for (let x of numbers) {
  ……
}

実は、次のようにconstにしてもエラーは発生しません。

// 合計を計算
for (const x of numbers) {
  ……
}

これがなぜOKなのかというと、変数xはループが1回まわるごとに新しく作られるので、厳密には値が変わっているわけではないからです。

でも、イメージ的にはxの値が更新されていくように感じますよね?そのため、この場合はletを使うほうが分かりやすいという人もいるのではないかと思います。

まとめ

JavaScriptは、"use strict"と書くと「厳格モード(strict mode)」で実行できます。「厳格モード」では、変数を「宣言」する必要があるなど、文法がより厳格になります。また、実行速度が速くなる可能性も、見逃せないメリットです。

既存のプログラムを「厳格モード」に置き換えようと思うと、なかなか苦労するかもしれません。でも、これから書き始めるプログラムなら、それほど大変ではないと思います。とにかく1行目に"use strict"と書いてしまってから、プログラミングにとりかかるのがおすすめです。

変数を「宣言」するとき、letconstをどのように使い分けるかは考えどころです。「constにできるのなら、そうすべき」と考えるのが原則ですが、絶対にそうしなければならないというわけではありません。とくにチーム開発では、どういうルールにするか話し合って決めるのがいいでしょう。

なお、letconstについては下記の本でも取り上げています。

https://zenn.dev/teamariesdotcom/books/af56bae422969b

また、下記のページではC言語のconstについて取り上げています。よろしければ参考にしてみてください!

https://curiocube.team-aries.com/progtips-ch15-saveparameters/

Discussion