JavaScript基礎 #3 制御フロー
#2 変数と型 で値を扱う道具を見ました。今回の記事は、その値を使って — 分岐 と 繰り返し を作るところです。
if / else if / else
#
一番なじみのある分岐です。
const score = 85;
if (score >= 90) {
console.log('A');
} else if (score >= 80) {
console.log('B');
} else if (score >= 70) {
console.log('C');
} else {
console.log('F');
}条件は truthy/falsy 検査 になります。#2 で見た falsy の7つ(false, 0, -0, 0n, ‘’, null, undefined, NaN)であれば、else に進みます。
const name = 'カーティス';
if (name) {
// name が空でない文字列のとき
console.log(`こんにちは, ${name}`);
}
// ほぼ同じ — 明示的
if (name !== '') {
console.log(`こんにちは, ${name}`);
}短く意図が明確 なときは truthy 検査を、何が空なのか をはっきりさせたいときは明示的な比較を。どちらもよく見られます。
三項演算子 — 短い分岐に向く表現 #
const score = 85;
const grade = score >= 60 ? '合格' : '不合格';
// 条件 ? 真のとき : 偽のとき
値を決める場面(変数代入、JSX、オブジェクトのフィールド)では、if/else より短くて読みやすくなります。ただし2段以上ネストするとすぐに読みにくくなるので、その場合は if/else if で展開して書くほうが良いです。
while と do...while
#
条件が真である間、繰り返します。
let count = 0;
while (count < 5) {
console.log(count);
count = count + 1;
}
// 0, 1, 2, 3, 4
do...while は 最低1回は必ず実行される 変形です。ユーザー入力のように「受け取ってから条件を検査する」場面に向いています。
let answer;
do {
answer = prompt('数字を入力してください');
} while (isNaN(Number(answer)));for — クラシックループ
#
3つの欄で構成されます — 初期化 ; 条件 ; 各反復後。
for (let i = 0; i < 5; i = i + 1) {
console.log(i);
}
// 0, 1, 2, 3, 4
C/Java と同じ形です。インデックスを直接扱う必要があるとき(逆順、スキップ、2つの配列を同時に進めるなど)は、依然として最も明確です。ただし、通常の配列の反復は for...of のほうが普通は読みやすくなります。
for...of — 配列イテレーションの標準
#
const fruits = ['りんご', 'バナナ', 'ぶどう'];
for (const fruit of fruits) {
console.log(fruit);
}
// りんご, バナナ, ぶどう
配列の 値 を1つずつ取り出してくれます。インデックスが不要なら、ほぼ常にこちらのほうがすっきりします。
インデックスも一緒に — entries()
#
const fruits = ['りんご', 'バナナ', 'ぶどう'];
for (const [i, fruit] of fruits.entries()) {
console.log(`${i}: ${fruit}`);
}
// 0: りんご, 1: バナナ, 2: ぶどう
fruits.entries() は [インデックス, 値] のペアを作ってくれるイテレータです。分割代入は #5 オブジェクトと配列 で詳しく扱いますが、ひとまず「2つの変数に同時に受け取る構文」と覚えておけば大丈夫です。
for...in — ほとんど使わない変形
#
名前が似ていて混乱しやすいですが、これは オブジェクトのキー を反復します。配列に使うとよくないことがよく起こります。
const user = { id: 'u1', name: 'カーティス', age: 30 };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}
// id: u1, name: カーティス, age: 30
const arr = ['a', 'b', 'c'];
arr.foo = 'oops';
for (const key in arr) {
console.log(key);
}
// 0, 1, 2, foo ← foo も出る
配列のインデックス(0、1、2)と一緒に、私たちが追加したプロパティ(foo)も入ってきます。そのため、配列は絶対に for...in で反復しないでください。オブジェクトには Object.keys/values/entries を使うほうが安全です。
const user = { id: 'u1', name: 'カーティス', age: 30 };
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}配列のメソッドによる反復 — forEach, map, filter プレビュー
#
for...of のほかに、配列は関数型スタイルの反復メソッドを持ちます。
const numbers = [1, 2, 3, 4, 5];
// 副作用のみ — forEach
numbers.forEach((n) => console.log(n));
// 変換 — map
const doubled = numbers.map((n) => n * 2);
// [2, 4, 6, 8, 10]
// 絞り込み — filter
const evens = numbers.filter((n) => n % 2 === 0);
// [2, 4]
これらのメソッドは #5 オブジェクトと配列 で本格的に扱います。今は「forループ以外にも表現方法がある」ということだけ覚えておいてください。
break と continue
#
ループの中で流れを制御する2つのキーワードです。
for (let i = 0; i < 10; i = i + 1) {
if (i === 5) break;
console.log(i);
}
// 0, 1, 2, 3, 4
for (let i = 0; i < 5; i = i + 1) {
if (i === 2) continue;
console.log(i);
}
// 0, 1, 3, 4 (2 が抜ける)
forEach/map/filter のようなメソッドの中では、break/continue は動作しません。途中で打ち切る必要があるなら、for...of で書き直すか、some/every のようなメソッド(次のシリーズで扱います)を使います。
switch — 複数の値での分岐
#
const day = 'mon';
switch (day) {
case 'mon':
case 'tue':
case 'wed':
case 'thu':
case 'fri':
console.log('平日');
break;
case 'sat':
case 'sun':
console.log('週末');
break;
default:
console.log('不明');
}case は上から下へ検査され、一致したところから break に出会うまで実行が続きます。これがよく落とし穴になる部分です — break を忘れると、次の case も一緒に実行されてしまいます(フォールスルー)。
switch (day) {
case 'mon':
console.log('月曜日!');
// break 忘れ
case 'tue':
console.log('火曜日!');
break;
}
// day が 'mon' なら — 月曜日! 火曜日! 両方出力
switchの比較は厳密(===)
#
switch のcaseマッチングは、常に === で行われます。自動変換は起こりません。
switch (5) {
case '5': console.log('a'); break; // 一致しない
case 5: console.log('b'); break; // ここでマッチ
}これは良い点です — == の落とし穴から自由になります。
短いifの落とし穴 — 中括弧の省略 #
JavaScriptは短いifで中括弧を省略できます。
if (condition)
doSomething();問題は、2行以上に増えたとき に事故が起きることです。
if (condition)
doSomething();
alsoThis(); // 常に実行される (if と無関係)
インデントは if の中のように見えますが、実際は常に実行されます。こうした事故を防ぐため、短いifでも常に中括弧 を使うコンベンションが安全です。ESLintの curly ルールが、まさにこれを強制してくれます。
まとめ #
今回の記事で整理した内容:
if/elseは truthy/falsy 検査または明示的な比較- 短い分岐は三項演算子、それ以上は
if/else if - 通常の配列の反復は
for...ofが標準 for...inはオブジェクトのキーの反復 — 配列には使わないbreak/continueは通常のループでのみ。メソッドのコールバックでは効かないswitchは fallthrough に注意、比較は常に===- 短いifでも中括弧を付けるほうが安全
次の記事(#4 関数)では、JavaScriptの関数 — 宣言/式/アロー の3つの定義方法、仮引数のパターン、そしてホイスティングとは何かを扱います。