🐿 こども経済新聞 Python入門SQL入門ネットワーク入門Git・GitHub声で覚える英語
段階
0 / 18 ステップ

まず、全体の地図

文法の暗記より先に、「何をするための道具か」をつかむ

JavaScript学習でいちばん多い挫折は、「文法は一通りやったけれど、いざ作ろうとすると手が止まる」という状態です。 これは、文法を作りたいものと切り離して覚えてしまっているために起こります。 実務で本当に役立つのは、「画面から値を受け取り、加工して、また画面に返す」という一本の流れを自分の手で書ける力です。 JavaScriptの仕事は、突きつめると次の4つしかありません。①値を持つ ②値を加工する ③画面を書き換える ④外と通信する。 このページは、この4つを下から順に積み上げ、最後に「壊れにくい書き方」までつなげる構成にしています。

JavaScriptがやっている仕事は、結局この一本道
JavaScriptの処理の流れ 入力を取る(フォーム・クリック・ファイル/STEP 8〜10)、加工する(条件分岐・配列メソッド・関数/STEP 1〜7)、画面に出す(DOM操作・文字列の組立・表示の出し分け/STEP 8〜10)、外と通信(fetch・async/await・JSON/STEP 11〜13)の順に進みます。 入力を取る 加工する 画面に出す 外と通信 フォーム クリック ファイル 条件分岐 配列メソッド 関数 DOM操作 文字列の組み立て 表示の出し分け fetch async / await JSON STEP 8〜10 (画面) STEP 1〜7 (文法) STEP 8〜10 (画面) STEP 11〜13 (通信)
段階ステップ身につくことできるようになること
基礎 STEP 0〜1 実行環境、コンソール、変数、データ型、型変換 ブラウザの開発者ツールでコードを試せる
文法 STEP 2〜7 条件分岐、繰り返し、関数、オブジェクト、配列メソッド、クラスとモジュール データを受け取って、必要な形に加工できる
画面 STEP 8〜10 DOM操作、イベント、フォームの入力チェック ボタン・入力欄が動く画面を、自分で組み立てられる
通信 STEP 11〜13 非同期処理、Promise、async/await、fetchとAPI サーバーからデータを取って画面に表示できる
開発環境 STEP 14〜15 エラー処理、デバッグ、Node.js、npm、ビルド、TypeScript入門 エラーを自力で読み解き、チームの環境で開発できる
実務 STEP 16〜17 コード品質、セキュリティ、保存、日付、フレームワークへの橋渡し チームの一員として任せてもらえる

目安の期間は「1日30分〜1時間、週5日」を想定しています。すでにHTMLとCSSに慣れている方は、もっと速く進みます。急ぐ必要はありません。手を動かした分だけ身につく分野です。

先に用意するもの 必要なのはブラウザ(Google ChromeやMicrosoft Edge)とテキストエディタ(Visual Studio Codeが定番)だけです。STEP 14までは、インストール作業なしでも学べます。有料のものは一切必要ありません。

18ステップの目次

18ステップ

すべての段階 / 18件を表示中
STEP 0 基礎

JavaScriptとは何か ── 実行できる場所を用意する

目安 1〜2日
このステップの到達点 ── JavaScriptがどこで動く言語なのかを説明でき、ブラウザの開発者ツールで自分の書いたコードを実行できる。そして、HTMLファイルからJavaScriptを読み込んで動かせる。ここができれば、あとの17ステップは「試しながら読む」だけになります。

JavaScriptは「Webページを動かすための言語」から始まった

HTMLはページの中身、CSSは見た目、そしてJavaScript動きを担当します。ボタンを押したら数が増える、入力ミスを赤く知らせる、続きを勝手に読み込む——こうした「利用者の操作に反応する部分」は、ほぼすべてJavaScriptが書いています。

今のJavaScriptは、ブラウザの中だけの言語ではありません。Node.jsという実行環境を使えば、パソコンやサーバーの上でも同じ言語が動きます。つまり、画面側(フロントエンド)とサーバー側(バックエンド)を、1つの言語で書けるようになっています。これがJavaScriptを学ぶ費用対効果がとても高い理由です。

役割言語たとえるとできないこと
HTMLマークアップ言語建物の骨組みと部屋割り色を変える、動かす
CSSスタイル言語内装・色・配置条件で処理を変える
JavaScriptプログラミング言語電気配線とスイッチ(骨組みの代わりにはならない)

この3つは競合しません。順番はHTML → CSS → JavaScriptです。HTMLとCSSがまったく分からない状態でJavaScriptだけを学ぶと、画面の書き換え(STEP 8)でつまずきます。不安な方は、先にHTMLのタグを20個ほど、CSSの指定を20個ほど触っておくと、あとが楽になります。

用語の整理 ── ECMAScript、ES6、TypeScript

学び始めると、似た言葉がいくつも出てきて混乱します。先に整理しておきます。

言葉読み方一言でいうと
JavaScriptジャバスクリプト言語そのものの名前。略してJSとも書く
ECMAScriptエクマスクリプトJavaScriptの仕様書の名前。中身はほぼ同義
ES6 / ES2015イーエスシックス2015年の大きな改訂版。letやアロー関数はここから
Javaジャバまったく別の言語。名前が似ているだけ。混同注意
TypeScriptタイプスクリプトJavaScriptに「型」を足した上位版(STEP 15)
Node.jsノード・ジェイエスブラウザの外でJavaScriptを動かす実行環境
よくある誤解 「JavaScriptはJavaの簡易版」ではありません。名前が似ているのは、1995年当時の宣伝上の事情によるものです。求人票で「Java」と「JavaScript」を読み違えると面接で困るので、ここははっきり区別してください。

いちばん速い実行方法 ── ブラウザの開発者ツール

まず、何もインストールせずに書き始めます。ブラウザには開発者ツール(デベロッパーツール)が最初から入っていて、その中のコンソールにコードを打つと、その場で実行できます。

  1. ブラウザ(ChromeやEdge)で、適当なページを開く
  2. WindowsF12、または Ctrl + Shift + IMacCommand + Option + I
  3. 上のタブから「Console(コンソール)」を選ぶ
  4. カーソルが出ている行に、下のコードを打って Enter
最初の一歩:コンソールに出力する
console.log("はじめてのJavaScript");
console.log(2 + 3);
console.log("こども" + "経済新聞");
実行結果
はじめてのJavaScript
5
こども経済新聞

console.log() は、かっこの中身をコンソールに表示する命令です。これから最後まで、いちばん多く使う命令です。「動いているか分からない」ときは、まずこれを挟む——これが実務の基本動作になります。

コンソールで慌てないために 赤い文字のエラーが出ても、パソコンは壊れません。打ち間違いを教えてくれているだけです。エラー文の読み方はSTEP 14でまとめて扱います。今は「赤が出たら、綴りを見直す」で十分です。

HTMLファイルから読み込んで動かす

コンソールは試し打ち用です。実際の開発では、HTMLファイルとJavaScriptファイルを分けて書きます。デスクトップに js-practice というフォルダを作り、次の2つのファイルを置いてください。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>JavaScriptの練習</title>
</head>
<body>
  <h1 id="title">練習ページ</h1>
  <button id="btn">押してみる</button>

  <!-- 読み込みは </body> の直前に置く -->
  <script src="main.js"></script>
</body>
</html>
main.js
console.log("main.js が読み込まれました");

const btn = document.getElementById("btn");
btn.addEventListener("click", function () {
  document.getElementById("title").textContent = "押されました!";
});
確かめ方

index.html をダブルクリックしてブラウザで開き、ボタンを押すと見出しが変わります。同時にコンソールに「main.js が読み込まれました」と出ていれば成功です。中身の意味は今わからなくて構いません。STEP 8・9で必ず分かるようになります。

なぜ script は最後に書くのか ブラウザはHTMLを上から順に読みます。script を先頭に書くと、ボタンがまだ存在しない時点でJavaScriptが動き、「そんな要素はない」というエラーになります。</body> の直前に置くか、<script src="main.js" defer>defer を付けるのが定番です。defer は「HTMLを全部読んでから実行して」という指示です。

学ぶ順番は「値 → 加工 → 画面 → 通信」

このページの順番には理由があります。上の段階は、下の段階が書けている前提で成り立つからです。

  • 変数が分からなければ、条件分岐は書けない
  • 関数が分からなければ、イベント処理は書けない
  • 配列の扱いが分からなければ、通信で受け取ったデータを表示できない
  • 非同期が分からなければ、APIのエラー対応ができない

この依存関係は、そのまま不具合が起きたときの調べる順番になります。学習の順番と、実務での調査の順番はほぼ同じです。だから下から学ぶと、知識がそのまま仕事の手順になります。

このページの使い方

  1. 一周目は読むだけ。分からない語があっても止まらず、最後まで読み通してください。
  2. 二周目でコードを打つ。黒い枠は、コンソールかファイルにそのまま貼って動かせます。写経(見ながら自分で打つ)を強く勧めます
  3. 三周目で一問一答。各ステップ末の問いに、声に出して答えてください。答えられなければ、その節だけ読み返します。
ふりがな機能について このページは、難しい漢字や英語の略語にふりがなを付けています。画面上部の「ふりがな ON/OFF」ボタンでいつでも切り替えられ、設定は次に開いたときも引き継がれます。読み方を間違えて覚えると、会話で通じない・検索できないという実害が出ます。最初は必ずONで読んでください
一問一答
  1. HTML・CSS・JavaScriptは、それぞれ何を担当しますか。
  2. JavaScriptとJavaの関係を一言で説明してください。
  3. <script> を </body> の直前に置くのはなぜですか。
解答を見る
  1. HTMLは中身(構造)、CSSは見た目、JavaScriptは動き(操作への反応)。
  2. まったく別の言語。名前が似ているだけで、文法も用途も異なる。
  3. HTMLが上から読まれるため、先に置くと操作したい要素がまだ存在せずエラーになるから(defer を付ける方法もある)。
STEP 1 基礎

変数とデータ型 ── 値を持つ、値を見分ける

目安 3日
このステップの到達点 ── constlet を使い分けられ、7つのデータ型を見分けられる。文字列と数値の変換で起きる事故("1" + 1"11" になる問題)を、自分の言葉で説明できる。

変数は「値に名前を付けた箱」

変数とは、値に名前を付けて置いておく場所です。名前を付けておけば、あとから何度でも呼び出せます。JavaScriptの変数の宣言方法は3つありますが、実務で使うのは2つだけです。

変数の宣言
const shopName = "こども商店";   // 変えない値 → const
let stock = 12;                  // あとで変える値 → let

stock = stock - 1;               // OK。letは再代入できる
console.log(shopName, stock);    // こども商店 11

// shopName = "べつの店";        // これはエラーになる
// TypeError: Assignment to constant variable.
宣言読み方再代入使いどころ
constコンストできないまずこれ。値を入れ替えない変数すべて
letレットできる数を数える、状態を切り替えるなど、入れ替えが必要なとき
varバーできる新しく書かない。古いコードを読むときだけ知っていればよい

迷ったら const から書くのが実務の定石です。書いている途中で「ここは入れ替えたい」と分かった時点で let に直します。こうすると「意図せず値が変わっていた」という不具合が減ります。var は、有効範囲(スコープ)の挙動が直感に反するため、新規のコードでは使いません。

名前の付け方には作法がある

✕ 避けたい書き方
const a = 1980;
const data2 = "山田";
const flg = true;

3日後の自分が読めません。何の数字かが名前から分かりません。

◯ 実務の書き方
const itemPrice = 1980;
const userName = "山田";
const isPublished = true;

単語をつなげ、2語目以降の頭を大文字にします(キャメルケース)。真偽値は is / has で始めます。

  • 使える文字は、英数字と _ $数字から始めることはできない
  • 大文字と小文字は区別される(userNameusername は別物)
  • 日本語の変数名も技術的には使えますが、実務では使いません
  • 変更しない設定値は、MAX_COUNT のように全部大文字にする慣習があります

データ型は7つ。まずは5つ覚える

データ型とは、値の種類のことです。種類によってできる操作が違うため、見分けられることが必須です。

読み方意味
stringストリング"こんにちは"文字列。クォートで囲む
numberナンバー1980 3.14 -5数値。整数と小数の区別がない
booleanブーリアンtrue false真偽値。はい/いいえ
undefinedアンディファインドundefinedまだ値が入っていない(未定義)
nullヌルnull「空である」と意図して入れた値
objectオブジェクト{ name: "山田" }複数の値をまとめた入れ物(STEP 5)
symbol / bigintシンボル/ビッグイント特殊用途。初学のうちは不要
型を調べる:typeof
console.log(typeof "こども");     // "string"
console.log(typeof 100);          // "number"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof { a: 1 });     // "object"
console.log(typeof [1, 2, 3]);    // "object"(配列もオブジェクトの仲間)
console.log(typeof null);         // "object"(※有名な仕様上の誤り)
読み取り方

typeof null"object" になるのは、JavaScript初期からの直っていない誤りです。仕様として残っているので、覚えるしかありません。配列かどうかを調べたいときは Array.isArray(値) を使います。

undefined と null の違い ── 実務で必ず聞かれる

 undefinednull
誰が入れるJavaScriptが自動で入れる書いた人が意図して入れる
意味まだ何も代入されていない「無い」ことが確定している
出る場面宣言だけした変数、存在しないプロパティ、戻り値のない関数「検索したが該当なし」を表すときなど
覚え方 undefined=まだ入っていないnull=わざと空にした。実務では「APIから null が返る」=サーバー側が「無い」と答えている、「undefined になる」=こちらのコードで名前を間違えている、という切り分けに直結します。

文字列の組み立て ── テンプレートリテラルを使う

文字列と変数をつなげる場面は非常に多いので、テンプレートリテラル(バッククォートで囲む書き方)を最初から使ってください。

+でつなぐ書き方と、テンプレートリテラル
const name = "山田";
const count = 3;

// 昔ながらの書き方(読みにくい)
console.log("こんにちは、" + name + "さん。カートに" + count + "点あります。");

// テンプレートリテラル(推奨)
console.log(`こんにちは、${name}さん。カートに${count}点あります。`);

// 中では計算もできる
console.log(`税込 ${Math.round(1980 * 1.1)} 円`);

// 改行をそのまま書ける
const address = `〒100-0001
東京都千代田区1-1-1`;
console.log(address);
実行結果
こんにちは、山田さん。カートに3点あります。
こんにちは、山田さん。カートに3点あります。
税込 2178 円
〒100-0001
東京都千代田区1-1-1

