すぐ実行すると結果は分かりますが、自分がどこを誤解したか分からないまま終わる場合があります。先に予測してから答え合わせします。
練習コード
const scores = [40, 80, 65];
const passed = scores
.filter((score) => score >= 60)
.map((score) => score + 5);
console.log(passed);
実行前に書く
scoresの初期値:
filter後の配列:
mapで各値がどう変わるか:
最終的な出力:
予測は [85, 70] です。実行して一致するか確認します。
間違いを分類する
- 条件式の読み違い
- 元配列が変わると思った
- メソッドの戻り値を誤解した
- 処理順を逆に読んだ
間違えた理由が、次に練習する小さな課題になります。
関数にも使う
入力例を一つ決め、引数、各行の値、戻り値を追います。非同期処理では、Promiseが返る時点と完了時点を分けます。
次に読む記事
ケーススタディ:短絡評価を含む権限判定を読む
「管理者である、または公開済みの記事を所有している場合だけ編集できる」という条件式を、実行せず説明するとします。最初に変数の意味を日本語へ置き換え、演算子の優先順位と短絡評価を確認します。左側の管理者判定が真なら右側は評価されません。したがって右側に関数呼び出しがある場合、全利用者で必ず呼ばれるとは説明できません。
判断は、入力候補を無作為に増やすのではなく、条件の境界で表にします。管理者か否か、公開済みか否か、所有者か否かの組み合わせを置き、各行で左から評価します。途中の値を頭の中だけで保持せず、「左辺は偽なので右辺へ進む」「公開済みが真でも所有者が偽なので全体は偽」と文章にします。未定義の記事が入り得るなら、プロパティ参照より前に停止する条件があるかも確認します。
悪い説明は「管理者か自分の記事なら編集できます」です。「公開済み」の条件が抜け、短絡による未評価も表せません。良い説明は「管理者なら即座に許可する。管理者でない場合だけ記事を確認し、公開済みかつ所有者本人なら許可する。記事が存在しない場合の扱いはこの式だけでは確定できない」と、確定事項と不明点を分けます。
実行前説明が失敗したと判断する条件は、値の変化順を飛ばす、代入と比較を取り違える、ループの終了条件を確認しない、外部関数の戻り値を想像で決めることです。実行後は、予測した分岐、評価回数、最終値をConsoleやデバッガで観測し、差分を「構文理解」「初期値の見落とし」「仕様の思い込み」に分類します。正解した回数より、どの読み違いが再発しているかを記録するほうが、次のコード読解に役立ちます。
まとめ
実行前のコード説明の中心は、変数・分岐・反復・戻り値・副作用を順に追い結果を予測することです。期待と実際を分け、条件を固定し、小さな仮説を同じ手順で検証します。成果は修正だけでなく、なぜその修正でよいと判断できたかという確認可能な根拠です。
参考リソース
関連記事
ケース:mapとfilterを連続して読む
商品配列では、まず各商品についてfilter callbackのboolean結果を書き、残った要素だけをmapの表へ移します。元配列、filter後、map後を分けると要素数と型の変化を説明できます。
product.stock && product.price * 1.1はstockが0ならfalseではなく0を返します。filterではfalsyですが、通常の計算式として読むと違います。priceが文字列なら乗算は数値変換、加算は文字列結合になり得ます。値の型を想像で補いません。
sortは元配列を変更し、mapは新配列を返します。出力だけでなく後続行が参照する元変数も予測します。関数外counterを増やすなら副作用として別欄へ書きます。実行後は最初に差が生まれたcallbackを特定し、入力、戻り値、採用結果を照合します。これで復習対象をtruthy判定、暗黙変換、破壊的変更へ具体化できます。
構文ごとの追跡表
| 構文 | 書き出すもの | 見落としやすい点 |
|---|---|---|
| 代入 | 変更前後の値 | =と===の違い |
| if | 条件値と選んだ枝 | else ifの順序 |
| loop | 各回のindexと累積値 | 0回、終了条件 |
| function | 引数、戻り値、副作用 | returnなしのundefined |
&& / ` | ` | |
| await | 待つPromiseと再開地点 | reject経路 |
登場人物は「利用者」ではなく、値、式、実行位置、call frameです。コード説明では変数を役名で呼ばず、その時点の具体値と値が作られた式を結びます。
実行後の確認方法
予測表には行番号、評価前の値、評価した式、評価後の値、次の行を書きます。外部関数の戻り値が不明なら想像せず「この契約次第」と保留します。実行後はbreakpointやConsoleで分岐、評価回数、最終値を比較し、差を構文理解、初期値、暗黙変換、仕様の思い込みへ分類します。
再帰ではcallごとに引数を別行へ置き、base caseから戻り値を逆にたどります。array methodではcallbackの引数と戻り値を要素ごとに書きます。正解を暗記するより、予測と実行結果が分かれた最初の式を特定することが練習の成果です。
非同期コードを実行前に読む
async functionはawaitまでの同期部分、Promise解決後の再開、呼出元へ返るPromiseを分けます。awaitがloop内なら直列待機か、Promise配列をまとめて待つかで順序が変わります。
setTimeoutは指定時間に必ず実行ではなく、その時間後に実行可能になります。現在のstackとmicrotaskが先です。Promise callbackとtimerは登録順だけで決めずqueueを確認します。
try/catchはawaitしたrejectを捕捉できますが、awaitしないPromiseや別callback内例外は範囲が異なる場合があります。errorが伝わるframeを書きます。network完了順など仕様で決まらないものは断定せず、確定できない境界を明示します。
objectと参照を追う
二つの変数が同じobjectを指す時、片方のproperty変更はもう片方からも見えます。代入を値のcopyと説明せず、primitiveか参照かを分けます。spreadは一段のcopyなので、入れ子objectは共有される点も表へ書きます。
関数へobjectを渡しpropertyを書き換える場合、returnがなくても呼出元の状態が変わります。parameterへの再代入とproperty変更は別です。前者は呼出元変数の参照先を変えません。これを具体的なobject IDを仮置きして追います。
closureでは関数作成時に値を複製するのではなく、外側bindingを参照します。loop内callbackが後で走る例ではletとvarでbindingの作られ方が違います。出力だけを覚えず、各callbackがどのbindingを見るかを説明します。
class methodではinstance field、static field、prototype methodを区別します。thisは定義場所だけでなく呼出し方で決まる場合があるため、methodを変数へ渡した時のreceiverを確認します。不明なlibrary callbackのthis契約はdocumentへ保留します。