【Eloquent JavaScript和訳】 Chapter 2: JavaScript の基礎: 値、変数、制御フロー

今は覚えなくても結構です。しかしこれらを変数名として使うと予期しないことが起こる場合があります。私の経験では、char (1 文字の文字列を保存するため) と class はよく間違って使ってしまう例です。

Ex. 2.4

前の 2 つの練習問題の回答を while ではなく for を使って書き換えてください。

var result = 1;
for (var counter = 0; counter < 10; counter = counter + 1)
result = result * 2;
show(result);

'{' を使ってブロックをオープンしなくても、ループ内のステートメントにはスペースが 2 つ分インデントされ、それが上の行に '属している' こと明らかになります。

var line = "";
for (var counter = 0; counter < 10; counter = counter + 1) {
line = line + "#";
print(line);
}

プログラムは前の値に基づいた値で変数を '更新' しなければならないことがよくあります。例えば counter = counter + 1 です。JavaScript にはこれに代わるショートカット counter += 1 があります。これは他の演算、例えば result の値を 2 倍する result *= 2 または下方にカウントする counter -= 1、にも有効です。

counter += 1 と counter -= 1 の短縮形がそれぞれ counter++ と counter-- です。

ループはプログラムの制御フローに影響を与えると言われます。ステートメントの実行順序を変えるからです。多くの場合、複数のステートメントをスキップするフローが使われます。

3 と 4 で割ることのできる 20 以下の数字を全て表示します。

for (var counter = 0; counter < 20; counter++) {
if (counter % 3 == 0 && counter % 4 == 0)
show(counter);
}

キーワード if とキーワード while とはそれほど異なりません: 与えられた条件 (括弧内) をチェックし、この条件に基づいてステートメントを実行します。しかし if は一度だけ実行します。この場合ステートメントの実行回数はゼロか 1 です。

モジュロ (%) 演算子を使うとある数字が別の数字で割り切れるかを簡単にテストすることができます。割り切れる場合、モジュロが返す除算の余りはゼロです。

0 から 20 までの数字を全て表示する場合で 4 で割り切れないものをを括弧で囲みたい場合、次のようになります:

for (var counter = 0; counter < 20; counter++) {
if (counter % 4 == 0)
print(counter);
if (counter % 4 != 0)
print("(" + counter + ")");
}

しかしプログラムは counter が 4 で割り切れるかを 2 回判断しなければなりません。else 部を if ステートメントの後に付けると同じ効果を得ることができます。else ステートメントは if の条件が false の場合にだけ実行されます。

for (var counter = 0; counter < 20; counter++) {
if (counter % 4 == 0)
print(counter);
else
print("(" + counter + ")");
}

この例を少し発展させて、15 よりも大きい場合にそれらの後に星を 2 つ追加し、10 よりも大きい (しかし 15 よりも大きくない) 場合に星を 1 つ追加し、そうでない場合は星は無しにします。

for (var counter = 0; counter < 20; counter++) {
if (counter > 15)
print(counter + "**");
else if (counter > 10)
print(counter + "*");
else
print(counter);
}

この例は if ステートメントがチェーンできることを示しています。この場合、プログラムはまず counter が 15 より大きいかを調べます。大きい場合は星を 2 つ表示し、他のテストをスキップします。大きくない場合、counter が 10 よりも大きいかをチェックし続けます。counter が 10 よりも大きくない場合にだけ print ステートメントに飛びます。

Ex. 2.5

prompt を使って 2 + 2 の値を自分に問いかけるプログラムを書いてください。alert を使って、答えが "4" の場合は何か褒める言葉を、"3" または "5" の場合は "惜しい!"、その他の場合は何か意地悪な言葉を表示してください。

var answer = prompt("You! What is the value of 2 + 2?", "");
if (answer == "4")
alert("You must be a genius or something.");
else if (answer == "3" || answer == "5")
alert("Almost!");
else
alert("You're an embarrassment.");

ループが常にその終わりに行く必要のない場合、break キーワードを使うことができます。break はすぐに現在のループからジャンプし、その後の処理を続けます。次のプログラムは 20 よりも大きく 7 で割り切れる最初の数字を見つけます:

for (var current = 20; ; current++) {
if (current % 7 == 0)
break;
}
print(current);

for コンストラクトはループの終わりをチェックする部分を持ちません。つまり、break ステートメントに依って処理を止めます。同じプログラムを次のように簡単にすることもできました...

for (var current = 20; current % 7 != 0; current++)
;
print(current);

この場合ループ本体は空です。コロン 1 つで空のステートメントになります。このループは変数 current が求める値になるまでインクリメントするだけです。break を使った例を示したかったために紹介しましたが、最初の例にも注意してください。

Ex. 2.6

前の問題の回答に while を、任意で break を加え、正しい答えが出るまで質問を繰り返すようにしてください。

while (true) ... は自身では終わらないループを作れることに注意してください。true が true である限りループするなど馬鹿げているかもしれませんが、このトリックは役に立ちます。

var answer;
while (true) {
answer = prompt("You! What is the value of 2 + 2?", "");
if (answer == "4") {
alert("You must be a genius or something.");
break;
}
else if (answer == "3" || answer == "5") {
alert("Almost!");
}
else {
alert("You're an embarrassment.");
}
}

