はじめに
最近、学習メモを Zenn のスクラップに積みながら JavaScript Primer を読み進めました。読み方としては、章ごとに「腹落ちしたポイント」や「モヤモヤしたところ」を短文メモ+最小コードで残すスタイル。この記事は、そのメモを俯瞰して整理した“読書ログ”です。
同時に、ユースケース章の Todo アプリ を最後まで写経し、手元で動かして構造を噛み砕きました。Todo は「追加・完了・削除」という最小要件ながら、状態管理・設計・イベント駆動をコンパクトに一周できる題材で、学びの密度が高かったです(参考:章リンク)。
良かったところ
-
「this」と実行コンテキストの関係がクリアに
「関数/メソッド呼び出し」「コンストラクタ」「アロー関数」でthisがどう決まるかを、一貫した軸(呼び出し時に動的/定義時に静的)で整理できた。メソッドを変数に抜き出したときにthisを失う“あるある”も、対応パターンごとに納得感があった。 -
エラー処理まわりの“いまどき”が揃っている
Errorのcauseオプションでスタックの文脈を保つ、try...catchでは非同期に投げられた例外は拾えない、catchの後ろにthenが走る理由(常に新しい Promise を返す仕様)など、実務でハマりやすいポイントが網羅されていた。 -
標準機能を“使いどころ”で押さえられる
Map/Set と Object の違い、Iterable/Iterator/Generator の役割、JSON.stringifyのreplacer/space、toJSONの挙動など、意図と副作用までセットで学べた。 -
App(調停役)/Model(状態)/View(表示)の分離
最終リファクタでAppは「イベントの管理者」に集中し、表示生成はTodoItemView/TodoListViewへ委譲。責務の境界が明確で読みやすく、拡張もしやすい構成。 -
イベント駆動で“DOM直叩き”の複雑さを回避
TodoListModelが状態変更時にchangeを発火し、View 側がそれを購読してレンダリングする Observer パターン(自前 EventEmitter) が秀逸。
悪かったところ(もしあれば)
-
ジェネレータの実務イメージは弱め
遅延評価の利点は理解できたが、日常の業務コードでの“リファクタ前→後”の対比がもう一歩ほしかった(これは自分の経験不足側の課題でもある)。 -
章またぎの関連づけは自分で編み直す必要あり
例:エラーハンドリングの責務分割(UI層/API層/ドメイン層)など、設計観点へのブリッジは自分のプロジェクト文脈に落として補完した。 -
Todo では“イベント×モデル”の抽象が少し急
DOM 直更新 → モデル導入 → 自前 EventEmitter という段階は正道だが、概念ジャンプを感じる人もいそう。補助図や「最小実装→汎化」の対比がもう一呼吸あると、さらに入りやすい。
学んだこと
1) this の決まり方と落とし穴
-
アロー関数は定義時に
thisが静的に束縛される。 -
メソッド抜き出しで
thisを失う ⇒bind/ ラップ / アロー関数で対応。 -
コールバック内
thisは strict だとundefined。map()の中でthisを使うならアロー関数が手堅い。
2) エラー処理の要点
-
Errorのcauseで原因エラーの文脈を保つ。 -
非同期例外は
try...catchでは拾えない(タイマー内throwなど)。 -
catch後にthenが動くのは、各メソッドが常に新しい Promise を返す仕様だから。 -
ES2022 で モジュール直下
awaitが解禁。
3) データ構造・反復
- Map/Set と Object の使い分け(キーにオブジェクト、要素の存在判定、列挙特性)。
- Iterable/Iterator/Generator:遅延評価でメモリと計算を節約する設計の引き出しを増やす。
4) JSON.stringify を“道具”として使う
-
replacerで 変換ルール/許可リストを指定できる。
const obj = { id: 1, name: "js-primer", bio: null };
const replacer = (k, v) => (v === null ? undefined : v);
JSON.stringify(obj, replacer); // => {"id":1,"name":"js-primer"}
- 非シリアライズ値(関数/
undefined/Symbol)は 配列中ならnullに置換。 -
Map/Setはそのままでは{}になる。必要ならtoJSONを用意する。
5) 小ネタ&実務メモ
- フェッチ処理は “どこで例外を握りつぶすか”の責務設計を都度見直す。
- Node.js で CLI を作ると、学びの**再現性(手で動かせる)**が上がる。
6) ES Modules とエントリーポイントの定石(Todo から)
-
<script type="module">は 1 本に集約し、すべてをindex.jsからimport。別<script>に分けるとモジュールスコープが別れて連携できない落とし穴を実験で理解。
7) App は「調停役」に寄せる(Todo から)
-
Appは フォーム/DOM 取得・Model ⇄ View の橋渡し に集中。HTML 要素の生成は View コンポーネントへ移譲して行数の線形肥大を抑える、という設計判断が刺さった。
8) Model は状態と“イベント”の責務を持つ(Todo から)
-
TodoListModel/TodoItemModelが状態を握り、変化時にchangeを emit。購読側が都度再描画することで「操作=表示更新」の密結合を解消。Observer パターンのベストプラクティスが凝縮。
9) HTML/CSS は最初に固定する(Todo から)
- まず 骨組み(HTML/CSS) を完成させ、以降は JS だけ でふるまいを足していく進め方。UI 崩れとロジックの混線を避けやすい。
Vanilla JS Todo アプリで 特に 参考になったところ
-
index.jsでAppを import → インスタンス化 の流れが明示的。
フレームワークの「ルートにマウント」感覚と地続きで、理解がスムーズ。 -
View 分割(
TodoItemView/TodoListView)
App の肥大を防ぎ、イベントをコールバックで外部注入する設計でテストもしやすい(コールバック差し替えが容易)。 -
EventEmitter の自前実装を通じた理解
addEventListener/emitを自作して Observer を体感。Node や DOM のイベントモデルへの橋渡しとして最適。
難しかったこと
-
thisとスコープ/コンテキストの重ね合わせ
文脈が多層にあるため、最初は概念が交差して混乱しがち。実行パスごとに「誰が呼んだか/どこで定義したか」を紙に書き出すと整理できた。 -
非同期の例外伝播
「同期のtry...catchに乗らない経路」を体で覚える必要があった。テストで“わざと”失敗させると理解が早い。 -
ジェネレータの“最小有効活用”
実務のボトルネックに対して、配列→ジェネレータで何がどれだけ改善するかを“数値”で掴むのが課題。 -
「操作イベント」と「状態変化イベント」の分離設計(Todo から)
操作(submit/click)だから更新 ではなく、“モデルが変わったから描画” へ思考を切り替えるのに最初は違和感。実装して腑に落ちた。 -
App から HTML 生成を剥がすタイミング(Todo から)
いつ View 化するかの見極め。最終リファクタの指針(App は管理者に集中)が強い指標に。
まとめ(これからの活かし方)
設計・運用の指針
- UI/API/ドメインで 例外の責務境界 を言語化してコード規約に落とす。
- コレクション操作は Map/Set の採用判断基準(キー特性・列挙コスト・意図の可読性)をチームで共有。
- 学びの定着用に 小さな CLI を量産して“再現性のあるメモ”を残す。
具体アクション(Todo からの転用)
- 設計テンプレ:App(調停)/Model(状態+イベント)/View(DOM生成)の雛形を社内/個人テンプレに。
-
静的解析:ESLint+型(JSDoc or TS gradual)で View の契約(
onUpdateTodo/onDeleteTodo等)を明文化。 -
E2E 自動化:Playwright で 追加→完了→削除→件数表示 をシナリオ化。DOM のセレクタは
id="js-..."を活用。 - React への橋渡し:React でも Model(外部ストア/カスタムフック)/View(純粋 UI)/App(橋渡し) に当てはめて再設計してみる。
参考
おわりに
Vanilla JS の Todo は、小さな規模で 設計の骨法 を反復練習できる良教材だった。React を触った後にやると、「フレームワークの魔法」を剝いだ素の構造が見えて、理解が一段深まる。次はこの設計をそのまま ESLint/Prettier/Playwright の土台に乗せ、テスト自動化まで込みで“ちゃんとした構成”を一つ作り切る。
脚注
- Todoアプリ · JavaScript Primer #jsprimer — https://jsprimer.net/use-case/todoapp/
- エントリーポイント · JavaScript Primer #jsprimer — https://jsprimer.net/use-case/todoapp/entrypoint/
- イベントとモデル · JavaScript Primer #jsprimer — https://jsprimer.net/use-case/todoapp/event-model/
- Todoアプリのリファクタリング · JavaScript Primer #jsprimer — https://jsprimer.net/use-case/todoapp/final/
- アプリの構成要素 · JavaScript Primer #jsprimer — https://jsprimer.net/use-case/todoapp/app-structure/