JavaScriptでプログラミング教育するときの変数定義ではvar/let/constのどれを使うべきか2020
アシアル情報教育研究所の岡本です
JavaScriptで変数を定義するときに「let」使っていますか?
私、これまでletを使ってこなかったのですが、2021年度の公式テキストでは「let」導入しようかなと思い立ち、調査を始めました。
※ 2020年度版のテキストは印刷済みなので、「var」のままです
let導入の検討理由
「開発者ツール」で「ステップ実行」するときに、letで変数定義した方が確認しやすかったから。
letで定義すると「スクリプト」のスコープで変数が扱われるので、開発者ツールで変数の値を確認するときに見つけやすいことが判明しました。
varで定義すると「グローバル」のスコープで変数の値を確認できるのですが、ここには有りとあらゆる変数が列挙されており、変数を探すのが大変でした。
これは、let導入を検討する必要がありますね。
プログラミング教育でスコープの話は必要か?
必ずしも必要ではない(というか後回しにすべき)!
「システム開発」の新人教育では必要ですが、「プログラミング教育」はプロの要請を目的としていないため、スコープの話は必ずしも必要ではありません。
学習時間が潤沢にあれば別ですが、10~20時間の限られた時間でプログラミング入門を教える場合には「配列・関数・繰り返し」を教えるが大変なので、スコープの話は後回しで良いと考えます。
第一、関数よりも前にスコープの話をしても、有り難みがない。
あと、文科省の教員研修用資料では変数の紹介はありますが、スコープはスコープ外で「var」すら使ってませんでした(マジで)。
「var」を「let」に切り替えたときのデメリット
varはVariableの略と説明できるが、letは何の略と説明するか、それが問題だ。
あとはif文やfor文の中で変数定義をした場合に、ifやforの外から変数が参照できなくて問合せが殺到するかもしれない。
そうだ変数を先に定義させよう
プログラムの中で利用するスコープの広い変数を先に宣言するようにし、ifやforやfunctionの中では狭いスコープの変数を定義するように指導すれば、スコープが原因でトラブルに見舞われる機会は減るはず。
試しにサンプルアプリをletに書き換えた
書籍「Monacaで学ぶはじめてのプログラミング」で最もソースコードの長いサンプルアプリである「英単語学習アプリ」(約70行)をvarからletに変えてみました。
英単語学習アプリのJS部分(before)
// 問題番号
var no = 0;
// 正解数
var score = 0;
// 単語リスト
var wordList = [
{
japanese: "電話",
english: "Phone"
},
{
japanese: "歴史",
english: "History"
},
{
japanese: "社会",
english: "Society"
},
{
japanese: "世代",
english: "Generation"
},
{
japanese: "知識",
english: "Knowledge"
}
];
// 問題を表示する
function showQuestion() {
if(no < wordList.length) {
// 次の問題がある場合は、表示する
document.getElementById("question").innerHTML = wordList[no].japanese;
} else {
//全問終了したら、成績を発表する
document.getElementById("question").innerHTML = score + "/" + wordList.length;
document.getElementById("answerForm").style.display = "none";
if(score == wordList.length) {
// 全問正解の場合
document.getElementById("resultMessage").innerHTML = "全問正解!よくできました!";
document.getElementById("resultImage").src = "gold.png";
} else if(score >= wordList.length * 0.6) {
// 6割以上正解の場合
document.getElementById("resultMessage").innerHTML = "惜しい!あともう一歩でした!";
document.getElementById("resultImage").src = "silver.png";
} else {
// 6割未満の場合
document.getElementById("resultMessage").innerHTML = "もう少しがんばりましょう。";
document.getElementById("resultImage").src = "bronze.png";
}
}
}
// 入力された回答の正誤判定を行う
function judge() {
var answer = document.getElementById("answer").value;
if(answer == wordList[no].english) {
alert("正解です!");
score++;
} else {
alert("残念!不正解です。");
}
// 次の問題を表示
no++;
showQuestion();
var answer = document.getElementById("answer").value = "";
}
英単語学習アプリのJS部分(after)
// 問題番号
let no = 0;
// 正解数
let score = 0;
// 単語リスト
let wordList = [
{
japanese: "電話",
english: "Phone"
},
{
japanese: "歴史",
english: "History"
},
{
japanese: "社会",
english: "Society"
},
{
japanese: "世代",
english: "Generation"
},
{
japanese: "知識",
english: "Knowledge"
}
];
// 問題を表示する
function showQuestion() {
if(no < wordList.length) {
// 次の問題がある場合は、表示する
document.getElementById("question").innerHTML = wordList[no].japanese;
} else {
//全問終了したら、成績を発表する
document.getElementById("question").innerHTML = score + "/" + wordList.length;
document.getElementById("answerForm").style.display = "none";
if(score == wordList.length) {
// 全問正解の場合
document.getElementById("resultMessage").innerHTML = "全問正解!よくできました!";
document.getElementById("resultImage").src = "gold.png";
} else if(score >= wordList.length * 0.6) {
// 6割以上正解の場合
document.getElementById("resultMessage").innerHTML = "惜しい!あともう一歩でした!";
document.getElementById("resultImage").src = "silver.png";
} else {
// 6割未満の場合
document.getElementById("resultMessage").innerHTML = "もう少しがんばりましょう。";
document.getElementById("resultImage").src = "bronze.png";
}
}
}
// 入力された回答の正誤判定を行う
function judge() {
let answer = document.getElementById("answer").value;
if(answer == wordList[no].english) {
alert("正解です!");
score++;
} else {
alert("残念!不正解です。");
}
// 次の問題を表示
no++;
showQuestion();
let answer = document.getElementById("answer").value = "";
}
let変更後の実行結果
二重定義して怒られました、MonacaIDE上では77行目です。
function judge()の中でanswer変数をletで二重に定義しているのが原因です。
varなら二重定義が許されていたのですが、letだとダメ。
これは、二重定義しなければよい、77行目のletを外せば解決できました。
ところで「const」とは何か?
定数のようなものです。
アシアル社内のエンジニアいわく、「再代入できない変数」のほうがしっくりくるそうですが、定数とします。MDNにも定数と書かれています。
const – MDN – Mozilla
数字や文字列のような値(プリミティブ)を代入する場合は、変更できないのですが配列やオブジェクトや配列(JavaScriptの場合は配列もオブジェクトの一種)の中の値は変更可能です。
こんなかんじ。オブジェクトのプロパティも普通に追加できますね。
なお、アシアル社内のエンジニアいわく、「基本は全部constで定義して、後で変更する物だけletにして欲しい」と申していました。
岡本の意見は「初心者が事前に、後で変更するかどうか分かるわけ無いだろう。インタプリタ型のテキストプログラミングで入門するメリットは、保守性より柔軟性。」
ということで、原則const案は採用しないと思いますが、初級者以上の方はご参考までに。
まとめ
- プログラミング教育においてスコープの話はスコープ外
- 100行未満のコードだったら、varでもletでも大して変わらないと思う
- しかし、開発者ツールでステップ実行するときにletの御利益がある
- ifやforの中でletを使ってスコープにハマる事故さえ防げるならlet使いたい
- 「スコープ」や「変数の巻き上げ」を紹介する記事を次は書きたい
追伸:
元アシアル社員で「Webフロントエンドハイパフォーマンスチューニング」という専門書も執筆されているanatooさんも、constの推奨について2016年にブログで言及されていました。
ES2015でvarやletを使う場面はほとんど無いので、まずconstを使う。constだとダメな場合にはletを使う。
JavaScript(ES2015)でvarやletを使う必要はほぼ無い