最初の if の本体に 2 つのステートメントがあるため、全ての本体に中括弧を追加しました。これは好みの問題です。if/else チェーンにブロックの本体とステートメント 1 つの本体があるのは私には少々バランスが取れていない感じがしますが、決めるのはあなたです。

別の回答もあります。間違いなくより素晴らしいのですが、break がありません:

var value = null;
while (value != "4") {
value = prompt("You! What is the value of 2 + 2?", "");
if (value == "4")
alert("You must be a genius or something.");
else if (value == "3" || value == "5")
alert("Almost!");
else
alert("You're an embarrassment.");
}

前の練習問題の回答にはステートメント var answer; があります。これは answer という名前の変数を作成しますが、それに値を与えません。この変数の値を取り出すとどうなるでしょう?

var mysteryVariable;
show(mysteryVariable);

触手という観点では、この変数は空中に消え、つかむものは何もありません。空の場所の値を要求すると、undefined という特殊な値が返されます。print や alert のような興味を引く値を返さない関数は undefined という値も返します。

show(alert("I am a side effect."));

同じような値に null があり、'この変数は定義されているが値をもっていない' ということを意味しています。undefined と null との意味上の違いは非常に学問的で、興味を起こさせません。実際にプログラムでは、'値を持っている' かをチェックしなければならないことがよくあります。このような場合に式 something == undefined が使われます。何故なら、これらが全く同じ値でなくても、null == undefined は true になるからです。

これは我々を次のトリッキーな主題へと向かわせます...

show(false == 0);
show("" == 0);
show("5" == 5);

これらは全て値 true を返します。タイプの異なる値を比べる時、JavaScript は複雑で分かりづらいルールを使います。ここで詳しく説明するつもりはありませんが、多くの場合、値の 1 つを他の値のタイプに変えようとします。しかし null または undefined の時には、両側が null または undefined の場合に true になります。

変数が値 false かどうかをテストしたい場合はどうでしょう? 文字列や数字をブール値に変換するルールでは、0 および空の文字列を false とし、その他の値を true とします。このため、式 variable == false も variable が 0 または "" を参照している時に true になります。このような場合に自動的なタイプ変換を 望まない 時のために 2 つの演算子 === と !== があります。前者は値が他の値と同一であるかをテストし、後者は同一でないかをテストします。

show(null === undefined);
show(false === 0);
show("" === 0);
show("5" === 5);

これらは全て false です。

if、while、または for ステートメントの条件として与えられる値はブール型である必要はありません。値はチェックされる前に自動的にブール型に変換されます。つまり、数字 0、空の文字列 ""、null、undefined、そしてもちろん false は全て false になります。

これら以外の値全てが true に変換されるということから、多くの場合に明示的な比較を除外することが可能になります。変数が文字列か null を含むと分かっている場合、そのチェックは非常に簡単です...

var maybeNull = null;
// ... mystery code that might put a string into maybeNull ...
if (maybeNull)
print("maybeNull has a value");

mystery code が maybeNull に値 "" を与える場合は例外です。空の文字列は false で何も表示されません。何をしようとしているかにもよりますが、これは 間違っています。このような場合は些細なミスを防ぐためにも明示的に === null または === false を加えるべきです。数字の値が 0 になるかもしれない場合も同様です。

前の例で 'mystery code' についての行を不審に思ったかもしれません。プログラムにテキストを含めたほうが良い場合がよくあります。プログラムに人間の言葉で説明を追加するのが最も一般的です。

// The variable counter, which is about to be defined, is going
// to start with a value of 0, which is zero.
var counter = 0;
// Now, we are going to loop, hold on to your hat.
while (counter < 100 /* counter is less than one hundred */)
/* Every time we loop, we INCREMENT the value of counter,
Seriously, we just add one to it. */
counter++;
// And then, we are done.

この種のテキストをコメントと呼びます。ルールは、'/*' でコメントが始まり '*/' まで続きます。'//' は別の種類のコメントの始まりで、これは行の終わりまで続きます。

お分かりのように、コメントを付け足すだけで簡単なプログラムも大きく、醜く、複雑に見えるプログラムなってしまうこともあります。

タイプの自動変換が起こってしまう場合が他にもあります。非文字列値を文字列に加える場合、その値は結合前に自動的に文字列に変換されます。数字と文字列を掛ける場合、JavaScript は文字列から数字を作ろうとします。

show("Apollo" + 5);
show(null + "ify");
show("5" * 5);
show("strawberry" * 5);

最後のステートメントは NaN を表示します。この特別な値は '数字ではない' という意味で数字タイプです (矛盾しているように思われるかもしれません)。この場合、NaN は strawberry は数字ではないと言っています。値 NaN にどんな算術演算をしても結果は NaN になります。これが上の例で 5 を掛けても NaN になった理由です。また、NaN == NaN が false になることにも面食らうでしょう。値が NaN であるかは isNaN 関数でチェックすることができます。NaN はブール型に変換されると false になるもう一つの (最後の) 値です。

これら自動変換は非常に便利なのですが、同時に奇妙で間違いの元になりかねません。+ と * は共に算術演算子ですが、例では働きが全く異なります。私のコードでは + を非文字列に使うことがよくありますが、* と他の数値演算子は文字列値には使わないことにしています。数字を文字列に変換するのは常に可能で直接的ですが、文字列から数字への変換は上手くいかない場合があります (例の最後の行がそれです)。明示的に文字列を数字に変換するためには Number を使うことができ、NaN を得るリスクがあることは明らかです。