バッククォート(`)は、Windowsでは Shift + @、Macでは Shift + Option + @ で入力できます。${ } の中には変数だけでなく式も書けます。

型の自動変換 ── ここが最大の事故ポイント

JavaScriptは、型が違う値どうしを計算しようとすると、勝手に型をそろえてから実行します。これを暗黙の型変換といいます。便利に見えて、実務では不具合の温床です。

つまずきやすい例
console.log(1 + 1);        // 2
console.log("1" + 1);      // "11"  ← 文字列としてつながる
console.log("10" - 1);     // 9     ← ひき算は数値に変換される
console.log("10" * 2);     // 20
console.log(1 + true);     // 2     ← true は 1 として扱われる
console.log([] + {});      // "[object Object]"
なぜ危ないのか フォームの入力欄から取った値は、数字を打っても必ず文字列です。"10" + 5105 になります。合計金額がとんでもない数字になる不具合の、いちばん多い原因がこれです。入力値は、使う前に必ず数値へ変換すると覚えてください。
明示的に型を変換する
// 文字列 → 数値
console.log(Number("10") + 5);      // 15   ← 基本はこれ
console.log(parseInt("10個", 10));  // 10   ← 途中まで読む(単位付き対策)
console.log(parseFloat("3.5kg"));   // 3.5

// 変換に失敗すると NaN(Not a Number)になる
const wrong = Number("あいう");
console.log(wrong);                 // NaN
console.log(Number.isNaN(wrong));   // true  ← 判定はこれで行う

// 数値 → 文字列
console.log(String(2178));          // "2178"
console.log((2178).toLocaleString()); // "2,178"(3桁区切り。表示向き)

// 小数の丸め
console.log(Math.round(3.5));       // 4(四捨五入)
console.log(Math.floor(3.9));       // 3(切り捨て)
console.log(Math.ceil(3.1));        // 4(切り上げ)
console.log((3.14159).toFixed(2));  // "3.14"(文字列になる点に注意)
小数の落とし穴 0.1 + 0.20.30000000000000004 になります。バグではなく、コンピュータが2進数で小数を扱うための誤差で、多くの言語で共通です。金額の計算では、円単位の整数で計算する(例:小数の「円」ではなく「銭」や整数の円で持つ)のが実務の鉄則です。
動かして確かめる ── 型変換のふるまい
「計算する」を押してください。

入力欄の値はつねに文字列です。左が「文字列のまま」、右が「Number() で数値にしてから」の結果です。+ のときだけ結果が大きく変わることを確かめてください。

一問一答
  1. constlet は、どう使い分けますか。
  2. undefinednull の違いを説明してください。
  3. "10" + 5 の結果と、その理由を答えてください。
  4. 入力欄の値を合計する前に必ずやることは何ですか。
解答を見る
  1. 原則 const。あとから値を入れ替える必要があるものだけ let
  2. undefined はまだ値が入っていない状態(JavaScriptが自動で付ける)、null は「無い」と意図して入れた値。
  3. "105"+ は片方が文字列だと文字列連結として扱われるため。
  4. Number() などで数値に変換する(変換できたかを Number.isNaN() で確認するとなお良い)。
STEP 2 文法

条件分岐 ── 場合によって処理を変える

目安 3日
このステップの到達点 ── ifswitch を書き分けられ、== ではなく === を使う理由を説明できる。「値がある/ない」を安全に判定でき、条件が深くならない書き方(早期リターン)を身につける。

比較演算子 ── まず === を体に入れる

記号意味結果
===厳密に等しい(型も値も同じ)1 === "1"false
!==厳密に等しくない1 !== "1"true
==型を変換してから比べる(使わない1 == "1"true
> >=より大きい/以上5 >= 5true
< <=より小さい/以下3 < 3false
実務の鉄則:比較は === == は型をそろえてから比べるため、0 == ""null == undefinedtrue になるなど、直感に反する結果を返します。等価比較は必ず ===!==。チームのコード検査ツール(ESLint)でも、ほぼ例外なくこれが強制されます。

if文 ── 基本の形

if / else if / else
const score = 72;

if (score >= 80) {
  console.log("合格(優)");
} else if (score >= 60) {
  console.log("合格");
} else {
  console.log("不合格");
}
// → 合格

// 条件を組み合わせる
const age = 15;
const hasTicket = true;

if (age < 18 && hasTicket) {   // かつ(AND)
  console.log("学生料金で入場できます");
}
if (age < 6 || age >= 65) {    // または(OR)
  console.log("無料です");
}
if (!hasTicket) {              // 否定(NOT)
  console.log("チケットを購入してください");
}

truthy と falsy ── 「値がある」の判定

JavaScriptでは、真偽値以外の値も条件として書けます。条件の場所に置いたとき false 扱いになる値をfalsy(フォルシー)、それ以外をtruthy(トゥルーシー)と呼びます。

falsy(偽として扱われる)truthy(真として扱われる)
false 0 -0 ""(空文字) null undefined NaN 上記以外すべて"0" "false" [](空配列) {}(空オブジェクト)も真
よくある落とし穴
const count = 0;

if (count) {
  console.log("在庫あり");
} else {
  console.log("在庫なし");   // ← 0 は falsy なのでこちら
}

// 「未入力かどうか」を見たいのに、0 まで弾いてしまう例
const inputValue = 0;
if (!inputValue) {
  console.log("入力してください");  // 0 は正しい入力なのに弾かれる
}

// 意図を明確にした書き方
if (inputValue === undefined || inputValue === null) {
  console.log("入力してください");
}
// もっと短く(?? は null と undefined のときだけ右側を使う)
const safe = inputValue ?? "未入力";
console.log(safe);   // 0
||?? の違い a || b は「aがfalsyならb」。a ?? b は「aがnullかundefinedのときだけb」。数量や金額など0が正しい値になり得る場面では ?? を使います。??ヌル合体演算子(Nullish coalescing)と読みます。

三項演算子 ── 短い分岐を1行で

条件 ? 真のとき : 偽のとき
const stock = 0;

// if で書くと4行
let label;
if (stock > 0) { label = "販売中"; } else { label = "売り切れ"; }

// 三項演算子なら1行
const label2 = stock > 0 ? "販売中" : "売り切れ";
console.log(label2);   // 売り切れ

// 画面表示の出し分けで多用する
const price = 1980;
console.log(`${price.toLocaleString()}円(${stock > 0 ? "在庫あり" : "入荷待ち"})`);
入れ子にしない a ? b : c ? d : e のように三項演算子を重ねると、途端に読めなくなります。2段以上になったら if か次に出る switch に書き換える——これは実務のレビューで必ず指摘される点です。

switch文 ── 同じ値との比較が3つ以上のとき

switch の書き方
const status = "shipping";

switch (status) {
  case "pending":
    console.log("入金待ち");
    break;              // ← break を忘れると下に流れ落ちる
  case "paid":
    console.log("準備中");
    break;
  case "shipping":
    console.log("発送済み");
    break;
  case "canceled":
  case "refunded":      // まとめて同じ処理にできる
    console.log("取り消し");
    break;
  default:              // どれにも当てはまらないとき
    console.log("不明な状態: " + status);
}
// → 発送済み

switch の比較は === と同じ厳密比較です。break の書き忘れは、初学者のみならず経験者もやる典型的な不具合なので、書いたら必ず目視で確認してください。

早期リターン ── 条件を深くしない書き方

✕ 入れ子が深い
if (user) {
  if (user.isActive) {
    if (user.age >= 18) {
      送信する();
    }
  }
}

3段になると、条件と処理の対応が追えなくなります。

◯ ダメな条件を先に返す
if (!user) return;
if (!user.isActive) return;
if (user.age < 18) return;

送信する();

「除外条件を上に並べ、本題を下に書く」。実務のコードはほぼこの形です。

return は関数の中でしか使えません(STEP 4で扱います)。今は「弾く条件を先に書く」という発想だけ覚えてください。この書き方に慣れると、あとで読みやすさが段違いになります。

動かして確かめる ── truthy / falsy 判定機
値を選んで「判定する」を押してください。

[]{}になる点が、最も間違えやすいところです。「配列が空かどうか」は arr.length === 0 で判定します。

一問一答
  1. == ではなく === を使うのはなぜですか。
  2. falsy な値を6つ挙げてください。
  3. ||?? の違いは何ですか。
  4. 空の配列 [] は条件式で真ですか、偽ですか。
解答を見る
  1. == は型を変換してから比較するため、0 == "" のような直感に反する結果になるから。
  2. false0""nullundefinedNaN
  3. || は左がfalsyなら右、?? は左がnull/undefinedのときだけ右。0や空文字を有効な値として扱いたいときは ??
  4. 真(truthy)。中身が空でも配列という値は存在するため。空かどうかは length で見る。
STEP 3 文法

繰り返しと配列の基本 ── 同じ処理をまとめる

目安 4日
このステップの到達点 ── 配列に値を出し入れでき、forfor...of を書き分けられる。「一覧データを1件ずつ処理する」という、実務で最も多い形を自分で書ける。

配列は「順番付きの箱の並び」

配列(array)は、複数の値を順番に並べて1つの変数で扱うしくみです。商品一覧、記事一覧、選択肢……実務のデータはほぼすべて配列で届きます。

配列の作成と取り出し
const fruits = ["りんご", "みかん", "ぶどう"];

console.log(fruits[0]);        // りんご  ← 番号は 0 から始まる
console.log(fruits[2]);        // ぶどう
console.log(fruits[3]);        // undefined(存在しない番号)
console.log(fruits.length);    // 3       ← 個数
console.log(fruits[fruits.length - 1]);  // ぶどう(最後の要素)
console.log(fruits.at(-1));    // ぶどう(新しい書き方)
番号は0から 1番目は [0]、2番目は [1]。この「ずれ」が原因のミスを境界の間違いと呼び、実務でも頻出します。最後の要素は length - 1 と、口に出して覚えてください。
追加・削除・検索
const list = ["A", "B", "C"];

list.push("D");          // 末尾に追加 → ["A","B","C","D"]
list.unshift("Z");       // 先頭に追加 → ["Z","A","B","C","D"]
const last = list.pop(); // 末尾を取り出して削除 → "D"
const first = list.shift();  // 先頭を取り出して削除 → "Z"
console.log(list);       // ["A","B","C"]

console.log(list.includes("B"));   // true   含まれるか
console.log(list.indexOf("C"));    // 2      何番目か(無ければ -1)
console.log(list.join(" / "));     // "A / B / C"  文字列に連結

// 一部を取り出す(元の配列は変わらない)
console.log(list.slice(0, 2));     // ["A","B"]

// 指定位置を書き換える(元の配列が変わる)
list.splice(1, 1, "X");            // 1番目から1つ消して "X" を入れる
console.log(list);                 // ["A","X","C"]
「元を変える」か「新しく作る」か pushsplicesortreverse元の配列を書き換えますsliceconcatmapfilter新しい配列を返します。この区別を意識しないと、「別の場所のデータまで変わってしまった」という原因の分かりにくい不具合になります。迷ったら、元を変えない方(新しく作る方)を選んでください。

繰り返し ── 3つの書き方

for / for...of / while
const items = ["ノート", "鉛筆", "消しゴム"];

// ① for:番号が必要なとき
for (let i = 0; i < items.length; i++) {
  console.log(`${i + 1}番目: ${items[i]}`);
}

// ② for...of:中身だけでよいとき(読みやすい。実務ではこちらが多い)
for (const item of items) {
  console.log(item);
}

// ③ while:終わる条件が回数で決まらないとき
let stock = 3;
while (stock > 0) {
  console.log(`残り${stock}個`);
  stock--;              // ← これを忘れると無限ループ
}
実行結果(①)
1番目: ノート
2番目: 鉛筆
3番目: 消しゴム

i++ は「iに1を足す」の短縮形です。i-- は1を引きます。for の3つの部分は、「開始値;続ける条件;1回ごとの変化」と読みます。

無限ループに注意 while の中で条件が変わらないと、ブラウザが固まります。固まったら、そのタブを閉じれば復帰できます(Chromeなら Ctrl/Command + W)。パソコンが壊れることはありません。

break と continue

途中で抜ける・その回だけ飛ばす
const nums = [3, 8, 0, 5, 12];

for (const n of nums) {
  if (n === 0) continue;    // 0のときはこの回を飛ばす
  if (n > 10) break;        // 10を超えたら繰り返し自体を終える
  console.log(100 / n);
}
// 33.33...  12.5  20

入れ子の繰り返しと、二次元の配列

表のようなデータを回す
const table = [
  ["名前", "点数"],
  ["山田", 82],
  ["佐藤", 91],
];

for (const row of table) {
  console.log(row.join("\t"));
}

// 九九の3の段
for (let i = 1; i <= 9; i++) {
  console.log(`3 × ${i} = ${3 * i}`);
}

繰り返しの中に繰り返しを書くと、処理の回数は掛け算で増えます(100件×100件=1万回)。件数が多いデータで三重・四重にすると、画面が固まるほど遅くなります。実務では二重までを目安にし、それ以上必要なら設計を見直します。

動かして確かめる ── 買い物リストの合計
    まだ何も追加されていません。

    中では push() で配列に足し、for...of で合計しています。価格は Number() で数値に変換してから足しています(STEP 1)。

    一問一答
    1. 配列 arr の最後の要素を取り出す書き方を2通り答えてください。
    2. forfor...of は、どう使い分けますか。
    3. slicesplice の違いは何ですか。
    4. breakcontinue の違いは何ですか。
    解答を見る
    1. arr[arr.length - 1] または arr.at(-1)
    2. 番号(何番目か)が必要なら for、中身だけでよいなら読みやすい for...of
    3. slice は元を変えずに一部を切り出す。splice は元の配列そのものを書き換える。
    4. break は繰り返し全体を終える。continue はその回だけ飛ばして次に進む。
    STEP 4 文法

    関数 ── 処理に名前を付けて、使い回す

    目安 5日
    このステップの到達点 ── 関数を定義して呼び出せる。引数・戻り値・スコープを説明でき、アロー関数を読み書きできる。ここが弱いと、この先のイベント処理も非同期処理も書けません。18ステップ中、最も重要な回です。

    関数は「材料を受け取り、結果を返す機械」

    関数とは、まとまった処理に名前を付けたものです。同じ処理を何度も書かずに済み、名前が説明になるため、コードが読みやすくなります。

    関数の考え方
    関数の引数と戻り値 引数として税抜価格1980を渡すと、calcTax関数が中でかけ算をして、戻り値として税込価格2178を返します。 引数(ひきすう)=入れるもの 戻り値(もどりち)=出てくるもの 税抜価格 1980 calcTax( ) 中でかけ算する 税込価格 2178 渡す return 呼び出すときに渡す値 処理の本体 呼び出した場所に戻る値 const 税込 = calcTax(1980); ← この「=」の右側が、上の一本道そのもの
    関数の定義と呼び出し
    // 定義(この時点では実行されない)
    function calcTax(price) {
      return Math.round(price * 1.1);
    }
    
    // 呼び出し(ここで実行される)
    console.log(calcTax(1980));   // 2178
    console.log(calcTax(500));    // 550
    
    // 戻り値は変数に入れられる
    const total = calcTax(1200) + calcTax(800);
    console.log(total);           // 2200
    • 引数(ひきすう)= 関数に渡す材料。かっこの中に書く
    • 戻り値(もどりち)= return で返す結果。返さない関数もある
    • return に到達した時点で、関数はそこで終わる(以降の行は実行されない)

    3つの書き方 ── 関数宣言・関数式・アロー関数

    同じ処理を3通りで書く
    // ① 関数宣言(もっとも基本)
    function double(n) {
      return n * 2;
    }
    
    // ② 関数式(変数に入れる)
    const double2 = function (n) {
      return n * 2;
    };
    
    // ③ アロー関数(今のJavaScriptの主流)
    const double3 = (n) => {
      return n * 2;
    };
    
    // ③の短縮形:処理が1つの式だけなら { } と return を省ける
    const double4 = (n) => n * 2;
    
    console.log(double(5), double2(5), double3(5), double4(5));  // 10 10 10 10
    書き方読み方特徴使いどころ
    function 名前() {}関数宣言定義より前の行からでも呼べる(巻き上げ)部品として独立した処理
    const f = function () {}関数式定義より前では呼べない古いコードで見かける
    const f = () => {}アロー関数短い。this の扱いが素直(STEP 7)いま書くならこれ。特に配列メソッドやイベント
    巻き上げ(ホイスティング) function で宣言した関数は、ファイル内のどこからでも呼び出せます(定義が上に持ち上げられたように振る舞うため、こう呼びます)。一方 const に入れた関数は、その行より後ろでしか呼べません。エラー Cannot access 'f' before initialization はこれが原因です。

    引数のいろいろ ── デフォルト値と可変長

    既定値・複数・残余引数
    // 既定値(渡されなかったときに使う値)
    function greet(name, suffix = "さん") {
      return `${name}${suffix}、こんにちは`;
    }
    console.log(greet("山田"));           // 山田さん、こんにちは
    console.log(greet("山田", "先生"));   // 山田先生、こんにちは
    
    // 引数が渡されないと undefined になる
    function sub(a, b) { return a - b; }
    console.log(sub(10));                 // NaN(10 - undefined)
    
    // 個数が決まらないとき(残余引数)
    function sum(...numbers) {
      let total = 0;
      for (const n of numbers) total += n;
      return total;
    }
    console.log(sum(1, 2, 3));            // 6
    console.log(sum(10, 20, 30, 40));     // 100
    引数は3つまでを目安に 引数が4つ5つと増えると、呼び出し側で順番を間違えます。多くなったらオブジェクトを1つ渡す形にします(STEP 5)。例:createUser({ name, email, age })。名前付きで渡せるので、順番を気にせず、読んで意味が分かります。

    スコープ ── 変数が見える範囲

    スコープとは、変数が有効な範囲のことです。{ } の中で const / let を宣言すると、その { } の中だけで使えます。

    見える範囲を確かめる
    const shopName = "こども商店";   // ファイル全体で見える(グローバル)
    
    function showStock() {
      const stock = 10;              // この関数の中だけ
      console.log(shopName);         // OK:外側は見える
      console.log(stock);
    }
    
    showStock();
    // console.log(stock);           // エラー:ReferenceError: stock is not defined
    
    if (true) {
      let inner = "ブロックの中";
      var old = "varは範囲が広い";
    }
    // console.log(inner);           // エラー
    console.log(old);                // "varは範囲が広い" ← varの困った挙動

    内側から外側は見えるが、外側から内側は見えない——これがスコープの基本です。var{ } を無視して関数全体に広がるため、意図しない上書きが起きます。これが「var を使わない」理由です。

    関数を渡す ── コールバック関数

    JavaScriptでは、関数そのものを引数として渡せます。渡された側があとで呼び出す関数をコールバック関数といいます。イベント処理も配列メソッドも非同期処理も、すべてこの仕組みの上に成り立っています。

    関数を引数に渡す
    function repeat(times, action) {
      for (let i = 1; i <= times; i++) {
        action(i);          // 渡された関数を呼び出す
      }
    }
    
    repeat(3, (n) => {
      console.log(`${n}回目`);
    });
    // 1回目 / 2回目 / 3回目
    
    // 「呼び出す」と「渡す」の違いに注意
    const say = () => console.log("こんにちは");
    setTimeout(say, 1000);      // ◯ 1秒後に実行される(関数を渡した)
    setTimeout(say(), 1000);    // ✕ すぐ実行され、結果(undefined)を渡している
    かっこの有無が意味を変える say関数そのものsay()関数を実行した結果です。イベント登録で「なぜかページを開いた瞬間に動く」という不具合は、ほぼ100%これが原因です。STEP 9で再度出てきます。

    良い関数の条件

    • 1つの関数は1つの仕事だけにする(名前が「〜と〜をする」になったら分割の合図)
    • 名前は動詞から始めるgetUsercalcTotalisValidEmailrenderList
    • 同じ引数なら同じ結果を返す形(純粋関数)を基本にすると、試験も修正も簡単になる
    • 20行を大きく超えたら、切り出せる部分がないか疑う
    動かして確かめる ── 関数で税込価格を計算する
    税抜価格を入れて「計算する」を押してください。

    動いているのは const calcTax = (price, rate = 0.1) => Math.round(price * (1 + rate)); というたった1行の関数です。引数の既定値も使っています。

    一問一答
    1. 引数と戻り値を、それぞれ一言で説明してください。
    2. setTimeout(say, 1000)setTimeout(say(), 1000) の違いは何ですか。
    3. 関数の中で const 宣言した変数を、関数の外から使えますか。
    4. コールバック関数とは何ですか。
    解答を見る
    1. 引数=関数に渡す材料。戻り値=return で関数から返す結果。
    2. 前者は関数そのものを渡すので1秒後に実行される。後者はその場で実行し、戻り値(undefined)を渡してしまう。
    3. 使えない。{ } の内側で宣言した変数は、その内側だけで有効(スコープ)。
    4. あとで呼び出してもらうために、引数として渡す関数のこと。
    STEP 5 文法

    オブジェクト ── 実務のデータはこの形で届く

    目安 4日
    このステップの到達点 ── オブジェクトの読み書きができ、分割代入・スプレッド・オプショナルチェーンを使える。JSONとオブジェクトの関係を説明でき、「参照のコピー」による事故を避けられる。

    オブジェクトは「名札付きの箱」

    配列が順番で値を管理するのに対し、オブジェクト名前(キー)で値を管理します。1件のデータ(ユーザー1人、商品1つ)を表すのに使います。

    オブジェクトの基本
    const user = {
      name: "山田花子",
      age: 34,
      email: "hanako@example.com",
      isAdmin: false,
    };
    
    // 読み取り:2通り
    console.log(user.name);        // 山田花子(ドット記法。基本はこちら)
    console.log(user["email"]);    // hanako@example.com(キーを変数で指定したいとき)
    
    const key = "age";
    console.log(user[key]);        // 34
    
    // 書き換え・追加・削除
    user.age = 35;
    user.tel = "03-0000-0000";     // 無いキーに代入すると追加される
    delete user.isAdmin;
    console.log(user);
    
    // 無いキーを読むと undefined(エラーにはならない)
    console.log(user.address);     // undefined
    const なのに中身を変えられる? const が禁止するのは「変数そのものの入れ替え」だけです。中身の書き換えは可能です。user = {} はエラー、user.age = 35 はOK——この区別が実務で効いてきます。

    入れ子とオブジェクトの配列 ── APIのデータの形

    実務で届くデータの典型
    const order = {
      id: 1024,
      customer: { name: "佐藤", tel: "090-0000-0000" },
      items: [
        { name: "ノート", price: 180, qty: 3 },
        { name: "鉛筆",   price: 90,  qty: 10 },
      ],
    };
    
    console.log(order.customer.name);      // 佐藤
    console.log(order.items[1].name);      // 鉛筆
    console.log(order.items.length);       // 2
    
    // 1件ずつ処理する
    for (const item of order.items) {
      console.log(`${item.name}: ${item.price * item.qty}円`);
    }
    // ノート: 540円 / 鉛筆: 900円

    サーバーから届くデータは、ほぼ必ず「オブジェクトの配列」「配列を含むオブジェクト」です。この形をたどれるかどうかが、STEP 13(API通信)でそのまま効いてきます。

    オプショナルチェーン ── 「無いかもしれない」を安全に読む

    ?. で落ちないようにする
    const data = { user: { name: "山田" } };
    
    console.log(data.user.name);          // 山田
    // console.log(data.company.name);    // エラー:Cannot read properties of undefined
    
    console.log(data.company?.name);      // undefined(エラーにならない)
    console.log(data.company?.name ?? "未登録");   // 未登録
    
    // 配列や関数にも使える
    console.log(data.items?.[0]);         // undefined
    console.log(data.save?.());           // undefined(save が無くても落ちない)
    頻出エラー Cannot read properties of undefined (reading 'name') は、「undefined の中の name を読もうとした」という意味です。つまり手前の階層が存在しないということ。?. で回避できますが、まずはなぜ手前が無いのかを確かめてください(キー名の綴り違い、データがまだ届いていない、など)。

    分割代入 ── 必要な値だけ取り出す

    よく使う書き方
    const user = { name: "山田", age: 34, city: "東京" };
    
    // 従来の書き方
    // const name = user.name;
    // const age  = user.age;
    
    // 分割代入
    const { name, age } = user;
    console.log(name, age);            // 山田 34
    
    // 別名を付ける・既定値を入れる
    const { city: pref, tel = "未登録" } = user;
    console.log(pref, tel);            // 東京 未登録
    
    // 配列でも使える
    const [first, second] = ["金", "銀"];
    console.log(first, second);        // 金 銀
    
    // 関数の引数で使うと読みやすい(実務で多用)
    function showUser({ name, age }) {
      console.log(`${name}(${age}歳)`);
    }
    showUser(user);                    // 山田(34歳)

    スプレッド構文 ── 複製と結合

    ... で広げる
    const base = { name: "山田", age: 34 };
    
    // 複製しつつ一部を上書き(元は変えない。実務で最頻出)
    const updated = { ...base, age: 35 };
    console.log(base.age, updated.age);      // 34 35
    
    // オブジェクトの結合(後ろが優先)
    const defaults = { color: "白", size: "M" };
    const input = { size: "L" };
    console.log({ ...defaults, ...input });  // { color: "白", size: "L" }
    
    // 配列でも使える
    const a = [1, 2];
    const b = [3, 4];
    console.log([...a, ...b]);               // [1,2,3,4]
    console.log(Math.max(...[5, 9, 3]));     // 9

    値のコピーと参照 ── 分かりにくい不具合の原因

    数値や文字列は値そのものが複製されますが、オブジェクトと配列は「置き場所の案内(参照)」が複製されます。つまり、代入しただけでは中身は同じ1つです。

    同じものを指してしまう例
    const a = { count: 1 };
    const b = a;          // 参照のコピー(同じオブジェクトを指す)
    b.count = 99;
    console.log(a.count); // 99 ← aまで変わってしまう
    
    // 複製したいときはスプレッド
    const c = { ...a };
    c.count = 1;
    console.log(a.count, c.count);   // 99 1
    
    // 入れ子は「浅いコピー」になる点に注意
    const org = { name: "本社", tags: ["A", "B"] };
    const copy = { ...org };
    copy.tags.push("C");
    console.log(org.tags);           // ["A","B","C"] ← 中の配列は共有されたまま
    
    // 入れ子ごと完全に複製する(深いコピー)
    const deep = structuredClone(org);
    deep.tags.push("D");
    console.log(org.tags.length, deep.tags.length);   // 3 4
    覚え方 { ...obj }1階層だけの複製(浅いコピー)。入れ子まで完全に分けたいときは structuredClone(obj)(最近のブラウザで利用可)、または JSON.parse(JSON.stringify(obj))(日付や関数が消える点に注意)。

    JSON ── データの共通語

    JSON(ジェイソン)は、データを文字列としてやり取りするための形式です。見た目はJavaScriptのオブジェクトとほぼ同じですが、あくまで文字列である点が違います。サーバーとの通信、設定ファイル、ブラウザへの保存——すべてJSONが使われます。

    オブジェクト ⇄ JSON文字列
    const user = { name: "山田", age: 34, tags: ["管理者"] };
    
    // オブジェクト → JSON文字列(送信・保存するとき)
    const json = JSON.stringify(user);
    console.log(json);
    // {"name":"山田","age":34,"tags":["管理者"]}
    
    console.log(JSON.stringify(user, null, 2));   // 読みやすく整形して出す
    
    // JSON文字列 → オブジェクト(受信・読み込むとき)
    const parsed = JSON.parse(json);
    console.log(parsed.name);      // 山田
    console.log(typeof json, typeof parsed);   // string object
    JSONのきまり内容
    キーは必ずダブルクォート{"name":"山田"}{name:"山田"} は不正
    シングルクォートは使えない文字列も必ず " で囲む
    末尾のカンマは不可{"a":1,} はエラー
    関数・undefinedは入れられないJSON.stringify では消える
    コメントは書けない設定ファイルで詰まりやすい点

    JSON.parse() は、形式が壊れていると例外を投げて止まります。通信で受け取ったJSONを解析するときは、try / catch で囲むのが実務の作法です(STEP 14)。

    一問一答
    1. 配列とオブジェクトの使い分けを一言で説明してください。
    2. const user = {...} のとき、user.age = 40 はエラーになりますか。
    3. const b = a;(aはオブジェクト)としたあと、b を変えると a はどうなりますか。
    4. JSONとJavaScriptのオブジェクトの違いは何ですか。
    解答を見る
    1. 配列は順番で管理(一覧)、オブジェクトは名前で管理(1件の中身)。
    2. エラーにならない。const が禁止するのは変数自体の再代入だけで、中身の変更は可能。
    3. a も変わる。参照(同じ実体への案内)をコピーしているため。複製するなら { ...a }
    4. JSONは文字列としてのデータ形式。キーを必ずダブルクォートで囲むなど、書き方の制約が厳しい。
    STEP 6 文法

    配列メソッド ── 実務でいちばん書く処理

    目安 5日
    このステップの到達点 ── map / filter / find / reduce / sort を使い分けられる。「一覧を絞り込み、並べ替え、表示用の形に変える」という、業務コードの中心作業を for を書かずにこなせる。

    なぜ for を卒業するのか

    for でも書けますが、実務のコードは配列メソッドで書かれます。理由は「何をしているか」が一目で分かるからです。filter と書いてあれば絞り込み、map と書いてあれば変換だと、読んだ瞬間に分かります。

    △ for で書くと
    const result = [];
    for (let i = 0; i < items.length; i++) {
      if (items[i].price >= 1000) {
        result.push(items[i].name);
      }
    }

    6行読んで、ようやく意図が分かります。

    ◯ 配列メソッドなら
    const result = items
      .filter((item) => item.price >= 1000)
      .map((item) => item.name);

    「1000円以上に絞って、名前だけ取り出す」とそのまま読めます

    使うデータ

    以下の例で共通して使う配列
    const products = [
      { id: 1, name: "ノート",   price: 180,  stock: 24, category: "文具" },
      { id: 2, name: "鉛筆",     price: 90,   stock: 0,  category: "文具" },
      { id: 3, name: "電卓",     price: 1980, stock: 5,  category: "事務" },
      { id: 4, name: "ホチキス", price: 1200, stock: 12, category: "事務" },
      { id: 5, name: "消しゴム", price: 120,  stock: 40, category: "文具" },
    ];

    forEach ── 1件ずつ処理する(戻り値なし)

    forEach
    products.forEach((product, index) => {
      console.log(`${index + 1}. ${product.name} — ${product.price}円`);
    });
    // 1. ノート — 180円
    // 2. 鉛筆 — 90円 ...

    コールバックは (要素, 添字, 配列全体) の順に受け取れます。使わない引数は書かなくて構いません。forEach結果を返さないので、新しい配列がほしいときは次の map を使います。

    map ── 同じ件数のまま、形を変える

    map(変換)
    // 名前だけの配列にする
    const names = products.map((p) => p.name);
    console.log(names);   // ["ノート","鉛筆","電卓","ホチキス","消しゴム"]
    
    // 税込価格を足したオブジェクトの配列にする
    const withTax = products.map((p) => ({
      ...p,
      taxIncluded: Math.round(p.price * 1.1),
    }));
    console.log(withTax[2].taxIncluded);   // 2178
    
    // 表示用の文字列にする(画面表示の定番)
    const rows = products.map((p) => `${p.name}:${p.price.toLocaleString()}円`);
    console.log(rows.join("\n"));
    アロー関数でオブジェクトを返すとき (p) => { ...p } と書くと、{ が「処理のかたまり」と解釈されて失敗します。丸かっこで包んで (p) => ({ ...p }) と書きます。よく詰まる点なので、ここは丸暗記して構いません。

    filter ── 条件に合うものだけ残す

    filter(絞り込み)
    // 在庫があるものだけ
    const inStock = products.filter((p) => p.stock > 0);
    console.log(inStock.length);      // 4
    
    // 条件を組み合わせる
    const cheapStationery = products.filter((p) => p.category === "文具" && p.price < 150);
    console.log(cheapStationery.map((p) => p.name));   // ["鉛筆","消しゴム"]
    
    // キーワード検索(大文字小文字を無視)
    const keyword = "ノート";
    const hit = products.filter((p) => p.name.toLowerCase().includes(keyword.toLowerCase()));
    console.log(hit);

    filter のコールバックは真偽値を返すのが決まりです。true を返した要素だけが新しい配列に残ります。該当なしのときは空配列 [] が返ります(null ではありません)。

    find / some / every ── 探す・確かめる

    1件だけ探す、条件を確かめる
    // 最初に見つかった1件(オブジェクトそのもの)
    const target = products.find((p) => p.id === 3);
    console.log(target.name);            // 電卓
    console.log(products.find((p) => p.id === 999));   // undefined(見つからない)
    
    // 何番目かを知りたいとき
    console.log(products.findIndex((p) => p.id === 3));   // 2
    
    // 1つでも当てはまるか
    console.log(products.some((p) => p.stock === 0));     // true(在庫切れがある)
    
    // 全部が当てはまるか
    console.log(products.every((p) => p.price > 50));     // true
    findfilter を混同しない find要素(1件)filter配列を返します。filter(...)[0] と書きたくなったら find の出番です。また find の結果は undefined になり得るので、そのまま .name を読むと落ちます(?. を使う)。

    reduce ── 集計する

    reduce(リデュース)は、配列を1つの値にまとめるメソッドです。合計・最大値・件数集計など、集計はこれで書きます。最初は難しく見えますが、「累積の箱を持ち回る」と理解すれば単純です。

    reduce(集計)
    // 合計金額(在庫数 × 価格)
    const total = products.reduce((sum, p) => sum + p.price * p.stock, 0);
    //                             ↑累積   ↑今の要素            ↑初期値
    console.log(total);        // 22140
    
    // 単純な合計
    console.log([1, 2, 3, 4].reduce((a, b) => a + b, 0));   // 10
    
    // カテゴリごとの件数を数える(実務で頻出)
    const countByCategory = products.reduce((acc, p) => {
      acc[p.category] = (acc[p.category] ?? 0) + 1;
      return acc;
    }, {});
    console.log(countByCategory);   // { 文具: 3, 事務: 2 }
    初期値を必ず書く 第2引数の初期値(0{})を省くと、空配列のときにエラーになります。実務では「該当0件」は普通に起きるため、初期値は必ず書いてください。

    sort ── 並べ替え(元の配列を壊す点に注意)

    sort(並べ替え)
    // ✕ そのまま使うと文字列として比較される
    console.log([10, 9, 100].sort());        // [10, 100, 9]
    
    // ◯ 数値は比較関数を渡す
    console.log([10, 9, 100].sort((a, b) => a - b));   // [9, 10, 100] 昇順
    console.log([10, 9, 100].sort((a, b) => b - a));   // [100, 10, 9] 降順
    
    // オブジェクトの配列を価格の安い順に
    const cheapFirst = [...products].sort((a, b) => a.price - b.price);
    //                 ↑ コピーしてから並べ替える(元を壊さない)
    console.log(cheapFirst.map((p) => p.price));   // [90,120,180,1200,1980]
    
    // 文字列(日本語を含む)の並べ替え
    const byName = [...products].sort((a, b) => a.name.localeCompare(b.name, "ja"));
    console.log(byName.map((p) => p.name));
    sort は元の配列を書き換えます 画面に出している元データを直接 sort すると、他の場所の表示まで並び替わります。[...arr].sort() のようにコピーしてから並べ替えるのが実務の定石です(新しい toSorted() も同じ目的のメソッドです)。

    つなげて書く ── メソッドチェーン

    絞る → 並べる → 形を変える
    const view = products
      .filter((p) => p.stock > 0)                    // 在庫あり
      .sort((a, b) => b.price - a.price)             // 高い順
      .map((p) => `${p.name}(${p.price}円)`)        // 表示用に整形
      .slice(0, 3);                                  // 上位3件
    
    console.log(view);
    // ["電卓(1980円)","ホチキス(1200円)","ノート(180円)"]

    上から下へ、データが加工されていく流れをそのまま書けるのが利点です。ただし1つの連結が5段を超えると読みづらくなるため、そのときは途中で変数に区切ります。

    動かして確かめる ── 商品一覧を絞り込む・並べ替える

    動いているのは filter().sort().map() の3行だけです。実務の一覧画面も、原理はまったく同じです。

    メソッド返すもの元の配列使う場面
    forEachなし(undefined)変えない1件ずつ何かする(表示・登録)
    map同じ件数の新配列変えない形を変える(表示用データ作り)
    filter絞った新配列変えない条件で絞る(検索・在庫あり)
    find要素1つ/undefined変えないIDで1件探す
    some / every真偽値変えない条件の確認・入力チェック
    reduceまとめた値変えない合計・集計・集約
    sort並べ替えた配列変える並べ替え(コピーしてから)
    一問一答
    1. mapfilter の違いを説明してください。
    2. find が見つからなかったときの戻り値は何ですか。
    3. [10, 9, 100].sort() の結果と、その理由を答えてください。
    4. reduce の第2引数(初期値)を省くと、どんな問題が起きますか。
    解答を見る
    1. map は件数を変えずに各要素を変換する。filter は条件に合う要素だけを残す(件数が減る)。
    2. undefined。そのままプロパティを読むとエラーになるため ?. や存在確認が必要。
    3. [10, 100, 9]。比較関数を渡さないと文字列として比較されるため。数値は (a,b) => a - b を渡す。
    4. 空配列のときにエラーになる。また集計対象の型が意図しないものになる。
    STEP 7 文法

    クラス・モジュール・this ── コードを分けて育てる

    目安 4日
    このステップの到達点 ── ファイルを分割して import / export でつなげられる。クラスの基本形を読み書きでき、this が何を指すかを場面ごとに説明できる。

    モジュール ── 1ファイル1役割に分ける

    コードが数百行になると、1つのファイルでは手に負えなくなります。JavaScriptでは、ファイルをモジュールとして分け、必要なものだけを受け渡しできます。

    tax.js(部品側)
    // 名前付きエクスポート(複数出せる)
    export const TAX_RATE = 0.1;
    
    export function calcTax(price) {
      return Math.round(price * (1 + TAX_RATE));
    }
    
    // この関数はファイルの外からは見えない(公開しない)
    function log(message) {
      console.log(`[tax] ${message}`);
    }
    
    // 既定のエクスポート(1ファイルに1つだけ)
    export default function formatPrice(price) {
      return `${price.toLocaleString()}円`;
    }
    main.js(使う側)
    import formatPrice, { calcTax, TAX_RATE } from "./tax.js";
    //     ↑既定             ↑名前付きは { } で囲む
    
    console.log(TAX_RATE);                  // 0.1
    console.log(formatPrice(calcTax(1980)));  // 2,178円
    
    // 別名を付けることもできる
    // import { calcTax as addTax } from "./tax.js";
    HTML側(type="module" が必要)
    <script type="module" src="main.js"></script>
    ファイルを直接開くと動きません type="module" のファイルは、セキュリティ上の制約(CORS)により file:// では動きません。簡易サーバー経由で開く必要があります。VS Codeの拡張機能「Live Server」を入れるか、npx serve を実行してください(STEP 15)。エラー CORS policy: Cross origin requests are only supported for protocol schemes: http... が出たら、これが原因です。

    クラス ── 同じ形のデータと処理をまとめる

    クラスの基本形
    class Cart {
      #items = [];                 // # を付けると外から触れない(プライベート)
    
      constructor(customerName) {  // new したときに1回だけ動く
        this.customerName = customerName;
      }
    
      add(name, price, qty = 1) {
        this.#items.push({ name, price, qty });
        return this;               // 自分を返すと .add().add() とつなげられる
      }
    
      get total() {                // プロパティのように読める(ゲッター)
        return this.#items.reduce((sum, i) => sum + i.price * i.qty, 0);
      }
    
      summary() {
        return `${this.customerName}様:${this.#items.length}点 / ${this.total}円`;
      }
    }
    
    const cart = new Cart("山田");
    cart.add("ノート", 180, 3).add("電卓", 1980);
    console.log(cart.total);       // 2520
    console.log(cart.summary());   // 山田様:2点 / 2520円
    読み方意味
    classクラス設計図。これ自体はデータを持たない
    newニュー設計図から実体(インスタンス)を作る
    constructorコンストラクタ作られた瞬間に一度だけ動く初期化処理
    thisディスその実体自身。クラス内では「自分のこと」
    extendsエクステンズ他のクラスを引き継ぐ(継承)

    現代のフロントエンド開発では、クラスを自分で書く場面は多くありません(関数とオブジェクトで足りることが多いため)。ただし、ライブラリやフレームワークの説明はクラスで書かれていることが多く、読めることは必須です。まずは「読めればよい」で構いません。

    this ── つまずきの定番

    this「呼び出され方」によって指すものが変わります。ここを整理しておくと、あとの事故が減ります。

    this が変わる場面
    const timer = {
      count: 0,
    
      // ✕ 通常の関数:this が別のものになり、count が増えない
      startBad() {
        setInterval(function () {
          this.count++;            // ここの this は timer ではない
          console.log(this.count); // NaN
        }, 1000);
      },
    
      // ◯ アロー関数:外側の this をそのまま引き継ぐ
      startGood() {
        setInterval(() => {
          this.count++;
          console.log(this.count); // 1, 2, 3 ...
        }, 1000);
      },
    };
    実務での結論 コールバックにはアロー関数を使う。これだけで this の問題はほぼ起きません。thisundefined になったり window を指したりしていたら、まず「通常の関数で書いていないか」を疑ってください。

    クロージャ ── 状態を閉じ込める

    クロージャとは、関数が、自分が作られたときの外側の変数を覚えている性質のことです。難しそうな名前ですが、使い道は明快です。

    外から触れないカウンター
    function createCounter() {
      let count = 0;                 // 外からは直接触れない
    
      return {
        increment: () => ++count,
        reset: () => { count = 0; },
        get current() { return count; },
      };
    }
    
    const counter = createCounter();
    counter.increment();
    counter.increment();
    console.log(counter.current);    // 2
    console.log(counter.count);      // undefined(直接は見えない)

    クロージャは、グローバル変数を増やさずに状態を持つための道具です。実務では、設定を閉じ込めた関数、1回だけ実行する処理、イベントの制御(連打防止など)で自然に使うことになります。

    一問一答
    1. export default と名前付き export の違いは何ですか。
    2. モジュールを使うHTMLで必要な指定は何ですか。
    3. コールバックにアロー関数を使うと良いのはなぜですか。
    4. new は何をする命令ですか。
    解答を見る
    1. export default は1ファイルに1つで、読み込む側が名前を自由に付ける。名前付きは複数出せて、{ } で同じ名前で受け取る。
    2. <script type="module" src="...">。加えて、http経由(簡易サーバー)で開く必要がある。
    3. アロー関数は外側の this をそのまま引き継ぐため、this がずれる不具合を避けられるから。
    4. クラス(設計図)から実体を作り、constructor を1回実行する。
    STEP 8 画面

    DOM操作 ── 画面を読み取る・書き換える

    目安 4日
    このステップの到達点 ── HTMLの要素を取得し、文字・見た目・属性を書き換えられる。要素を新しく作って一覧を組み立てられる。textContentinnerHTML の使い分けを、安全面の理由とともに説明できる。

    DOMとは「HTMLをJavaScriptから触れる形にしたもの」

    ブラウザはHTMLを読み込むと、それを木のような構造に組み立てます。これをDOM(ドム/Document Object Model)といいます。JavaScriptから触るのは、HTMLの文字列ではなく、この組み立てられた木です。

    HTMLとDOMの対応
    HTMLとDOMの木構造の対応 bodyの中にh1とulがあり、ulの中にliが2つあるHTMLは、documentを根として、body、h1、ul、liがぶら下がる木構造としてブラウザに読み込まれます。 ① 書いたHTML <body> <h1 id="title"> <ul class="list"> <li>りんご</li> <li>みかん</li> </ul> </body> ブラウザが 組み立てる ② JavaScriptから触れる形(DOM) document body h1#title ul.list li「りんご」 li「みかん」 querySelector で この節を探し出す JavaScriptは document を入口に、この木をたどって書き換える。 触っているのは「HTMLの文字列」ではなく「組み立てられた木」。

    要素を取得する ── querySelector を覚えれば足りる

    取得の基本
    // CSSと同じ書き方で指定できる(最初に見つかった1つ)
    const title = document.querySelector("#title");      // id
    const box   = document.querySelector(".card");       // class
    const first = document.querySelector("ul li");       // 子孫
    
    // 当てはまるものを全部(配列のようなもの)
    const items = document.querySelectorAll(".list li");
    console.log(items.length);
    items.forEach((li) => console.log(li.textContent));
    
    // 古い書き方(今も動くが、querySelectorで統一してよい)
    const t2 = document.getElementById("title");         // # は書かない
    
    // 見つからないと null が返る
    const nothing = document.querySelector("#no-such-id");
    console.log(nothing);        // null
    頻出エラー:Cannot read properties of null 「取得できていない(null)ものを操作した」という意味です。原因はほぼこの3つです。①idやclassの綴り違い ②#. の付け忘れ ③HTMLより先にJavaScriptが動いているscript の位置、または defer の付け忘れ)。

    中身を書き換える ── textContent と innerHTML

    文字を入れる2つの方法
    const box = document.querySelector("#box");
    
    // ① 文字として入れる(安全。原則こちら)
    box.textContent = "こんにちは";
    
    // ② HTMLとして解釈させる(必要なときだけ)
    box.innerHTML = "<b>強調</b>した文字";
    
    // 違いが分かる例
    const userInput = "<img src=x onerror=alert('攻撃')>";
    box.textContent = userInput;   // そのまま文字として表示される(安全)
    box.innerHTML  = userInput;    // タグとして実行されてしまう(危険)
    実務の鉄則 利用者が入力した値を innerHTML に入れてはいけません。これは XSS(クロスサイトスクリプティング)という代表的な攻撃の入口になります。原則 textContent、どうしてもHTMLが必要なときは、自分が作った安全な文字列に限る——これを徹底してください(STEP 16で詳述)。

    見た目を変える ── classList を使う

    スタイルはCSSに任せ、JSはクラスを付け外しする
    const card = document.querySelector(".card");
    
    card.classList.add("is-active");        // 付ける
    card.classList.remove("is-hidden");     // 外す
    card.classList.toggle("is-open");       // あれば外す・なければ付ける
    console.log(card.classList.contains("is-active"));   // true
    
    // 直接スタイルを当てることもできる(多用しない)
    card.style.display = "none";
    card.style.backgroundColor = "#fff";    // CSSのbackground-color → キャメルケース
    なぜ classList を優先するのか 見た目の定義がCSSに集まり、JavaScriptは「状態の切り替え」だけを担当できるからです。style を直接書くと、色や余白の指定がJSとCSSに散らばり、修正のとき両方を探すことになります。クラス名は is- で始めるのが分かりやすい慣習です。

    属性と入力値

    属性・value・data属性
    const img = document.querySelector("#photo");
    img.src = "cat.jpg";                      // よく使う属性は直接書ける
    img.alt = "ねこの写真";
    img.setAttribute("width", "320");         // 一般的な書き方
    console.log(img.getAttribute("src"));
    
    const input = document.querySelector("#name");
    console.log(input.value);                 // 入力欄の中身は value
    input.value = "初期値";
    input.disabled = true;                    // 真偽値の属性
    
    const check = document.querySelector("#agree");
    console.log(check.checked);               // チェック状態
    
    // 自由に情報を持たせる data 属性(実務で多用)
    // HTML: <button data-id="12" data-action="delete">削除</button>
    const btn = document.querySelector("button");
    console.log(btn.dataset.id);              // "12"(文字列で来る点に注意)
    console.log(btn.dataset.action);          // "delete"

    要素を作って追加する ── 一覧表示の基本形

    配列からリストを組み立てる
    const fruits = ["りんご", "みかん", "ぶどう"];
    const ul = document.querySelector("#fruit-list");
    
    ul.textContent = "";                  // いったん空にする(重複表示の防止)
    
    for (const fruit of fruits) {
      const li = document.createElement("li");   // 要素を作る
      li.textContent = fruit;                    // 中身を入れる
      li.classList.add("fruit-item");
      ul.appendChild(li);                        // 親に追加する
    }
    
    // 件数が多いときは、まとめて追加すると速い
    const frag = document.createDocumentFragment();
    for (const fruit of fruits) {
      const li = document.createElement("li");
      li.textContent = fruit;
      frag.appendChild(li);
    }
    ul.appendChild(frag);                        // 画面の描き直しが1回で済む
    「毎回まっさらにして描き直す」 一覧を更新するとき、差分を計算しようとすると難しくなります。実務でも、まず textContent = "" で空にしてから作り直す方法が基本です(件数が数百程度なら十分に速い)。ReactやVueといったフレームワークは、この「描き直し」を自動化・効率化する道具だと理解すると腑に落ちます。

    要素の削除と移動

    消す・入れ替える
    const target = document.querySelector("#temp");
    target.remove();                          // 自分自身を消す
    
    const list = document.querySelector("#list");
    list.replaceChildren();                   // 子要素を全部消す(新しい書き方)
    
    // 親・子・兄弟をたどる
    console.log(target.parentElement);        // 親
    console.log(list.children.length);        // 子の数
    console.log(target.closest(".card"));     // 上にたどって最初に見つかる .card

    closest() は、STEP 9のイベント委譲で必ず使います。「押されたボタンが、どの行のものか」を特定するときの定番です。

    動かして確かめる ── DOM操作の基本セット
    ここが書き換わります

      「目立たせる」は classList.toggle()、「リストに追加」は createElement()appendChild() です。入力値は textContent で入れているので、タグを入力しても文字として表示されます(安全)。試しに <b>太字</b> と入力してみてください。

      一問一答
      1. textContentinnerHTML は、どう使い分けますか。
      2. Cannot read properties of null というエラーの原因を3つ挙げてください。
      3. 見た目を変えるとき style より classList が推奨されるのはなぜですか。
      4. 入力欄の中身を取り出すプロパティは何ですか。
      解答を見る
      1. 原則 textContent(文字として安全に入る)。HTMLとして解釈させたいときだけ innerHTML。利用者の入力は入れない。
      2. 綴り違い/#. の付け忘れ/HTMLが読み込まれる前にJavaScriptが動いている。
      3. 見た目の定義をCSSに集約でき、JavaScriptは状態の切り替えだけを担当できるから。
      4. value(チェックボックスは checked)。
      STEP 9 画面

      イベント ── 利用者の操作に反応する

      目安 4日
      このステップの到達点 ── addEventListener でクリックや入力に反応でき、イベントオブジェクトから必要な情報を取り出せる。イベント委譲を使って、あとから増えた要素にも反応できる。

      基本の形

      addEventListener
      const btn = document.querySelector("#btn");
      
      btn.addEventListener("click", () => {
        console.log("押されました");
      });
      //                  ↑種類      ↑実行したい関数(コールバック)
      
      // 名前付き関数を渡してもよい(あとで解除できる)
      function onClick() { console.log("クリック"); }
      btn.addEventListener("click", onClick);
      btn.removeEventListener("click", onClick);   // 解除
      
      // ✕ よくある間違い:かっこを付けると、その場で実行されてしまう
      // btn.addEventListener("click", onClick());
      イベント名いつ起きるか主な対象
      clickクリック(スマホのタップも含む)ボタン、リンク、任意の要素
      input入力するたびに(1文字ごと)入力欄
      change入力を終えて確定したとき選択欄、チェックボックス
      submitフォームが送信されたときform
      keydownキーが押されたとき入力欄、document
      focus / blur入力欄に入った/離れたとき入力欄
      DOMContentLoadedHTMLの読み込みが完了したときdocument

      イベントオブジェクト ── 何が起きたかを受け取る

      event(e)から情報を取る
      const input = document.querySelector("#search");
      
      input.addEventListener("input", (e) => {
        console.log(e.target.value);      // 入力された値
        console.log(e.type);              // "input"
      });
      
      document.addEventListener("keydown", (e) => {
        if (e.key === "Escape") console.log("Escが押された");
        if (e.key === "Enter" && e.ctrlKey) console.log("Ctrl+Enter");
      });
      
      // リンクの既定の動作(ページ移動)を止める
      document.querySelector("#link").addEventListener("click", (e) => {
        e.preventDefault();               // 移動しない
        console.log("独自の処理をする");
      });

      e.target実際に操作された要素e.currentTargetイベントを登録した要素です。ふだんは e.target で足りますが、次のイベント委譲でこの違いが効いてきます。

      バブリングとイベント委譲

      要素で起きたイベントは、親へ親へと伝わっていきます。これをバブリング(泡が上がるようす)といいます。この性質を使うと、親に1つ登録するだけで、中の子すべての操作を受け取れます。これがイベント委譲です。

      クリックが伝わる順(バブリング)
      イベントのバブリング buttonで起きたクリックは、まずbutton(e.target)、次に親のul#list、さらにdocumentへと外側に向かって伝わります。親のul#listで1つだけ受け取れば、中のどのボタンの操作も処理できます。 document ul#list ← ここに1つだけ登録する li 削除ボタン ← クリック button で発生(e.target) 親の ul#list に伝わる ここで受け取るのがイベント委譲 さらに document へ伝わる ※ 内側から外側へ「泡が上がる」ように伝わるので、バブリングと呼びます。
      あとから増える要素にも効く書き方
      const list = document.querySelector("#list");
      
      // ✕ これだと、登録した時点で存在する要素にしか効かない
      // document.querySelectorAll(".delete").forEach((b) => b.addEventListener(...));
      
      // ◯ 親に1つだけ登録する(イベント委譲)
      list.addEventListener("click", (e) => {
        const button = e.target.closest(".delete");
        if (!button) return;                  // 関係ない場所のクリックは無視
      
        const id = button.dataset.id;
        console.log(`${id} を削除します`);
        button.closest("li").remove();
      });
      実務での効果 100行の表に削除ボタンがあるとき、100個の登録は不要で、親に1つで済みます。しかもあとから追加された行にも自動で効きます。「動的に増える要素にイベントが効かない」という詰まりは、これで解決します。

      連打・入力しすぎへの対策(デバウンス)

      入力が止まってから処理する
      function debounce(fn, delay = 300) {
        let timerId;
        return (...args) => {
          clearTimeout(timerId);              // 前回の予約を取り消す
          timerId = setTimeout(() => fn(...args), delay);
        };
      }
      
      const search = document.querySelector("#search");
      const onSearch = debounce((e) => {
        console.log("検索:", e.target.value); // 入力が止まって0.3秒後に1回だけ
      }, 300);
      
      search.addEventListener("input", onSearch);

      検索欄で1文字ごとにサーバーへ問い合わせると、10文字で10回の通信になります。デバウンスは、こうした無駄をなくす定番の型です。STEP 7のクロージャがそのまま使われている点にも注目してください。

      動かして確かめる ── クリック・入力・委譲
      0

        一覧の「完了」「削除」は、親に1つだけ登録したイベント委譲で処理しています。何件追加しても登録は増えません。下の黒い枠に、発生したイベントを記録しています。

        一問一答
        1. addEventListener("click", onClick)addEventListener("click", onClick()) の違いは何ですか。
        2. イベント委譲とは何で、どんな利点がありますか。
        3. e.targete.currentTarget の違いは何ですか。
        4. 検索欄で1文字ごとの通信を防ぐ手法を何といいますか。
        解答を見る
        1. 前者は関数を渡す(正しい)。後者はその場で実行して戻り値を渡してしまう(ページを開いた瞬間に動く原因)。
        2. 親要素に1つだけイベントを登録し、バブリングで届いた子の操作を処理する方法。登録が1つで済み、あとから追加された要素にも効く。
        3. e.target は実際に操作された要素、e.currentTarget はイベントを登録した要素。
        4. デバウンス(一定時間入力が止まってから1回だけ実行する)。
        STEP 10 画面

        フォーム ── 入力を受け取り、確かめて、使える形にする

        目安 5日
        このステップの到達点 ── フォームの送信を横取りし、各入力値を取り出して入力チェック(バリデーション)を行い、エラーを画面に表示できる。ここまでできれば、問い合わせ画面・申込画面は一人で作れます。実務で最も依頼の多い部分です。

        まずHTMLを正しく書く ── ここで9割決まる

        フォームは、JavaScriptを書く前にHTMLの作りが正しいことが前提です。特に labelname は、実装のしやすさと使いやすさの両方に直結します。

        form.html(骨組み)
        <form id="contact-form" novalidate>
          <div class="field">
            <label class="field__label" for="name">お名前<span class="req">必須</span></label>
            <input class="input" type="text" id="name" name="name" autocomplete="name">
            <p class="field__error" data-error-for="name"></p>
          </div>
        
          <div class="field">
            <label class="field__label" for="email">メールアドレス<span class="req">必須</span></label>
            <input class="input" type="email" id="email" name="email" autocomplete="email">
            <p class="field__error" data-error-for="email"></p>
          </div>
        
          <div class="field">
            <label class="field__label" for="type">お問い合わせ種別</label>
            <select class="select" id="type" name="type">
              <option value="">選択してください</option>
              <option value="question">ご質問</option>
              <option value="request">ご要望</option>
            </select>
          </div>
        
          <button class="btn" type="submit">確認する</button>
        </form>
        • labelfor と入力欄の id をそろえる:ラベルを押すと入力欄に移動でき、読み上げソフトでも項目名が伝わります
        • name を付ける:まとめて値を取り出すときの鍵になります
        • type を適切にemailtel にすると、スマホで最適なキーボードが出ます
        • novalidate:ブラウザ既定のエラー表示を止め、自分で制御するための指定です
        • autocomplete:入力補完が効き、利用者の手間が大きく減ります

        送信を横取りする ── submitイベント

        ページ遷移を止めて、自分で処理する
        const form = document.querySelector("#contact-form");
        
        form.addEventListener("submit", (e) => {
          e.preventDefault();          // ← これが無いとページが再読み込みされる
        
          // ① 値をまとめて取り出す
          const formData = new FormData(form);
          const values = Object.fromEntries(formData.entries());
          console.log(values);         // { name: "山田", email: "...", type: "question" }
        
          // ② 1つずつ取り出すこともできる
          const name = form.elements.name.value.trim();
          console.log(name);
        });
        いちばん多いつまずき e.preventDefault() を書き忘れると、送信ボタンを押した瞬間にページが再読み込みされ、コンソールのログも消えます。「押しても何も起きない(一瞬光って戻る)」ときは、まずこれを疑ってください。
        また、click ではなく submit で受けるのが正解です。submit なら、入力欄でEnterキーを押したときにも同じ処理が動きます。

        入力チェック ── 検証は「関数の集まり」で書く

        入力チェックは、条件を if で延々と並べると手に負えなくなります。「1項目=1つの検証関数」に分けると、増えても壊れません。

        validate.js(検証部分)
        // 個々の検証は、エラー文(問題なければ空文字)を返す関数にする
        const validators = {
          name(value) {
            if (!value) return "お名前を入力してください。";
            if (value.length > 50) return "お名前は50文字以内で入力してください。";
            return "";
          },
          email(value) {
            if (!value) return "メールアドレスを入力してください。";
            // 「@があり、その後ろに . がある」程度の緩い確認で十分
            if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
              return "メールアドレスの形式が正しくありません。";
            }
            return "";
          },
          tel(value) {
            if (!value) return "";                     // 任意項目なので空はOK
            const digits = value.replace(/[^0-9]/g, ""); // 数字だけ取り出す
            if (digits.length < 10 || digits.length > 11) {
              return "電話番号は10桁または11桁で入力してください。";
            }
            return "";
          },
          message(value) {
            if (!value) return "お問い合わせ内容を入力してください。";
            if (value.length < 10) return "10文字以上で入力してください。";
            if (value.length > 500) return "500文字以内で入力してください。";
            return "";
          },
          agree(checked) {
            return checked ? "" : "個人情報の取り扱いに同意してください。";
          },
        };
        エラーの表示と送信処理
        function showError(fieldName, message) {
          const input = form.elements[fieldName];
          const field = input.closest(".field");
          const errorBox = field.querySelector(".field__error");
        
          field.classList.toggle("is-error", message !== "");
          errorBox.textContent = message;
          // 読み上げソフトにも異常を伝える
          input.setAttribute("aria-invalid", message !== "" ? "true" : "false");
        }
        
        form.addEventListener("submit", (e) => {
          e.preventDefault();
        
          const values = {
            name: form.elements.name.value.trim(),
            email: form.elements.email.value.trim(),
            message: form.elements.message.value.trim(),
            agree: form.elements.agree.checked,
          };
        
          // すべて検証し、エラーがあるかを集計する
          let firstErrorField = null;
          for (const [key, validate] of Object.entries(validators)) {
            if (values[key] === undefined) continue;
            const message = validate(values[key]);
            showError(key, message);
            if (message && !firstErrorField) firstErrorField = key;
          }
        
          if (firstErrorField) {
            form.elements[firstErrorField].focus();   // 最初のエラー項目へ移動
            return;                                   // 送信しない
          }
        
          console.log("送信内容:", values);           // 実際はここでサーバーへ送る(STEP 13)
        });
        エラー表示の作法どの項目が問題かを、その入力欄のすぐ下に書く ②どうすればよいかを書く(「不正な値です」ではなく「10文字以上で入力してください」) ③色だけに頼らない(赤色は色覚特性のある方に伝わらないことがあるため、必ず文章も出す) ④送信時に最初のエラー項目へ移動する。この4点はそのまま実務の品質基準です。
        入力中に赤くしない 入力の1文字目から「形式が違います」と出ると、まだ書いている途中の利用者に不快感を与えます。実務の定番は、①最初の検証は送信時 ②一度エラーになった項目だけ、入力するたびに再検証して解除するという組み合わせです。下のデモはこの動きで作ってあります。

        実際に動くフォーム

        ここまでの内容をすべて使った、動作するフォームです。わざと空のまま送信したり、メールアドレスを abc だけにしたりして、エラーの出方を確かめてください。

        動かして確かめる ── 入力チェック付きの問い合わせフォーム

        確認のご連絡に使用します。半角で入力してください。

        0 / 500

        このデモはどこにも送信しません。入力した内容はこのページの中だけで処理され、押した瞬間に画面下へ表示されます。実際の送信はSTEP 13の fetch で行います。

        チェックボックス・ラジオ・複数選択の取り出し方

        入力の種類ごとの取り方
        // 単一のチェックボックス
        const agree = form.elements.agree.checked;          // true / false
        
        // 同じ name のチェックボックス(複数選択)
        const checked = form.querySelectorAll('input[name="tags"]:checked');
        const tags = [...checked].map((el) => el.value);    // ["A","C"]
        
        // ラジオボタン(選ばれている1つ)
        const plan = form.querySelector('input[name="plan"]:checked')?.value ?? "";
        
        // 選択欄
        const type = form.elements.type.value;
        
        // まとめて取る(複数選択にも対応)
        const data = new FormData(form);
        console.log(data.getAll("tags"));                    // ["A","C"]

        入力値の整形 ── 送る前にそろえる

        よく使う整形処理
        const raw = "  ヤマダ ハナコ  ";
        
        const trimmed = raw.trim();                       // 前後の空白を除く
        const noSpace = trimmed.replace(/\s+/g, " ");     // 連続する空白を1つに
        const halfWidth = "03-1234".replace(/[0-9]/g, (c) =>
          String.fromCharCode(c.charCodeAt(0) - 0xFEE0)); // 全角数字→半角
        console.log(halfWidth);                            // 03-1234
        
        const digitsOnly = "03-1234-5678".replace(/[^0-9]/g, "");   // "0312345678"
        const lowerEmail = "TARO@Example.COM".toLowerCase();        // "taro@example.com"
        画面側のチェックは「親切」であって「安全」ではありません ブラウザ側の検証は、利用者に早く知らせるための機能です。ブラウザの検証はいくらでも回避できるため、サーバー側でも必ず同じ検証を行う必要があります。「フロントで見ているから大丈夫」は、実務では通用しません。
        一問一答
        1. フォームの処理を click ではなく submit で受けるのはなぜですか。
        2. e.preventDefault() を書き忘れると何が起きますか。
        3. 入力チェックのエラー文で気をつけることを2つ挙げてください。
        4. 画面側で検証していても、サーバー側の検証が必要なのはなぜですか。
        解答を見る
        1. 入力欄でEnterキーを押した送信も含めて、すべての送信操作を1か所で受け取れるから。
        2. ページが再読み込みされ、処理結果もコンソールのログも消える。
        3. 「どの項目か」と「どう直せばよいか」を書く/色だけで示さず文章でも伝える(他に、最初のエラー項目へ移動させる、など)。
        4. ブラウザ側の検証は利用者側で回避できるため。安全性の担保はサーバー側でしかできない。
        STEP 11 通信

        非同期処理のしくみ ── 待たずに進む理由

        目安 3日
        このステップの到達点 ── 「JavaScriptは1本の線で動く」ことを理解し、なぜ結果が期待と違う順番で出るのかを説明できる。setTimeout の挙動と、コールバック地獄が起きる理由が分かる。

        JavaScriptは、同時に2つのことをしない

        JavaScriptの処理は1本の線(シングルスレッド)で進みます。つまり、重い処理を書くと、その間は画面のクリックも反応しなくなります

        ところが、通信は数秒かかることがあります。そのあいだ画面が固まっては使い物になりません。そこでJavaScriptは、時間のかかる処理を外(ブラウザ側)に預けて、自分は次の行へ進むという方式をとります。これが非同期処理です。

        同期と非同期の違い
        同期処理と非同期処理の違い 同期は、注文してから受け取るまで待ち続け、その間は何も操作できません。非同期は、時間のかかる処理をブラウザに預けて先に進み、終わったら通知を受けて結果を受け取ります。 【同期】 注文する 出来上がるまで、ただ待つ この間、画面の操作にも反応できない 受け取る 待ち時間ぶん、 画面が固まる 【非同期】 注文する 先に進んで、ほかの作業をする 画面の操作にも反応できる 受け取る 待ち時間に、 別の処理を進められる ブラウザ側(通信・タイマー) 時間のかかる仕事は、ここが引き受ける 預ける 終わったら通知(コールバック)
        実行される順番を確かめる
        console.log("① 開始");
        
        setTimeout(() => {
          console.log("② 1秒後の処理");
        }, 1000);
        
        setTimeout(() => {
          console.log("③ 0秒の処理");
        }, 0);
        
        console.log("④ 終了");
        実行結果
        ① 開始
        ④ 終了
        ③ 0秒の処理
        ② 1秒後の処理

        ③は「0秒後」なのに④より後です。setTimeout に渡した処理は、いま書いている処理がすべて終わってから実行されるためです。この順番管理のしくみをイベントループと呼びます。「0秒 = すぐ」ではなく「0秒 = 手が空いたらすぐ」と理解してください。

        なぜ「結果が undefined になる」のか

        非同期でいちばん多い間違いは、まだ届いていない結果を使おうとすることです。

        典型的な失敗
        function getUser() {
          let user;
          setTimeout(() => {
            user = { name: "山田" };     // 1秒後に入る
          }, 1000);
          return user;                   // ← まだ undefined のまま返してしまう
        }
        
        console.log(getUser());          // undefined

        これは書き方の問題ではなく、時間の問題です。1秒後に届く値を、いま返すことはできません。解決策は2つあり、順に発展してきました。

        1. コールバック:「届いたら、この関数を呼んでね」と渡しておく(古い方式)
        2. Promise / async・await:「届いたら受け取る約束」を返す(現在の主流。STEP 12)
        コールバック方式
        function getUser(callback) {
          setTimeout(() => {
            callback({ name: "山田" });      // 届いた時点で呼ぶ
          }, 1000);
        }
        
        getUser((user) => {
          console.log(user.name);            // 1秒後に「山田」
        });

        コールバック地獄 ── なぜ Promise が生まれたか

        順番に3つ処理したいとき(古い書き方)
        getUser(1, (user) => {
          getOrders(user.id, (orders) => {
            getDetail(orders[0].id, (detail) => {
              console.log(detail);
              // さらに続くと、右へ右へ深くなっていく……
            }, onError);
          }, onError);
        }, onError);

        処理が増えるほど右へ深くなり、エラー処理も各段に書く必要があります。これをコールバック地獄と呼びます。この読みにくさを解消するために作られたのが、次のステップの Promiseasync / await です。

        タイマー関連の道具 setTimeout(fn, ms)=1回だけ遅らせる/setInterval(fn, ms)=繰り返す/clearTimeout(id)clearInterval(id)=止める。setInterval は必ず止める手段を用意すること。止め忘れると、画面を離れたあとも動き続け、動作が重くなる原因になります。
        動かして確かめる ── 実行の順番

        同期処理(①④)が先に出て、setTimeout(0)(③)、1秒後(②)の順に記録されます。時刻も一緒に出しているので、実際の間隔を確かめてください。

        一問一答
        1. JavaScriptが「シングルスレッド」であるとは、どういう意味ですか。
        2. setTimeout(fn, 0) は、なぜすぐには実行されないのですか。
        3. 非同期処理の結果が undefined になるのは、なぜですか。
        解答を見る
        1. 同時に1つの処理しか実行しないということ。重い処理の間は、画面の操作にも反応できない。
        2. いま実行中の処理がすべて終わってから、順番待ちの列(イベントループ)で実行されるため。
        3. 結果が届く前に、その値を読んで(返して)しまっているため。時間差を前提にした書き方が必要になる。
        STEP 12 通信

        Promiseとasync/await ── 非同期を素直に書く

        目安 4日
        このステップの到達点 ── async / await で非同期処理を上から下へ読める形に書け、try / catch でエラーを受け止められる。複数の処理を同時に走らせる書き方(Promise.all)も使える。

        Promise は「あとで結果を渡す約束」

        Promise(プロミス)は、まだ結果が出ていない処理を表すオブジェクトです。状態は3つしかありません。

        状態読み方意味受け取り方
        pendingペンディング処理中(結果待ち)
        fulfilledフルフィルド成功。結果の値がある.then() / await
        rejectedリジェクテッド失敗。理由(エラー)がある.catch() / try...catch
        Promiseを作る・使う
        // 指定ミリ秒待つ関数(実務でもよく自作する)
        function sleep(ms) {
          return new Promise((resolve) => setTimeout(resolve, ms));
        }
        
        // 成功・失敗する処理の例
        function fetchUser(id) {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              if (id > 0) resolve({ id, name: "山田" });   // 成功 → resolve
              else reject(new Error("IDが不正です"));       // 失敗 → reject
            }, 500);
          });
        }
        
        // .then / .catch で受け取る
        fetchUser(1)
          .then((user) => console.log(user.name))
          .catch((error) => console.error(error.message))
          .finally(() => console.log("完了(成功でも失敗でも通る)"));

        async / await ── 上から下へ読める形にする

        .then() のつなぎ書きも動きますが、実務では async / await で書きます。同期処理と同じ見た目になり、圧倒的に読みやすいためです。

        △ .then のつなぎ書き
        fetchUser(1)
          .then((user) => fetchOrders(user.id))
          .then((orders) => console.log(orders))
          .catch((e) => console.error(e));

        途中の値を使い回したいとき、書き方が急に難しくなります。

        ◯ async / await
        try {
          const user = await fetchUser(1);
          const orders = await fetchOrders(user.id);
          console.log(user, orders);
        } catch (e) {
          console.error(e);
        }

        上から順に読めて、途中の値も普通の変数として使えます。

        async / await の書き方
        // ① 関数の前に async を付ける
        async function showUser(id) {
          try {
            // ② Promiseを返す処理の前に await を付ける(結果が出るまで待つ)
            const user = await fetchUser(id);
            console.log(user.name);
            return user;                    // asyncの戻り値は自動的にPromiseになる
          } catch (error) {
            // ③ 失敗はここに来る
            console.error("取得に失敗:", error.message);
            return null;
          } finally {
            console.log("処理終了");
          }
        }
        
        // アロー関数なら
        const showUser2 = async (id) => { /* ... */ };
        
        // 呼び出し側でも結果を待てる
        const user = await showUser(1);
        覚えること内容
        awaitasync の中で関数に async を付けないと使えない(モジュール直下では可)
        async 関数は必ずPromiseを返すreturn 1 と書いても、呼び出し側では await が必要
        エラーは try / catchawait した処理が失敗すると例外として飛んでくる
        await は「待つ」その関数の中だけが止まる。ページ全体は固まらない

        同時に走らせる ── Promise.all

        await を並べると、1つずつ順番に待ってしまいます。互いに関係のない処理は、同時に走らせるほうが圧倒的に速くなります。

        直列と並列
        // ✕ 直列:1秒 + 1秒 + 1秒 = 約3秒
        const a = await sleep(1000);
        const b = await sleep(1000);
        const c = await sleep(1000);
        
        // ◯ 並列:同時に走るので約1秒
        const [x, y, z] = await Promise.all([sleep(1000), sleep(1000), sleep(1000)]);
        
        // 1つでも失敗したら全体が失敗する(在庫と価格の両方が必要、など)
        try {
          const [user, orders] = await Promise.all([fetchUser(1), fetchOrders(1)]);
        } catch (e) {
          console.error("どれかが失敗しました", e);
        }
        
        // 失敗しても他の結果はほしいとき
        const results = await Promise.allSettled([fetchUser(1), fetchUser(-1)]);
        console.log(results);
        // [{status:"fulfilled", value:{...}}, {status:"rejected", reason:Error}]
        使い分け 前の結果を次で使うなら直列await を並べる)。互いに独立しているなら並列Promise.all)。画面の初期表示で3つのAPIを叩くような場面では、並列にするだけで体感速度が3倍変わります。

        よくある間違い

        見つけにくい3つの罠
        // ① await の付け忘れ → Promiseオブジェクトがそのまま入る
        const user = fetchUser(1);
        console.log(user.name);          // undefined
        console.log(user);               // Promise { <pending> } ← この表示が出たら付け忘れ
        
        // ② forEach の中の await は待ってくれない
        [1, 2, 3].forEach(async (id) => {
          await fetchUser(id);           // 外側は待たずに次へ進む
        });
        console.log("終わった");          // ← 実際はまだ終わっていない
        
        // ◯ 順番に待つなら for...of
        for (const id of [1, 2, 3]) {
          await fetchUser(id);
        }
        // ◯ 同時でよいなら map + Promise.all
        await Promise.all([1, 2, 3].map((id) => fetchUser(id)));
        
        // ③ catch を書かない → 握りつぶされる(Unhandled promise rejection)
        fetchUser(-1);                   // 失敗しても誰も気づかない
        コンソールに Promise { <pending> } と出たら それは await の付け忘れです。非同期の不具合の大半がこれで、しかもエラーが出ずに undefined が伝わっていくため、原因が遠くで現れます。「値が undefined」で困ったら、まず await の有無を確認してください。
        動かして確かめる ── 直列と並列の速さの違い

        1件あたり0.8秒かかる処理を3件実行します。直列は約2.4秒、並列は約0.8秒。同じ処理内容でも、書き方だけで3倍違うことを体感してください。

        一問一答
        1. Promiseの3つの状態を挙げてください。
        2. await はどこで使えますか。
        3. コンソールに Promise { <pending> } と表示されたとき、何を疑いますか。
        4. 独立した3つの通信を速く終わらせるには、どう書きますか。
        解答を見る
        1. pending(処理中)、fulfilled(成功)、rejected(失敗)。
        2. async を付けた関数の中(モジュールの最上位でも可)。
        3. await の付け忘れ。Promiseそのものを値として扱ってしまっている。
        4. await Promise.all([...]) で並列に実行する。
        STEP 13 通信

        fetchとAPI通信 ── サーバーとデータをやり取りする

        目安 5日
        このステップの到達点 ── fetch でデータを取得・送信でき、読み込み中・成功・失敗・0件の4状態を画面に出し分けられる。ステータスコードとCORSの意味を説明できる。ここまでで「画面を作る仕事」の基本形は完成です。

        APIとは「データをくれる窓口」

        API(エーピーアイ)とは、プログラムから呼び出せる窓口のことです。Web開発では、決められたURLにお願いを送ると、JSONでデータが返ってくるしくみを指すのがふつうです。

        通信の流れ
        fetchによるAPI通信の流れ ブラウザのJavaScriptがGETリクエストを送り、サーバーがデータを調べてJSONを200 OKで返し、ブラウザがそれを解釈して画面に表示します。待っている間は読み込み中の表示を出します。 ブラウザ(あなたのJS) サーバー ① GET /api/products ② データを調べる ③ JSON を返す(200 OK) ④ 画面に表示する 待っている間は「読み込んでいます…」 失敗・0件のときの表示も必ず用意する ※ 本文をJavaScriptのオブジェクトに変換するのが res.json()。これも待つ処理(await)。
        メソッド読み方用途
        GETゲット取得する商品一覧を読む
        POSTポスト新規に登録する問い合わせを送信する
        PUT / PATCHプット/パッチ更新する(全体/一部)プロフィールを変更する
        DELETEデリート削除するカートから外す

        fetch の基本 ── GETで取得する

        最小の形
        async function getProducts() {
          const response = await fetch("https://example.com/api/products");
        
          // ★重要:404や500でも例外にならない。自分で確認する必要がある
          if (!response.ok) {
            throw new Error(`通信に失敗しました(${response.status})`);
          }
        
          const data = await response.json();   // 本文をJSONとして解釈(これも非同期)
          return data;
        }
        
        const products = await getProducts();
        console.log(products);
        最重要の注意点 fetch は、サーバーが404や500を返しても「成功」として扱います(通信自体はできたため)。例外になるのは、ネットワーク断など通信そのものが成立しなかったときだけです。if (!response.ok) throw ... を必ず書く——これは実務レビューで最も指摘される点です。
        コード意味画面での扱い
        200成功データを表示する
        201作成できた(POSTの成功)完了画面を出す
        400リクエストが不正入力内容を見直してもらう
        401 / 403未認証/権限なしログイン画面へ誘導する
        404見つからない「該当なし」を表示する
        500サーバー内部のエラー時間をおいて再試行を案内する

        POSTで送信する ── フォームの送信

        JSONを送る
        async function sendContact(values) {
          const response = await fetch("https://example.com/api/contact", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",   // 本文の形式を伝える
            },
            body: JSON.stringify(values),           // オブジェクト → JSON文字列
          });
        
          if (!response.ok) {
            // サーバーがエラーの詳細をJSONで返すこともある
            const detail = await response.json().catch(() => null);
            throw new Error(detail?.message ?? `送信に失敗しました(${response.status})`);
          }
          return await response.json();
        }
        
        // STEP 10のフォームと組み合わせる
        form.addEventListener("submit", async (e) => {
          e.preventDefault();
          const button = form.querySelector("button[type=submit]");
          button.disabled = true;                   // ★二重送信を防ぐ
          try {
            await sendContact({ name: "山田", message: "こんにちは" });
            alert("送信しました");
          } catch (error) {
            alert(error.message);
          } finally {
            button.disabled = false;                // 成功でも失敗でも必ず戻す
          }
        });
        二重送信の防止は必須 通信中にボタンを連打されると、同じ問い合わせが何件も登録されます。送信開始でボタンを無効化し、finally で戻す——これは必ず入れてください。実際の事故として最も多いものの1つです。

        4つの状態を画面に出し分ける

        実務の通信処理で本当に大事なのは、fetch の書き方よりも状態の管理です。次の4つを必ず設計してください。

        状態画面に出すもの忘れるとどうなるか
        読み込み中「読み込んでいます…」/読み込み表示画面が固まったように見え、連打される
        成功(データあり)一覧・詳細
        成功(0件)「該当するデータがありません」真っ白な画面になり、不具合と誤解される
        失敗理由と「再試行」ボタン利用者が何もできなくなる
        状態を出し分ける実装の型
        const view = document.querySelector("#view");
        
        async function render() {
          view.textContent = "読み込んでいます…";       // ① 読み込み中
        
          try {
            const products = await getProducts();
        
            if (products.length === 0) {                // ③ 0件
              view.textContent = "該当する商品がありませんでした。";
              return;
            }
        
            view.replaceChildren();                     // ② 成功
            for (const p of products) {
              const li = document.createElement("li");
              li.textContent = `${p.name}(${p.price}円)`;
              view.appendChild(li);
            }
          } catch (error) {                             // ④ 失敗
            console.error(error);
            view.textContent = "読み込みに失敗しました。時間をおいて再度お試しください。";
          }
        }

        CORS ── 別のサイトのAPIを叩くと出る壁

        ブラウザには、別のドメインへの通信を、相手の許可がなければ読ませないという安全上の決まりがあります。これをCORS(コルス/オリジン間リソース共有)といいます。

        よく見るエラー
        Access to fetch at 'https://api.example.com/data' from origin
        'http://localhost:3000' has been blocked by CORS policy:
        No 'Access-Control-Allow-Origin' header is present on the requested resource.

        これはJavaScriptのコードの誤りではありません。「相手のサーバーが、このサイトからの読み取りを許可していない」という意味です。解決はサーバー側でしかできません(サーバーで許可設定を入れる、自社サーバー経由で取得する、など)。ブラウザ側の書き方をいくら変えても直りません。ここを知らずに何時間も溶かす人が非常に多いので、覚えておいてください。

        中断とタイムアウト

        AbortController
        // 5秒で打ち切る
        const controller = new AbortController();
        const timer = setTimeout(() => controller.abort(), 5000);
        
        try {
          const res = await fetch(url, { signal: controller.signal });
          const data = await res.json();
        } catch (error) {
          if (error.name === "AbortError") {
            console.log("時間内に応答がありませんでした");
          } else {
            throw error;
          }
        } finally {
          clearTimeout(timer);
        }
        
        // 検索の入力ごとに、前の通信を取り消す用途でも使う
        動かして確かめる ── 読み込み中・成功・0件・失敗の出し分け
        「データを取得する」を押してください。

        このデモは実際の通信は行わず、疑似的にサーバーの応答を作って動かしています。どの場合でも画面が真っ白にならないことを確かめてください。

        一問一答
        1. fetch は404のとき例外を投げますか。どう対処しますか。
        2. 通信処理で用意すべき4つの状態を挙げてください。
        3. CORSエラーは、どこを直せば解決しますか。
        4. 二重送信を防ぐには、どう書きますか。
        解答を見る
        1. 投げない。if (!response.ok) throw new Error(...) のように自分で判定する。
        2. 読み込み中/成功(データあり)/成功だが0件/失敗。
        3. サーバー側(許可の設定、または自社サーバーを経由させる)。ブラウザ側のコードでは解決できない。
        4. 送信開始でボタンを disabled = true にし、finally で必ず戻す。
        STEP 14 開発環境

        エラー処理とデバッグ ── 自力で原因にたどり着く

        目安 4日
        このステップの到達点 ── エラーメッセージを読んで原因の場所を特定でき、try / catch を適切な位置に書ける。開発者ツールのブレークポイントで処理を止め、変数の中身を確認できる。「調べ方を知っている」ことが、実務では文法知識より価値があります

        エラーメッセージは3つの部分を読む

        エラー表示の例
        Uncaught TypeError: Cannot read properties of null (reading 'value')
            at handleSubmit (main.js:42:31)
            at HTMLFormElement.<anonymous> (main.js:12:9)

        ① 種類TypeError ② 内容:nullの中の value を読もうとした ③ 場所main.js の42行31文字目、handleSubmit の中。まず③の場所を開く——これが鉄則です。エラーは怖いものではなく、場所を教えてくれる案内です。

        エラー意味典型的な原因
        SyntaxError文法の誤りかっこ・クォートの閉じ忘れ。実行前に出る
        ReferenceError: x is not definedその名前が存在しない変数名の綴り違い、宣言し忘れ、スコープ外
        TypeError: Cannot read properties of nullnullの中を読んだquerySelector が取得できていない
        TypeError: Cannot read properties of undefinedundefinedの中を読んだデータがまだ届いていない、キー名の誤り
        TypeError: x is not a function関数でないものを呼んだメソッド名の綴り違い(mapMapなど)
        Unexpected token < in JSONJSONのはずがHTMLだったAPIのURLが誤り、エラーページが返っている
        Maximum call stack size exceeded呼び出しが深すぎる関数が自分自身を無限に呼んでいる

        try / catch ── 止まってほしくない場所を守る

        基本形と、投げる側
        try {
          const data = JSON.parse(text);       // 失敗する可能性がある処理
          console.log(data.name);
        } catch (error) {
          console.error("解析に失敗:", error.message);
          // 利用者には分かる言葉で伝える
        } finally {
          console.log("成功でも失敗でも必ず通る");
        }
        
        // 自分でエラーを投げる
        function withdraw(balance, amount) {
          if (amount <= 0) throw new Error("金額は1円以上で指定してください");
          if (amount > balance) throw new Error("残高が不足しています");
          return balance - amount;
        }
        
        try {
          withdraw(1000, 5000);
        } catch (error) {
          console.error(error.message);        // 残高が不足しています
        }
        握りつぶさない catch (e) { }中身を空にするのは最悪の書き方です。問題が起きても誰も気づけず、原因調査が不可能になります。最低でも console.error(e) を書き、利用者には状況が分かる文言を画面に出すようにしてください。
        どこに書くか try / catch を至るところに書くと、かえって読みにくくなります。原則は「失敗し得る境界」に置くこと——①通信(fetch) ②解析(JSON.parse) ③外部ライブラリの呼び出し ④利用者の操作の入口(イベント処理)。この4か所を押さえれば十分です。

        console を使い分ける

        console の便利な使い方
        console.log("ふつうの出力");
        console.warn("注意");                    // 黄色で出る
        console.error("異常");                   // 赤で出る。呼び出し経路も表示
        
        // 変数名と値をまとめて出す({ } で囲むのがこつ)
        const name = "山田", age = 34;
        console.log({ name, age });              // { name: "山田", age: 34 }
        
        // 配列やオブジェクトの一覧を表で見る(非常に便利)
        console.table([{ id: 1, name: "A" }, { id: 2, name: "B" }]);
        
        // 条件を満たさないときだけ出す
        console.assert(age >= 18, "18歳未満です");
        
        // 処理時間を測る
        console.time("処理");
        // ...重い処理...
        console.timeEnd("処理");                 // 処理: 123.4ms
        
        // まとめて折りたたむ
        console.group("送信内容");
        console.log(name);
        console.groupEnd();

        ブレークポイント ── console.log より速く原因に着く

        開発者ツールのSources(ソース)タブでは、コードの行番号をクリックするとブレークポイントを置けます。そこで処理が止まり、その瞬間の変数の中身がすべて見られます

        1. 開発者ツール → Sources タブ → 左のファイル一覧から対象のJSを開く
        2. 止めたい行の行番号をクリック(青い印が付く)
        3. 画面を操作すると、その行で止まる
        4. 変数にマウスを乗せると値が見える。右の Scope にも一覧が出る
        5. F10=次の行へ/F11=関数の中へ入る/F8=次まで進める
        コードから止める
        function handleSubmit(values) {
          debugger;         // ← 開発者ツールが開いていると、この行で止まる
          console.log(values);
        }
        // ※ 消し忘れて公開しないよう注意(ESLintで検出できる:STEP 15)
        調べ方の型エラーの行を開く ②その手前に console.log を置き、値が期待どおりかを見る ③期待と違えば、さらに上流へさかのぼる ④「どこまでは正しく、どこから違うか」の境目を見つける。この二分探索のやり方は、どんな言語でも通用します。当てずっぽうで直さないことが最大のこつです。

        ネットワークタブ ── 通信の不具合はここを見る

        • Network タブを開いた状態で操作すると、通信が一覧に出る
        • Status 列でコードを確認(200/404/500)
        • 行をクリック → Payload で送った内容、Response で返ってきた内容を確認
        • 「そもそもリクエストが出ていない」ことも多い(イベントが動いていない、URLの組み立てミス)

        「サーバーが悪いのか、こちらが悪いのか」は、Networkタブを見れば1分で切り分けられます。送った内容が意図どおりなら相手側、意図と違うならこちら側です。この切り分けができると、他部署とのやり取りが一気に速くなります。

        動かして確かめる ── エラーを起こして読んでみる

        try / catch で受け止め、エラーの種類(name)と内容(message)を表示しています。ブラウザの開発者ツールを開いた状態で試すと、同じ内容がコンソールにも出ることを確認できます。

        一問一答
        1. エラーメッセージから最初に読み取るべき情報は何ですか。
        2. catch の中を空にしてはいけないのはなぜですか。
        3. Unexpected token < in JSON は、何が起きていますか。
        4. 通信の不具合で、まず開くべき開発者ツールのタブはどれですか。
        解答を見る
        1. 発生した場所(ファイル名と行番号)。次に種類と内容。
        2. 問題が起きたことに誰も気づけず、原因調査ができなくなるから。
        3. JSONを期待した場所にHTML(エラーページなど)が返ってきている。URLや認証の誤りを疑う。
        4. Network(ネットワーク)タブ。ステータスコードと送受信の内容を確認する。
        STEP 15 開発環境

        開発環境 ── Node.js、npm、ビルド、TypeScript

        目安 5日
        このステップの到達点 ── Node.jsとnpmの役割を説明でき、package.json を読める。チームの環境に参加して、開発サーバーを立ち上げられる。TypeScriptが何を解決する道具かを理解する。

        Node.js ── ブラウザの外でJavaScriptを動かす

        Node.jsは、JavaScriptをパソコンやサーバー上で動かす実行環境です。フロントエンドの開発でも、ツールを動かす土台として必ず必要になります。

        インストールの確認(ターミナル/コマンドプロンプト)
        # 入っているか確認(数字が出ればOK)
        node -v
        npm -v
        
        # 入っていなければ nodejs.org から LTS版 を入れる
        # ファイルを実行する
        node main.js

        LTS版(Long Term Support=長期サポート版)を選んでください。最新版は不具合に当たることがあります。複数の版を切り替える必要が出たら、nvmVolta といったバージョン管理ツールを使います(最初は不要です)。

        npm ── 部品を取り寄せる仕組み

        npm(エヌピーエム)は、世界中の開発者が公開しているJavaScriptの部品(パッケージ)を取り寄せ、管理する道具です。

        よく使うコマンド
        # 新しいプロジェクトを始める(package.json ができる)
        npm init -y
        
        # パッケージを入れる
        npm install dayjs                 # 本番でも使うもの
        npm install --save-dev eslint     # 開発時だけ使うもの(-D と省略可)
        
        # 既存プロジェクトに参加したとき(これが最も使う)
        npm install                       # package.json を見て、必要なものを全部入れる
        
        # 実行する
        npm run dev                       # package.json の scripts に書かれた命令
        npm run build
        
        # 入れずに1回だけ実行する
        npx serve                         # 簡易サーバーを立てる(STEP 7のCORS対策)
        package.json の読み方
        {
          "name": "my-app",
          "version": "1.0.0",
          "type": "module",              // import/export を使う指定
          "scripts": {                   // npm run ○○ で実行できる命令の一覧
            "dev": "vite",
            "build": "vite build",
            "lint": "eslint src"
          },
          "dependencies": {              // 本番でも必要
            "dayjs": "^1.11.10"
          },
          "devDependencies": {           // 開発時だけ必要
            "eslint": "^9.0.0",
            "vite": "^5.0.0"
          }
        }
        ファイル/フォルダ役割Gitで管理するか
        package.json必要な部品と実行命令の一覧する
        package-lock.json実際に入れた版を固定する記録する(全員の環境をそろえるため)
        node_modules/取り寄せた部品の実体。数万ファイルになるしない.gitignore に入れる)
        版の記号の意味 ^1.11.10 は「1系のうち、1.11.10以上の最新」を意味します。npm install のたびに細かい版が変わり得るため、package-lock.json を必ずGitに含めます。「自分の環境では動くのに、他の人の環境で動かない」の主因がここです。

        ビルドツール ── なぜ必要なのか

        ブラウザ用のコードを、そのまま公開することは今では少なく、ビルドという変換工程を挟みます。理由は次のとおりです。

        • まとめる(バンドル):数十個のファイルへの通信を1〜数個に減らす
        • 小さくする(最小化):空白や改行、長い変数名を削って軽くする
        • 古い書き方に変換する:古いブラウザでも動く形に直す
        • 開発を速くする:保存した瞬間に画面へ反映する(ホットリロード)
        Vite で始める(現在の定番)
        npm create vite@latest my-app
        cd my-app
        npm install
        npm run dev        # → http://localhost:5173 が立ち上がる
        npm run build      # → dist/ に公開用ファイルができる

        Vite(ヴィート)は現在いちばん使われている開発サーバー兼ビルドツールです。少し前は webpack(ウェブパック)が主流でした。求人票にはどちらも出てきますが、考え方は同じなので、片方を触れば他方も読めます。

        コードをそろえる道具 ── ESLint と Prettier

        道具読み方役割
        ESLintイーエスリント誤りやすい書き方を指摘する(== の使用、未使用の変数、debugger の残存など)
        Prettierプリティア見た目を自動で統一する(字下げ、改行位置、引用符)
        EditorConfigエディタコンフィグエディタ間で字下げ幅などをそろえる
        チーム開発では必須 書き方の好みで議論する時間は、そのまま無駄になります。「道具が決めたとおりにする」と決めてしまうのが正解です。VS Codeで「保存時に自動整形」を有効にしておくと、意識しなくてもそろいます。

        TypeScript ── 「型」で事故を防ぐ

        TypeScriptは、JavaScriptに型の指定を足した言語です。実行する前(書いている最中)に、型の食い違いを指摘してくれます。実務の求人では、すでにほぼ標準と言ってよい状況です。

        TypeScript の雰囲気
        // JavaScript:実行してみるまで誤りに気づけない
        function calcTax(price) {
          return Math.round(price * 1.1);
        }
        calcTax("1980");        // 文字列を渡しても、その場では止まらない
        
        // TypeScript:書いている最中に赤線で指摘される
        // function calcTax(price: number): number {
        //   return Math.round(price * 1.1);
        // }
        // calcTax("1980");   ← エラー:型 'string' を型 'number' に割り当てられません
        
        // データの形も定義できる
        // type Product = { id: number; name: string; price: number; stock?: number };
        //                                                    ↑ ? は「無くてもよい」
        学ぶ順番 JavaScriptが書けるようになってからTypeScriptに進んでください。同時に学ぶと、「どちらの決まりごとなのか」が区別できず混乱します。目安として、このページのSTEP 13まで自力で書けるようになったら、TypeScriptに進むのが適切です。学習には1〜2週間あれば入口は越えられます。

        Gitと組み合わせる

        実務では、コードの変更履歴をGitで管理します。JavaScript開発では、次の内容を .gitignore に入れるのが定番です。

        .gitignore の基本
        node_modules/
        dist/
        .env
        .env.local
        .DS_Store
        鍵を公開しない APIキーやパスワードを、コードに直接書いてGitに上げてしまう事故が後を絶ちません。.env ファイルに分け、必ず .gitignore に入れてください。そして、ブラウザで動くJavaScriptに秘密の鍵を置くことはできません(誰でも中身を見られるため)。秘密を伴う通信は、必ずサーバー側で行います。Gitの基本はGit・GitHub入門で扱っています。
        一問一答
        1. Node.jsとは何ですか。
        2. node_modules/ をGitで管理しないのはなぜですか。
        3. package-lock.json は何のためにありますか。
        4. TypeScriptは何を解決する道具ですか。
        解答を見る
        1. ブラウザの外でJavaScriptを実行する環境。開発ツールを動かす土台にもなる。
        2. ファイル数が膨大で、package.json から再生成できるため。
        3. 実際に入れたパッケージの版を固定し、全員の環境をそろえるため。
        4. 型の食い違いを実行前に検出し、実行時のエラーを減らす。
        STEP 16 実務

        実務のコード品質 ── 安全に、壊れにくく書く

        目安 5日
        このステップの到達点 ── XSSを防ぐ書き方を身につけ、日付・保存・パフォーマンスの定番処理を書ける。レビューで指摘される前に自分で気づける状態になる。ここが「勉強した人」と「仕事ができる人」の分かれ目です。

        セキュリティ ── 画面側で必ず守ること

        やってはいけないこと何が起きるか正しい書き方
        利用者の入力を innerHTML に入れるXSS。任意のスクリプトを実行されるtextContent を使う
        eval() に文字列を渡す任意のコードが実行される使わない。JSON.parse で代替
        APIキーをJSに書く誰でも閲覧でき、悪用されるサーバー側に置く
        個人情報を localStorage に保存他のスクリプトから読める保存しない。必要ならサーバーで管理
        URLの値をそのまま画面に出すXSSの入口になる必ず textContent 経由で出す
        安全に表示する
        const comment = getUserComment();   // 利用者が書いた文字列
        
        // ✕ 危険
        box.innerHTML = `<p>${comment}</p>`;
        
        // ◯ 安全:要素を作り、文字は textContent で入れる
        const p = document.createElement("p");
        p.textContent = comment;
        box.replaceChildren(p);
        
        // リンクを扱うときの注意
        const a = document.createElement("a");
        // javascript: で始まるURLは危険なので、http(s) だけを許可する
        if (url.startsWith("https://") || url.startsWith("http://")) {
          a.href = url;
        } else {
          a.href = "#";
        }
        a.rel = "noopener noreferrer";      // target="_blank" と併用する

        ブラウザへの保存 ── localStorage

        保存・読み出し・削除
        // 文字列しか保存できない → JSONに変換する
        const settings = { theme: "dark", fontSize: 16 };
        localStorage.setItem("app-settings", JSON.stringify(settings));
        
        // 読み出し(無ければ null)
        const saved = localStorage.getItem("app-settings");
        const parsed = saved ? JSON.parse(saved) : null;
        
        // 実務では必ず try/catch で囲む(容量超過・privateモードで失敗する)
        function loadSettings() {
          try {
            const raw = localStorage.getItem("app-settings");
            return raw ? JSON.parse(raw) : { theme: "light", fontSize: 14 };
          } catch (error) {
            console.error("設定の読み込みに失敗", error);
            return { theme: "light", fontSize: 14 };   // 既定値で続行する
          }
        }
        
        localStorage.removeItem("app-settings");   // 1件削除
        // localStorage.clear();                   // 全削除(他機能も消えるので注意)

        ちなみに、このページの「ふりがなON/OFF」と「進み具合」も localStorage で保存しています。ページを閉じても設定が残るのはこの仕組みのおかげです。sessionStorage はタブを閉じると消える版です。

        日付の扱い

        Date の基本と注意点
        const now = new Date();
        console.log(now.getFullYear());        // 2026
        console.log(now.getMonth());           // ★0から始まる(0 = 1月)
        console.log(now.getDate());            // 日
        console.log(now.getDay());             // 曜日(0 = 日曜)
        
        // 日本語で整形する(これが最も手軽で確実)
        console.log(now.toLocaleDateString("ja-JP"));   // 2026/8/14
        console.log(now.toLocaleString("ja-JP", {
          year: "numeric", month: "long", day: "numeric", weekday: "short",
        }));                                            // 2026年8月14日(金)
        
        // 文字列から作るときは、この形式を使う(他の形式は環境差が出る)
        const d = new Date("2026-08-14T09:00:00+09:00");
        
        // 日数を足す
        const tomorrow = new Date(now);
        tomorrow.setDate(now.getDate() + 1);
        
        // 差を求める(ミリ秒で返る)
        const diffDays = Math.floor((tomorrow - now) / (1000 * 60 * 60 * 24));
        月は0から getMonth() は1月が 0、12月が 11 です。「1か月ずれる」不具合の定番原因です。日付の計算が複雑になる場合は、day.js などのライブラリを使うのが実務の判断です(自作すると、うるう年や時差でほぼ確実に間違えます)。

        文字列と数値の実務テクニック

        よく使う処理
        const s = "こども経済新聞";
        
        console.log(s.length);                    // 7
        console.log(s.includes("経済"));           // true
        console.log(s.startsWith("こども"));       // true
        console.log(s.slice(0, 3));               // こども
        console.log(s.replace("新聞", "ニュース")); // こども経済ニュース
        console.log("  余白  ".trim());            // "余白"
        console.log("a,b,c".split(","));          // ["a","b","c"]
        console.log("5".padStart(2, "0"));        // "05"(時刻表示に便利)
        
        // 金額の表示
        console.log((1234567).toLocaleString());  // "1,234,567"
        console.log(new Intl.NumberFormat("ja-JP", {
          style: "currency", currency: "JPY",
        }).format(1980));                          // "¥1,980"
        
        // 乱数(0以上10未満の整数)
        console.log(Math.floor(Math.random() * 10));

        読みやすいコードの原則

        ✕ 指摘されるコード
        function f(d) {
          let r = [];
          for (let i = 0; i < d.length; i++) {
            if (d[i].s == 1) r.push(d[i]);
          }
          return r;
        }

        名前に意味がなく、1 が何を指すかも分かりません。

        ◯ 通るコード
        const STATUS_ACTIVE = 1;

        function filterActiveUsers(users) {
          return users.filter(
            (user) => user.status === STATUS_ACTIVE
          );
        }

        名前が説明になっていて、コメントが要りません。

        • 意味のない数字(マジックナンバー)に名前を付ける1STATUS_ACTIVE
        • コメントは「なぜ」を書く:「何をしているか」はコードを読めば分かる。書くべきは理由と背景
        • 早期リターンで入れ子を浅く(STEP 2)
        • 同じコードが3回出たら関数にする
        • 1つの関数は1画面(20〜30行)に収める

        パフォーマンスの基本

        • DOM操作は回数を減らす:ループの中で毎回 appendChild せず、まとめて追加する(STEP 8)
        • 要素の取得を使い回す:ループの中で querySelector を呼ばない
        • スクロールやリサイズの処理は間引く:デバウンス/スロットル(STEP 9)
        • 画像は遅延読み込み<img loading="lazy">
        • 測ってから直す:開発者ツールの Performance タブと console.time() で確認する。推測で最適化しない
        動かして確かめる ── textContent と innerHTML の違い(XSS)
        どちらかのボタンを押してください。

        textContent はタグを文字として表示し、innerHTMLタグとして解釈します。もし利用者が <img src=x onerror="..."> のような文字列を入力したら、後者ではそのコードが実行されます。このデモでは危険な要素は除去していますが、実際のコードでは利用者の入力を innerHTML に入れないのが原則です。

        一問一答
        1. XSSを防ぐために、表示処理で守るべき原則は何ですか。
        2. localStorage に保存できるのは何型ですか。オブジェクトはどう保存しますか。
        3. new Date().getMonth() が返す値の注意点は何ですか。
        4. APIキーをブラウザのJavaScriptに書いてはいけないのはなぜですか。
        解答を見る
        1. 利用者の入力を innerHTML に入れず、textContent で表示する。
        2. 文字列のみ。JSON.stringify() で文字列にして保存し、JSON.parse() で戻す。
        3. 0から始まる(0が1月)。そのまま表示すると1か月ずれる。
        4. ブラウザに配られたコードは誰でも中身を見られるため、鍵が漏れる。秘密を伴う処理はサーバー側で行う。
        STEP 17 実務

        次の一歩 ── フレームワークと、学び続け方

        目安 3日+継続
        このステップの到達点 ── フレームワークが何を解決する道具かを説明でき、次に学ぶものを自分で選べる。作品(ポートフォリオ)の方向性と、仕事に就くまでの現実的な道すじが描ける。

        なぜフレームワークが要るのか

        ここまでの方法(素のJavaScriptで querySelector して書き換える)は、画面が小さいうちは最良です。しかし、状態が10個、20個と増えると破綻します。「この値が変わったら、どこを書き換えるんだったか」を人間が全部覚えることになるからです。

        考え方の違い
        手続き型と宣言型の違い 素のJavaScriptでは、状態が変わるたびに書き換える場所を1つずつ自分で指示します。ReactやVueでは、状態と画面の対応だけを書けば、画面が自動で追従します。 【素のJavaScript】手続き型 状態が 変わった 見出しを書き換える 一覧を作り直す ボタンを無効にする 書き換える場所を、1つずつ自分で指示する。 画面が増えるほど、指示の書き漏らしが起きる。 【React / Vue】宣言型 状態を 変えるだけ 自動で追従 画面 「この状態のとき、画面はこうなる」とだけ書く。 どこを書き換えるかは、道具側が引き受ける。
        名前読み方特徴求人の傾向
        Reactリアクト世界的に最大勢力。学習資料が圧倒的に多い最も多い。迷ったらこれ
        VueビューHTMLに近い書き方。学習の入口がやさしい日本国内では根強く多い
        Next.jsネクストジェイエスReactを土台にした総合的な枠組み新規案件で増加中
        Svelteスベルト書きやすく軽い。採用はまだ少なめ限定的
        始める時期 このページのSTEP 13まで自力で書けることが目安です。素のJavaScriptを飛ばしてフレームワークから入ると、エラーが出たときに「フレームワークの問題か、JavaScriptの問題か」が切り分けられません。急がば回れです。

        作品を作る ── 何を作るか

        学習の総仕上げは、小さくても完成させた作品です。採用の場で見られるのは、規模ではなく「最後まで作り切ったか」「なぜそう作ったかを説明できるか」です。

        作るもの使う技術難度
        やることリスト(追加・完了・削除・保存)DOM、イベント、localStorage入門
        問い合わせフォーム(入力チェック付き)フォーム、検証、送信(STEP 10・13)入門
        公開APIを使った検索アプリfetch、非同期、状態の出し分け初級
        家計簿(集計・グラフ表示)配列メソッド、reduce、保存中級
        上記をReactで作り直すフレームワーク、状態管理中級

        作品はGitHubで公開し、README(説明書)を丁寧に書くことを勧めます。「何ができるか」「工夫した点」「詰まって解決したこと」を書いておくと、面接での話題がそのまま用意できます。公開の方法はGit・GitHub入門を参照してください。

        学び続けるための情報源

        • MDN Web Docs:メソッドの仕様を調べるならここ。日本語版があり、正確です。検索するときは「MDN ○○」と付けるのが定番です
        • 公式ドキュメント:ライブラリの使い方は、まず公式を見る習慣を付けてください
        • エラー文で検索する:エラーメッセージをそのまま貼って検索すると、たいてい同じ問題に当たった人が見つかります
        • 生成AIの使い方:答えを写すのではなく、「なぜこう書くのか」を聞く使い方をしてください。動くコードだけ受け取ると、直せない資産が増えます

        仕事に就くまでの現実的な道すじ

        時期やること到達点
        1〜2か月目このページのSTEP 0〜13フォームと一覧表示が作れる
        3か月目作品を2つ完成させ、GitHubで公開「作れます」と言える材料ができる
        4〜5か月目TypeScript+React(またはVue)求人の必須条件を満たす
        6か月目〜実務に近い課題(API連携、認証、テスト)面接で技術的な会話ができる
        採用の場で問われること 文法の暗記ではなく、「詰まったとき、どう調べて、どう解決したか」です。エラーメッセージの読み方(STEP 14)、通信の切り分け(STEP 13)、入力チェックの設計(STEP 10)——この3つを自分の言葉で説明できれば、未経験からでも十分に評価されます。

        まだ学んでいない、次に触れるとよいもの

        • テスト(Vitest、Jest):関数が期待どおり動くことを自動で確認する
        • 状態管理(Zustand、Piniaなど):画面をまたいだデータの持ち方
        • アクセシビリティ:キーボード操作、読み上げ対応。実務で求められる場面が増えています
        • Web API各種:Intersection Observer(表示検知)、Web Storage、Canvas、File API
        • サーバー側:Node.js+Express、データベース。1つの言語で両側を書けるのがJavaScriptの強みです
        一問一答
        1. フレームワークは、どんな問題を解決する道具ですか。
        2. フレームワークを学び始める目安は何ですか。
        3. 作品づくりで、規模より大事なことは何ですか。
        解答を見る
        1. 状態が増えたときの「画面の書き換え漏れ」。状態を書けば画面が自動で追従する形にしてくれる。
        2. 素のJavaScriptで、フォーム処理とAPI通信(このページのSTEP 13まで)が自力で書けること。
        3. 最後まで完成させることと、なぜそう作ったかを説明できること。

        早見表

        迷ったときに戻ってくる場所

        配列メソッド

        やりたいこと書き方返るもの
        1件ずつ処理するarr.forEach((v) => { ... })なし
        形を変えるarr.map((v) => v.name)新しい配列
        絞り込むarr.filter((v) => v.price > 100)新しい配列
        1件探すarr.find((v) => v.id === 3)要素/undefined
        合計するarr.reduce((a, b) => a + b, 0)まとめた値
        並べ替える[...arr].sort((a, b) => a - b)並べ替えた配列
        含まれるかarr.includes("A")真偽値
        1つでも該当arr.some((v) => v.stock === 0)真偽値
        全部該当arr.every((v) => v.price > 0)真偽値
        重複を除く[...new Set(arr)]新しい配列
        連結するarr.join("、")文字列

        DOM操作

        やりたいこと書き方
        要素を1つ取得document.querySelector("#id")
        要素を全部取得document.querySelectorAll(".item")
        文字を入れるel.textContent = "文字"
        入力値を取るinput.value check.checked
        クラスを付け外しel.classList.toggle("is-open")
        要素を作るdocument.createElement("li")
        追加・削除parent.appendChild(el) el.remove()
        中身を全消しel.replaceChildren()
        祖先をたどるel.closest(".card")
        イベント登録el.addEventListener("click", fn)
        既定動作を止めるe.preventDefault()

        非同期・通信

        やりたいこと書き方
        取得するconst res = await fetch(url);
        失敗を検出するif (!res.ok) throw new Error(res.status);
        JSONにするconst data = await res.json();
        送信するfetch(url, { method:"POST", headers:{...}, body: JSON.stringify(v) })
        同時に走らせるawait Promise.all([a(), b()])
        指定時間待つawait new Promise((r) => setTimeout(r, 500))
        エラーを受けるtry { ... } catch (e) { ... } finally { ... }

        つまずいたときの確認順

        症状まず確認すること
        何も起きないコンソールにエラーは出ていないか。script の読み込み位置(defer
        Cannot read properties of nullid・classの綴り、#.の有無、HTMLより先に動いていないか
        値が undefinedawait の付け忘れ、キー名の綴り、データが届く前に読んでいないか
        ボタンを押すとページが再読み込みe.preventDefault() の書き忘れ
        計算がおかしい(文字が連結される)入力値を Number() で変換したか
        あとから追加した要素が反応しないイベント委譲になっているか(親に登録)
        CORSエラーサーバー側の許可設定。ブラウザ側では直せない
        一覧が二重に表示される描き直す前に中身を空にしているか

        用語集

        会話で出てきたときに困らないように
        変数 variable
        値に名前を付けて保持するもの。const(入れ替え不可)と let(可)を使い分ける。
        スコープ scope
        変数が使える範囲。{ } の内側で宣言した変数は、その内側だけで有効。
        関数 function
        処理に名前を付けたもの。引数を受け取り、戻り値を返す。
        コールバック関数 callback
        あとで呼んでもらうために、引数として渡す関数。イベントや配列メソッドの土台。
        アロー関数 arrow function
        () => {} の形。短く書け、this が外側のものを引き継ぐ。
        クロージャ closure
        関数が、作られたときの外側の変数を覚えている性質。状態を閉じ込めるのに使う。
        オブジェクト object
        名前(キー)と値の組を集めた入れ物。1件のデータを表す。
        配列 array
        順番付きの値の並び。番号は0から始まる。
        プロパティ/メソッド property / method
        オブジェクトが持つ値がプロパティ、持つ関数がメソッド。
        参照 reference
        オブジェクトや配列の代入は、実体ではなく「置き場所の案内」が複製される。
        分割代入 destructuring
        const { name } = user のように、必要な値だけ取り出す書き方。
        スプレッド構文 spread
        ... で中身を展開する。複製や結合に使う。
        テンプレートリテラル template literal
        バッククォートで囲み、${ } で値を埋め込める文字列。
        truthy/falsy
        条件式で真/偽として扱われる値。0 や空文字は偽、[]{} は真。
        DOM Document Object Model
        HTMLをJavaScriptから操作できる形にした木構造。
        イベント event
        クリックや入力など、利用者や環境が起こす出来事。
        イベント委譲 event delegation
        親に1つ登録して、子の操作をまとめて受け取る手法。あとから増えた要素にも効く。
        バブリング bubbling
        イベントが子から親へ伝わっていく性質。
        非同期処理 asynchronous
        時間のかかる処理を待たずに次へ進み、終わったら結果を受け取る方式。
        イベントループ event loop
        非同期処理の実行順を管理する仕組み。待機中の処理は、いまの処理が終わってから実行される。
        Promise プロミス
        「あとで結果を渡す約束」を表すオブジェクト。pending/fulfilled/rejectedの3状態。
        async/await エイシンク/アウェイト
        非同期処理を、上から下へ読める形で書くための構文。
        fetch フェッチ
        サーバーと通信するための標準機能。404でも例外にならない点に注意。
        API Application Programming Interface
        プログラムから呼び出せる窓口。Web開発ではJSONを返すURLを指すことが多い。
        JSON ジェイソン
        データをやり取りするための文字列形式。キーは必ずダブルクォートで囲む。
        CORS コルス
        別ドメインへの通信を制限するブラウザの安全機能。解決はサーバー側で行う。
        XSS クロスサイトスクリプティング
        入力欄などから悪意のあるスクリプトを実行させる攻撃。textContent で防ぐ。
        モジュール module
        ファイル単位に分けたコード。import / export で受け渡す。
        npm エヌピーエム
        公開されている部品を取り寄せ、管理する仕組み。package.json で管理する。
        バンドル bundle
        複数のファイルを1つにまとめること。ViteやWebpackが行う。
        TypeScript タイプスクリプト
        JavaScriptに型を足した言語。実行前に型の食い違いを検出できる。
        localStorage ローカルストレージ
        ブラウザに文字列を保存する仕組み。閉じても残る。機密情報は入れない。
        デバウンス debounce
        入力が止まってから1回だけ処理する手法。検索欄の無駄な通信を防ぐ。
        NaN ナン
        Not a Number。数値に変換できなかったことを表す値。Number.isNaN() で判定する。

        よくある質問

        学び始めるときにつまずきやすい点
        HTMLとCSSを知らなくても、JavaScriptから始められますか?

        STEP 7までは進められますが、STEP 8(DOM操作)で必ず止まります。JavaScriptがやることの多くは「HTMLの要素を探して書き換える」ことなので、探す対象を知らなければ手が出ません。

        必要なのは2〜3日分です。タグを20個ほど(divpulliforminputbutton など)と、CSSの指定を20個ほど(色、余白、文字サイズ、displayflex)を触っておけば十分です。完璧を目指す必要はありません。

        プログラミングは数学ができないと無理ですか?

        Web開発の実務で使う数学は、四則演算と割合の計算がほとんどです。税込価格の計算、件数の集計、並べ替え——このページのコード例を見ていただければ分かるとおり、高度な数式は出てきません。

        必要なのは数学力より、手順を分解して順番に並べる力です。「まず入力を受け取り、次に空欄かを確かめ、問題なければ送る」——このように日本語で段取りを書ける人は、必ず書けるようになります。

        写経(コードを書き写すこと)に意味はありますか?

        あります。ただし、①打つ ②動かす ③一部を変えて壊す ④なぜ壊れたか説明する——この4段階までやって初めて効果が出ます。打って動いた時点で満足すると、記憶にほとんど残りません。

        特に効くのが③の「壊す」です。===== に変えたらどうなるか、await を消したらどうなるか。意図的に壊した経験が、実務でエラーに出会ったときの引き出しになります

        生成AIにコードを書いてもらうのは、学習になりますか?

        使い方次第です。答えを写すだけなら、学習にはなりません。動くけれど直せないコードが手元に残り、少し要件が変わった途端に手が止まります。

        効果的なのは、①まず自分で書く ②それをAIに見せて「もっと良い書き方は?」と聞く ③なぜそうなのかを説明してもらう ④納得できなければ公式ドキュメント(MDN)で裏を取るという順です。特にエラーメッセージの意味を説明してもらう使い方は、独学の弱点を補ってくれます。

        jQueryは今から学ぶ必要がありますか?

        新しく学ぶ必要はありません。jQueryが解決していた問題(ブラウザごとの書き方の違い、要素の取得の煩雑さ)は、今の標準機能(querySelectorfetchclassList)でほぼ解消しています。

        ただし、既存のサイトの保守では今も現役です。$("#id").on("click", ...) のような書き方を見て、「querySelectoraddEventListener のことだ」と読み替えられれば十分です。読めればよい、書けなくてよいという位置づけです。

        実務でいちばん多い不具合は何ですか?

        体感として多い順に、①値が想定と違う型で来ていた(文字列と数値、nullundefined)、②非同期の待ち忘れawait の付け忘れ)、③要素が取得できていない(綴りミス、読み込み順)、④0件・エラー時の表示を作っていなかった、⑤二重送信・連打の対策漏れです。

        いずれも、このページのSTEP 1・8・12・13で扱った内容です。高度な知識の不足ではなく、基本の詰めが原因であることがほとんどです。だからこそ、基礎を丁寧にやった人ほど、実務で強くなります。

        用語が覚えられません。どうすればよいですか?

        略語のまま覚えようとしないでください。「誰が、何をする役目か」を日本語の一文にするのがこつです。「DOM=HTMLをJavaScriptから触れる形にしたもの」「Promise=あとで結果を渡す約束」というように。

        そのうえで、読み方を必ず声に出して確認してください。読み方があいまいだと、会話でも検索でも使えません。このページのふりがな機能は、まさにそのために付けています。最初のうちはONのまま読み進めてください。

        これから学ぶ方・指導される方へ

        このページは、JavaScriptを初めて学ぶ方が、文法の網羅からではなく「入力→加工→表示→通信」という一本の流れから理解できるように構成しています。文法の暗記から入ると、「知っているのに作れない」状態になりやすいためです。まずは通読し、二周目でコードを打ち、三周目で一問一答を使って確認する使い方をおすすめします。

        指導される立場の方へ。学習者が詰まる場所は、経験上ほぼ決まっています。①関数の引数と戻り値(STEP 4)、②配列メソッドのコールバック(STEP 6)、③非同期処理の実行順(STEP 11〜12)の3か所です。ここに時間を配分し、それ以外は先に進みながら繰り返し戻る形にすると、途中で止まりにくくなります。特に③は、図と実際の実行順の記録を見せると理解が早まります。

        掲載しているコードは、ご自身の環境やブラウザの開発者ツールで実行してください。他人が管理するサイトやサービスに対して、無断で自動化・大量アクセスを行わないでください。技術的には簡単でも、利用規約違反や法令上の問題になり得ます。学習は、自分で用意した環境か、学習用に公開されているサービスの範囲で行ってください。