Lecture 1JavaScriptとは — Webを動かす唯一の言語

8:00

JavaScriptとは — Webを動かす唯一の言語

Web の3つの言語の役割分担

前提コース「HTML/CSS入門」では、Web の構造(HTML)と見た目(CSS)を学びました。しかし、「ボタンをクリックしたらメニューが開く」「入力内容をリアルタイムにチェックする」「サーバーからデータを取得して表示する」— これらの動きは HTML と CSS だけでは実現できません。

言語 役割
HTML 構造 見出し、段落、画像、フォーム
CSS 見た目 色、配置、アニメーション
JavaScript 動き クリック反応、データ取得、計算

JavaScript は ブラウザで動作する唯一のプログラミング言語 です。

JavaScript の歴史

出来事
1995 Brendan Eich が Netscape 社で10日間で開発(当初の名前は Mocha)
1997 ECMAScript として標準化(ECMA-262)
2009 Node.js 登場(サーバーサイドでも JavaScript が使えるように)
2015 ES6(ES2015) — 現代 JavaScript の基礎(let/const, アロー関数, クラス, Promise)
毎年 ECMAScript は年次リリースで進化中(ES2024 が最新)

ES6 以降が「モダン JavaScript」 です。本講座では ES6+ の書き方を使います。

JavaScript でできること

ブラウザ側(フロントエンド)

  • DOM操作: HTML の要素を追加・変更・削除
  • イベント処理: クリック、キー入力、スクロールに反応
  • API通信: サーバーからデータを取得して表示
  • アニメーション: 要素を動かす、フェードイン/アウト
  • フォームバリデーション: 入力内容のリアルタイムチェック

サーバー側(バックエンド — Node.js)

  • Web サーバーの構築(Express, Fastify)
  • データベース操作
  • ファイル処理
  • API の作成

本講座では フロントエンド(ブラウザ側) に集中します。

開発環境

HTML/CSS入門で使った VS Code + Chrome がそのまま使えます。追加のインストールは不要です。

Console を使ってみる

  1. Chrome を開く
  2. F12Console タブ
  3. 以下を入力して Enter:
console.log("Hello, JavaScript!");

画面に Hello, JavaScript! と表示されれば成功です。

Console でできること

// 計算
2 + 3          // → 5
10 * 20        // → 200
"Hello" + " " + "World"  // → "Hello World"

// 日付
new Date()     // → 現在の日時

// アラート(ポップアップ)
alert("こんにちは!")

Console はプログラミングの実験場です。迷ったらまず Console で試してみてください。

HTML ファイルに JavaScript を書く

script タグ(インライン)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>JavaScript入門</title>
</head>
<body>
  <h1 id="title">こんにちは</h1>
  <button id="btn">クリック</button>

  <script>
    document.getElementById("btn").addEventListener("click", function() {
      document.getElementById("title").textContent = "ボタンが押されました!";
    });
  </script>
</body>
</html>

外部ファイル(推奨)

<!-- index.html -->
<body>
  <h1 id="title">こんにちは</h1>
  <button id="btn">クリック</button>
  <script src="js/main.js"></script>
</body>
// js/main.js
document.getElementById("btn").addEventListener("click", function() {
  document.getElementById("title").textContent = "ボタンが押されました!";
});

script タグの配置

</body> の直前に配置する のが基本です。理由:

  1. HTML の解析が完了してから JavaScript が実行される
  2. DOM要素が確実に存在する状態でアクセスできる
  3. ページ表示がブロックされない

または defer 属性を使う方法もあります:

<head>
  <script src="js/main.js" defer></script>
</head>

defer は HTML の解析完了後に実行する指定です。現代の開発ではこちらも一般的です。

コメント

// 1行コメント

/*
  複数行
  コメント
*/

// コメントはコードの意図を説明する(「何をしているか」ではなく「なぜそうしているか」)

セミコロン

JavaScript ではセミコロン(;)は省略可能ですが、付ける習慣を推奨 します。ASI(Automatic Semicolon Insertion)に頼ると、まれに意図しない動作になることがあります。

// 推奨
const name = "田中";
console.log(name);

// 省略しても動く(が非推奨)
const name = "田中"
console.log(name)

実践ワーク

  1. my-js-app フォルダを作成、index.htmljs/main.js を作成
  2. Chrome Console で console.log(), alert(), 計算を試す
  3. ボタンをクリックするとテキストが変わる仕組みを実装
  4. console.log("デバッグメッセージ") で Console にメッセージを出す

まとめと次回の準備

今回のポイント: - JavaScript は Web に動きを付ける唯一のブラウザ言語 - ES6(2015年)以降が「モダン JavaScript」 - Chrome Console で手軽にコードを試せる - <script></body> 直前に配置(または defer

次回: 変数、データ型、演算子を学びます。プログラミングの基礎中の基礎で、すべてのコードの出発点です。

参考文献: - MDN Web Docs「JavaScript 第一歩」(https://developer.mozilla.org/ja/docs/Learn/JavaScript/First_steps) - ECMA International「ECMAScript 仕様」(https://tc39.es/ecma262/) - Douglas Crockford『JavaScript: The Good Parts』(O'Reilly, 2008)

Lecture 2変数・データ型・演算子 — データを扱う基本

12:00

変数・データ型・演算子 — データを扱う基本

変数とは

変数は データに名前を付けて保存する箱 です。

let(再代入可能)

let score = 0;
console.log(score);  // 0

score = 100;         // 再代入OK
console.log(score);  // 100

const(再代入不可 — 推奨)

const TAX_RATE = 0.1;
console.log(TAX_RATE);  // 0.1

TAX_RATE = 0.08;  // エラー: Assignment to constant variable.

var(古い書き方 — 使わない)

var name = "田中";  // ES6以前の書き方。使わないでください

var はスコープの挙動が直感に反するため、現代の JavaScript では使いません。

使い分けルール

const をデフォルトで使い、再代入が必要な場合のみ let を使う。 var は使わない。

const userName = "田中太郎";     // 変更しない値 → const
const MAX_RETRIES = 3;          // 定数 → const
let count = 0;                   // ループカウンタなど → let
let isLoggedIn = false;          // 状態が変わる → let

命名規則

// camelCase(推奨)
const userName = "田中";
const totalPrice = 1000;
const isActive = true;

// NG: 予約語
// const let = 5;      // エラー
// const class = "A";  // エラー

// NG: 数字始まり
// const 1stPlace = "田中";  // エラー

データ型

JavaScript には 7つのプリミティブ型オブジェクト型 があります。

プリミティブ型

// 1. 文字列(string)
const name = "田中太郎";
const greeting = 'こんにちは';
const message = `${name}さん、${greeting}`;  // テンプレートリテラル

// 2. 数値(number)
const age = 30;
const price = 1980.5;
const negative = -10;

// 3. 真偽値(boolean)
const isActive = true;
const isDeleted = false;

// 4. undefined(未定義)
let data;
console.log(data);  // undefined

// 5. null(意図的に「空」)
const result = null;

// 6. BigInt(巨大整数 — ES2020)
const bigNumber = 9007199254740992n;

// 7. Symbol(一意の識別子 — ES6)
const id = Symbol("id");

typeof 演算子

console.log(typeof "hello");     // "string"
console.log(typeof 42);          // "number"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof null);        // "object" ← JavaScript の歴史的バグ
console.log(typeof {});          // "object"
console.log(typeof []);          // "object" ← 配列もobject

テンプレートリテラル(バッククォート)

const name = "田中";
const age = 30;

// 従来の文字列結合
const msg1 = name + "さんは" + age + "歳です。";

// テンプレートリテラル(推奨)
const msg2 = `${name}さんは${age}歳です。`;

// 複数行もOK
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>${age}歳</p>
  </div>
`;

型変換

JavaScript は動的型付け言語で、型が自動変換されることがあります。

暗黙の型変換(危険)

console.log("5" + 3);     // "53"(文字列結合)
console.log("5" - 3);     // 2(数値として計算)
console.log("5" * "3");   // 15
console.log(true + 1);    // 2(true → 1)
console.log(false + 1);   // 1(false → 0)

明示的な型変換(安全)

// 文字列 → 数値
const num1 = Number("42");       // 42
const num2 = parseInt("42px");   // 42(先頭の数値部分だけ)
const num3 = parseFloat("3.14"); // 3.14

// 数値 → 文字列
const str1 = String(42);         // "42"
const str2 = (42).toString();    // "42"

// 真偽値
const bool1 = Boolean(0);        // false
const bool2 = Boolean("");       // false
const bool3 = Boolean("hello");  // true
const bool4 = Boolean(1);        // true

Falsy 値(false と判定される値)

// 以下はすべて false になる
Boolean(false)      // false
Boolean(0)          // false
Boolean("")         // false
Boolean(null)       // false
Boolean(undefined)  // false
Boolean(NaN)        // false

// それ以外はすべて true(Truthy)
Boolean("0")        // true(文字列の"0"はTruthy)
Boolean([])         // true(空配列はTruthy)
Boolean({})         // true(空オブジェクトはTruthy)

演算子

算術演算子

console.log(10 + 3);   // 13(加算)
console.log(10 - 3);   // 7(減算)
console.log(10 * 3);   // 30(乗算)
console.log(10 / 3);   // 3.333...(除算)
console.log(10 % 3);   // 1(剰余)
console.log(2 ** 10);  // 1024(べき乗 — ES2016)

比較演算子

// === 厳密等価(推奨)— 型も値も同じ
console.log(5 === 5);       // true
console.log(5 === "5");     // false

// == 等価(非推奨)— 型変換して比較
console.log(5 == "5");      // true(危険!)
console.log(0 == false);    // true(危険!)

// !== 厳密不等価(推奨)
console.log(5 !== "5");     // true

// 大小比較
console.log(10 > 5);    // true
console.log(10 >= 10);  // true
console.log(10 < 5);    // false
console.log(10 <= 10);  // true

常に ===!== を使ってください。 ==!= は暗黙の型変換が入り、バグの温床です。

論理演算子

// AND(両方 true のとき true)
console.log(true && true);    // true
console.log(true && false);   // false

// OR(どちらか true のとき true)
console.log(true || false);   // true
console.log(false || false);  // false

// NOT(反転)
console.log(!true);   // false
console.log(!false);  // true

// 実用例
const age = 25;
const hasLicense = true;

if (age >= 18 && hasLicense) {
  console.log("運転できます");
}

Null合体演算子(?? — ES2020)

const input = null;
const value = input ?? "デフォルト値";
console.log(value);  // "デフォルト値"

// || との違い
const count = 0;
console.log(count || 10);   // 10(0 は falsy なので)
console.log(count ?? 10);   // 0(null/undefined のみ置換)

代入演算子

let x = 10;
x += 5;   // x = x + 5 → 15
x -= 3;   // x = x - 3 → 12
x *= 2;   // x = x * 2 → 24
x /= 4;   // x = x / 4 → 6
x++;      // x = x + 1 → 7
x--;      // x = x - 1 → 6

実践ワーク

Console で以下を試してください:

// 1. 変数と計算
const price = 1980;
const quantity = 3;
const taxRate = 0.1;
const total = price * quantity * (1 + taxRate);
console.log(`合計: ${total}円`);

// 2. 型変換の確認
console.log(typeof "42");
console.log(typeof Number("42"));
console.log("5" + 3);
console.log("5" - 3);

// 3. 比較演算子
console.log(5 === "5");
console.log(5 == "5");

// 4. BMI計算
const height = 1.70;  // メートル
const weight = 65;     // kg
const bmi = weight / (height ** 2);
console.log(`BMI: ${bmi.toFixed(1)}`);

まとめと次回の準備

今回のポイント: - const がデフォルト、再代入が必要なら letvar は使わない - テンプレートリテラル `${変数}` で文字列を組み立てる - === で厳密比較。== は使わない - Falsy 値(0, "", null, undefined, NaN, false)を理解する

次回: 条件分岐(if / switch)とループ(for / while)。プログラムの流れを制御する基本構造を学びます。

参考文献: - MDN Web Docs「JavaScript データ型とデータ構造」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Data_structures) - MDN Web Docs「式と演算子」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Expressions_and_operators)

Lecture 3条件分岐とループ — プログラムの流れを制御する

12:00

条件分岐とループ — プログラムの流れを制御する

条件分岐

if 文

const score = 85;

if (score >= 90) {
  console.log("優");
} else if (score >= 70) {
  console.log("良");
} else if (score >= 60) {
  console.log("可");
} else {
  console.log("不可");
}
// → "良"

三項演算子(1行で書ける if)

const age = 20;
const message = age >= 18 ? "成人です" : "未成年です";
console.log(message);  // "成人です"

// 実用例: CSSクラスの切り替え
const isActive = true;
const className = isActive ? "btn-active" : "btn-inactive";

三項演算子はシンプルな2択に使います。複雑な条件には if 文を使ってください。

switch 文

const day = new Date().getDay();  // 0(日)〜 6(土)

switch (day) {
  case 0:
    console.log("日曜日");
    break;
  case 1:
    console.log("月曜日");
    break;
  case 2:
    console.log("火曜日");
    break;
  case 6:
    console.log("土曜日");
    break;
  default:
    console.log("平日です");
}

break を忘れるとフォールスルー(次の case も実行される)になるので注意してください。

論理演算子を使った条件

const user = { name: "田中", age: 30, isPremium: true };

// AND: 両方の条件を満たす
if (user.age >= 18 && user.isPremium) {
  console.log("プレミアムコンテンツにアクセス可能");
}

// OR: どちらかの条件を満たす
if (user.isPremium || user.age >= 65) {
  console.log("割引適用");
}

// NOT: 条件の否定
if (!user.isPremium) {
  console.log("アップグレードをおすすめします");
}

オプショナルチェイニング(?. — ES2020)

const user = { name: "田中", address: null };

// 従来: エラー回避のために if が必要
if (user.address && user.address.city) {
  console.log(user.address.city);
}

// オプショナルチェイニング: 安全にアクセス
console.log(user.address?.city);  // undefined(エラーにならない)

ループ(繰り返し)

for 文

for (let i = 0; i < 5; i++) {
  console.log(`${i + 1}回目`);
}
// 1回目, 2回目, 3回目, 4回目, 5回目

構造: for (初期化; 条件; 更新)

while 文

let count = 0;
while (count < 5) {
  console.log(`${count + 1}回目`);
  count++;
}

注意: 条件が永遠に true になるとブラウザがフリーズします(無限ループ)。必ず終了条件を設定してください。

for...of(配列の反復 — 推奨)

const fruits = ["りんご", "バナナ", "オレンジ"];

for (const fruit of fruits) {
  console.log(fruit);
}
// りんご, バナナ, オレンジ

for...in(オブジェクトのキー反復)

const user = { name: "田中", age: 30, city: "東京" };

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}
// name: 田中, age: 30, city: 東京

配列のメソッドによるループ(最も使用頻度が高い)

const numbers = [1, 2, 3, 4, 5];

// forEach: 各要素に処理を実行
numbers.forEach(function(num) {
  console.log(num * 2);
});
// 2, 4, 6, 8, 10

// forEach(アロー関数版)
numbers.forEach((num) => {
  console.log(num * 2);
});

アロー関数は次回「関数」の講義で詳しく学びます。

break と continue

// break: ループを途中で抜ける
for (let i = 0; i < 10; i++) {
  if (i === 5) break;
  console.log(i);
}
// 0, 1, 2, 3, 4

// continue: 現在の回をスキップして次へ
for (let i = 0; i < 10; i++) {
  if (i % 2 === 0) continue;  // 偶数をスキップ
  console.log(i);
}
// 1, 3, 5, 7, 9

実践的な例

FizzBuzz

プログラミングの定番問題です。

for (let i = 1; i <= 30; i++) {
  if (i % 15 === 0) {
    console.log("FizzBuzz");
  } else if (i % 3 === 0) {
    console.log("Fizz");
  } else if (i % 5 === 0) {
    console.log("Buzz");
  } else {
    console.log(i);
  }
}

合計・平均の計算

const scores = [80, 95, 70, 88, 92];

let total = 0;
for (const score of scores) {
  total += score;
}

const average = total / scores.length;
console.log(`合計: ${total}`);      // 425
console.log(`平均: ${average}`);    // 85

入力バリデーション

const email = "user@example.com";

if (email.length === 0) {
  console.log("メールアドレスを入力してください");
} else if (!email.includes("@")) {
  console.log("正しいメールアドレスを入力してください");
} else {
  console.log("OK");
}

九九の表

for (let i = 1; i <= 9; i++) {
  let row = "";
  for (let j = 1; j <= 9; j++) {
    row += String(i * j).padStart(4);
  }
  console.log(row);
}

実践ワーク

Console で以下を作成してください:

  1. 成績判定: 点数(0-100)を入力し、90以上=A、80以上=B、70以上=C、60以上=D、それ以下=F を表示
  2. FizzBuzz: 1〜50 まで実行
  3. 最大値の検索: 配列 [34, 72, 13, 55, 89, 21] の中から最大値を for 文で見つける
  4. 文字列の逆転: "JavaScript" を1文字ずつループで逆順にする

まとめと次回の準備

今回のポイント: - if / else if / else で条件分岐。三項演算子は単純な2択に - for ループと while ループ。無限ループに注意 - for...of は配列、for...in はオブジェクトに使う - ?.(オプショナルチェイニング)で安全にプロパティアクセス

次回: 関数を学びます。コードを部品化して再利用するための最も重要な概念です。

参考文献: - MDN Web Docs「制御フローとエラー処理」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Control_flow_and_error_handling) - MDN Web Docs「ループと反復処理」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Loops_and_iteration)

Lecture 4関数 — コードを部品化して再利用する

12:00

関数 — コードを部品化して再利用する

関数とは

関数は 入力を受け取り、処理して、結果を返す部品 です。同じ処理を何度も書かずに済みます。

関数の3つの書き方

1. function 宣言

function greet(name) {
  return `こんにちは、${name}さん!`;
}

console.log(greet("田中"));  // "こんにちは、田中さん!"

2. 関数式

const greet = function(name) {
  return `こんにちは、${name}さん!`;
};

console.log(greet("田中"));

3. アロー関数(ES6 — 推奨)

const greet = (name) => {
  return `こんにちは、${name}さん!`;
};

// 1行の場合は {} と return を省略できる
const greet = (name) => `こんにちは、${name}さん!`;

// 引数が1つの場合は () も省略できる
const greet = name => `こんにちは、${name}さん!`;

console.log(greet("田中"));

アロー関数を基本的に使ってください。 短く書けて、this の挙動も直感的です。

引数と戻り値

複数の引数

const add = (a, b) => a + b;
console.log(add(3, 5));  // 8

const introduce = (name, age, city) => {
  return `${name}(${age}歳、${city}在住)`;
};
console.log(introduce("田中", 30, "東京"));

デフォルト引数(ES6)

const greet = (name, greeting = "こんにちは") => {
  return `${greeting}、${name}さん!`;
};

console.log(greet("田中"));             // "こんにちは、田中さん!"
console.log(greet("田中", "おはよう"));  // "おはよう、田中さん!"

戻り値がない関数

const logMessage = (message) => {
  console.log(`[LOG] ${message}`);
  // return がなければ undefined を返す
};

const result = logMessage("テスト");
console.log(result);  // undefined

早期リターン

const divide = (a, b) => {
  if (b === 0) {
    return "エラー: 0で割れません";  // 早期リターン
  }
  return a / b;
};

console.log(divide(10, 3));  // 3.333...
console.log(divide(10, 0));  // "エラー: 0で割れません"

スコープ

変数が見える範囲(スコープ)を理解することは重要です。

ブロックスコープ(let / const)

const x = "グローバル";

if (true) {
  const y = "ブロック内";
  console.log(x);  // "グローバル"(外側は見える)
  console.log(y);  // "ブロック内"
}

console.log(x);  // "グローバル"
// console.log(y);  // エラー: y is not defined

関数スコープ

const outer = () => {
  const message = "外側の関数";

  const inner = () => {
    console.log(message);  // "外側の関数"(外側は見える)
  };

  inner();
};

outer();
// console.log(message);  // エラー: message is not defined

コールバック関数

関数を別の関数の引数として渡す — これが コールバック です。JavaScript の最重要概念の一つです。

// 高階関数: 関数を引数に取る関数
const doTask = (task, callback) => {
  console.log(`${task}を実行中...`);
  callback();
};

doTask("データ取得", () => {
  console.log("完了しました!");
});
// "データ取得を実行中..."
// "完了しました!"

配列メソッドとコールバック

const numbers = [1, 2, 3, 4, 5];

// map: 各要素を変換して新しい配列を返す
const doubled = numbers.map(n => n * 2);
console.log(doubled);  // [2, 4, 6, 8, 10]

// filter: 条件に合う要素だけの配列を返す
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens);  // [2, 4]

// find: 条件に合う最初の要素を返す
const found = numbers.find(n => n > 3);
console.log(found);  // 4

// reduce: 全要素を1つの値にまとめる
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum);  // 15

これらのメソッドは for ループより 宣言的 で読みやすいコードが書けます。

メソッドチェイン

const users = [
  { name: "田中", age: 30, active: true },
  { name: "鈴木", age: 25, active: false },
  { name: "佐藤", age: 35, active: true },
  { name: "山田", age: 28, active: true },
];

// アクティブなユーザーの名前を年齢順で取得
const result = users
  .filter(user => user.active)
  .sort((a, b) => a.age - b.age)
  .map(user => user.name);

console.log(result);  // ["山田", "田中", "佐藤"]

分割代入(Destructuring — ES6)

オブジェクトの分割代入

const user = { name: "田中", age: 30, city: "東京" };

// 従来
const name = user.name;
const age = user.age;

// 分割代入
const { name, age, city } = user;
console.log(name);  // "田中"
console.log(age);   // 30

// 関数の引数で使う(頻出パターン)
const greetUser = ({ name, age }) => {
  return `${name}さん(${age}歳)`;
};
console.log(greetUser(user));

配列の分割代入

const colors = ["赤", "青", "緑"];
const [first, second, third] = colors;
console.log(first);   // "赤"
console.log(second);  // "青"

// 不要な要素をスキップ
const [, , last] = colors;
console.log(last);  // "緑"

スプレッド構文(... — ES6)

// 配列のコピー・結合
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
console.log(combined);  // [1, 2, 3, 4, 5, 6]

// オブジェクトのコピー・マージ
const defaults = { theme: "light", lang: "ja", fontSize: 16 };
const userSettings = { theme: "dark", fontSize: 18 };
const settings = { ...defaults, ...userSettings };
console.log(settings);  // { theme: "dark", lang: "ja", fontSize: 18 }

// 残余引数
const sum = (...nums) => nums.reduce((acc, n) => acc + n, 0);
console.log(sum(1, 2, 3, 4, 5));  // 15

実践ワーク

// 1. 税込価格計算関数
const calcTax = (price, taxRate = 0.1) => {
  return Math.floor(price * (1 + taxRate));
};
console.log(calcTax(1000));     // 1100
console.log(calcTax(1000, 0.08)); // 1080

// 2. 配列の統計関数
const stats = (numbers) => {
  const sum = numbers.reduce((acc, n) => acc + n, 0);
  const avg = sum / numbers.length;
  const max = Math.max(...numbers);
  const min = Math.min(...numbers);
  return { sum, avg, max, min };
};
console.log(stats([80, 95, 70, 88, 92]));

// 3. ユーザーフィルタリング
const users = [
  { name: "田中", age: 30, city: "東京" },
  { name: "鈴木", age: 25, city: "大阪" },
  { name: "佐藤", age: 35, city: "東京" },
];
const tokyoUsers = users
  .filter(u => u.city === "東京")
  .map(u => u.name);
console.log(tokyoUsers);  // ["田中", "佐藤"]

まとめと次回の準備

今回のポイント: - アロー関数 () => {} を基本的に使う - デフォルト引数で柔軟な関数設計 - map, filter, find, reduce は for ループより宣言的 - 分割代入とスプレッド構文で簡潔にデータを扱う

次回: 配列とオブジェクトを深く学びます。JavaScript のデータ構造の中核です。

参考文献: - MDN Web Docs「関数」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Functions) - MDN Web Docs「アロー関数式」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions) - MDN Web Docs「分割代入」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)

Lecture 5配列とオブジェクト — データをまとめて管理する

12:00

配列とオブジェクト — データをまとめて管理する

配列(Array)

配列は 順序を持つデータの集まり です。

配列の作成

const fruits = ["りんご", "バナナ", "オレンジ"];
const numbers = [1, 2, 3, 4, 5];
const mixed = ["文字列", 42, true, null];  // 異なる型の混在も可能(非推奨)
const empty = [];

要素へのアクセス

const fruits = ["りんご", "バナナ", "オレンジ"];

console.log(fruits[0]);  // "りんご"(インデックスは0から)
console.log(fruits[1]);  // "バナナ"
console.log(fruits[2]);  // "オレンジ"
console.log(fruits.length);  // 3

// 末尾の要素
console.log(fruits[fruits.length - 1]);  // "オレンジ"
console.log(fruits.at(-1));               // "オレンジ"(ES2022)

配列の操作メソッド

要素の追加・削除

const arr = ["a", "b", "c"];

arr.push("d");       // 末尾に追加 → ["a", "b", "c", "d"]
arr.pop();           // 末尾を削除 → ["a", "b", "c"]
arr.unshift("z");    // 先頭に追加 → ["z", "a", "b", "c"]
arr.shift();         // 先頭を削除 → ["a", "b", "c"]

// splice: 任意の位置で追加/削除
arr.splice(1, 1);           // index 1 から1個削除 → ["a", "c"]
arr.splice(1, 0, "x", "y"); // index 1 に挿入 → ["a", "x", "y", "c"]

注意: push, pop, splice は元の配列を変更(破壊的操作)します。

非破壊的メソッド(新しい配列を返す)

const arr = [3, 1, 4, 1, 5, 9];

// スライス(部分配列)
const sliced = arr.slice(1, 4);   // [1, 4, 1](元の配列は変わらない)

// 結合
const joined = arr.concat([2, 6]); // [3, 1, 4, 1, 5, 9, 2, 6]

// ソート(非破壊版 — ES2023)
const sorted = arr.toSorted((a, b) => a - b);  // [1, 1, 3, 4, 5, 9]

// 反転(非破壊版 — ES2023)
const reversed = arr.toReversed();  // [9, 5, 1, 4, 1, 3]

// 含まれるか
console.log(arr.includes(4));  // true
console.log(arr.indexOf(4));   // 2(最初に見つかったインデックス)

変換メソッド(前回の復習 + 追加)

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// map: 変換
const squared = numbers.map(n => n ** 2);
// [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

// filter: 抽出
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6, 8, 10]

// reduce: 集約
const sum = numbers.reduce((acc, n) => acc + n, 0);  // 55

// some: 1つでも条件を満たすか
const hasLargeNumber = numbers.some(n => n > 8);  // true

// every: すべて条件を満たすか
const allPositive = numbers.every(n => n > 0);  // true

// flat: ネストされた配列を平坦化
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.flat();  // [1, 2, 3, 4, 5, 6]

// flatMap: map + flat
const words = ["Hello World", "Good Morning"];
const chars = words.flatMap(s => s.split(" "));
// ["Hello", "World", "Good", "Morning"]

文字列との変換

// 配列 → 文字列
const arr = ["HTML", "CSS", "JavaScript"];
console.log(arr.join(", "));  // "HTML, CSS, JavaScript"
console.log(arr.join(" | ")); // "HTML | CSS | JavaScript"

// 文字列 → 配列
const csv = "田中,30,東京";
const parts = csv.split(",");  // ["田中", "30", "東京"]

オブジェクト(Object)

オブジェクトは キーと値のペアの集まり です。

オブジェクトの作成

const user = {
  name: "田中太郎",
  age: 30,
  email: "tanaka@example.com",
  isActive: true,
  hobbies: ["読書", "プログラミング"],
  address: {
    city: "東京",
    zipCode: "100-0001"
  }
};

プロパティへのアクセス

// ドット記法(推奨)
console.log(user.name);           // "田中太郎"
console.log(user.address.city);   // "東京"

// ブラケット記法(キーが変数の場合に使用)
const key = "name";
console.log(user[key]);           // "田中太郎"

// キーにスペースやハイフンがある場合
const config = { "max-retries": 3 };
console.log(config["max-retries"]);  // 3

プロパティの追加・変更・削除

const user = { name: "田中", age: 30 };

// 追加
user.email = "tanaka@example.com";

// 変更
user.age = 31;

// 削除
delete user.email;

console.log(user);  // { name: "田中", age: 31 }

const で宣言しても、オブジェクトの プロパティ は変更できます。const が保護するのは変数の再代入(user = {} など)であって、中身の変更ではありません。

オブジェクトのメソッド

const keys = Object.keys(user);     // ["name", "age"]
const values = Object.values(user);  // ["田中", 31]
const entries = Object.entries(user); // [["name", "田中"], ["age", 31]]

// キーの存在チェック
console.log("name" in user);          // true
console.log(user.hasOwnProperty("name")); // true

// オブジェクトのコピー(スプレッド構文)
const copy = { ...user };

// オブジェクトのマージ
const defaults = { theme: "light", lang: "ja" };
const settings = { theme: "dark" };
const merged = { ...defaults, ...settings };
// { theme: "dark", lang: "ja" }

Object.entries() + map(頻出パターン)

const prices = { apple: 150, banana: 100, orange: 200 };

const taxIncluded = Object.entries(prices).map(([name, price]) => {
  return { name, price: Math.floor(price * 1.1) };
});

console.log(taxIncluded);
// [
//   { name: "apple", price: 165 },
//   { name: "banana", price: 110 },
//   { name: "orange", price: 220 }
// ]

JSON

JSON(JavaScript Object Notation)は、JavaScript のオブジェクト構文に基づくデータ交換フォーマットです。API 通信でほぼ必ず使用します。

// オブジェクト → JSON文字列
const user = { name: "田中", age: 30 };
const json = JSON.stringify(user);
console.log(json);  // '{"name":"田中","age":30}'

// JSON文字列 → オブジェクト
const parsed = JSON.parse(json);
console.log(parsed.name);  // "田中"

// 整形出力(デバッグ用)
console.log(JSON.stringify(user, null, 2));
// {
//   "name": "田中",
//   "age": 30
// }

JSON と JavaScript オブジェクトの違い

項目 JavaScript オブジェクト JSON
キー クォート省略可 ダブルクォート必須
関数、undefined 可 文字列、数値、配列、オブジェクト、true/false/null のみ
コメント 不可
末尾のカンマ 不可

Map と Set

ES6 で追加されたデータ構造です。

Map(キーに任意の型を使える連想配列)

const map = new Map();
map.set("name", "田中");
map.set(42, "数値キー");
map.set(true, "真偽値キー");

console.log(map.get("name"));  // "田中"
console.log(map.size);          // 3
console.log(map.has("name"));   // true

map.delete("name");

// 反復
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

Set(重複のない値の集合)

const set = new Set([1, 2, 3, 3, 3]);
console.log(set.size);  // 3(重複は除去される)

set.add(4);
set.delete(1);
console.log(set.has(2));  // true

// 配列の重複除去(頻出テクニック)
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)];
console.log(unique);  // [1, 2, 3]

実践ワーク

// 1. 商品一覧の処理
const products = [
  { name: "ノートPC", price: 89800, category: "電子機器" },
  { name: "マウス", price: 2980, category: "電子機器" },
  { name: "デスク", price: 15800, category: "家具" },
  { name: "チェア", price: 29800, category: "家具" },
  { name: "モニター", price: 34800, category: "電子機器" },
];

// カテゴリ別にグループ化
// 電子機器の合計金額を計算
// 価格が10000円以上の商品名をリストアップ

// 2. 配列の重複除去
const tags = ["JavaScript", "HTML", "CSS", "JavaScript", "HTML"];
// → ["JavaScript", "HTML", "CSS"]

まとめと次回の準備

今回のポイント: - 配列: push/pop は破壊的、map/filter/slice は非破壊的 - オブジェクト: ドット記法でアクセス、Object.keys/values/entries で反復 - JSON: JSON.stringify()JSON.parse() で変換 - Set で重複除去、Map で任意キーの連想配列

次回: DOM操作。JavaScript で HTML の要素を選択し、変更し、追加・削除する方法を学びます。Web アプリ開発の核心です。

参考文献: - MDN Web Docs「Array」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array) - MDN Web Docs「Object」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object) - MDN Web Docs「JSON」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON)

Lecture 6DOM操作の基本 — HTMLをJavaScriptで動かす

12:00

DOM操作の基本 — HTMLをJavaScriptで動かす

DOMとは

DOM(Document Object Model)は、HTML をツリー構造のオブジェクトとして表現したものです。JavaScript はこの DOM を通じて HTML を読み取り、変更します。

<html>
  <body>
    <h1>タイトル</h1>
    <p>段落</p>
  </body>
</html>
document
└── html
    └── body
        ├── h1 → "タイトル"
        └── p  → "段落"

すべての HTML タグは ノード(node) として DOM ツリーに存在し、JavaScript でアクセスできます。

要素の取得

querySelector(推奨 — CSS セレクタで取得)

// IDで取得
const title = document.querySelector("#title");

// クラスで取得(最初に見つかった1つ)
const card = document.querySelector(".card");

// タグで取得
const firstParagraph = document.querySelector("p");

// 複合セレクタ
const navLink = document.querySelector("nav a.active");
const firstItem = document.querySelector("ul > li:first-child");

querySelectorAll(複数の要素を取得)

// すべての .card 要素
const cards = document.querySelectorAll(".card");

// NodeList(配列に似たオブジェクト)
console.log(cards.length);  // 要素数
cards.forEach(card => {
  console.log(card.textContent);
});

// 配列に変換(配列メソッドをすべて使いたい場合)
const cardsArray = [...cards];

従来のメソッド(参考)

const el1 = document.getElementById("title");           // ID(#なし)
const el2 = document.getElementsByClassName("card");     // クラス(.なし)
const el3 = document.getElementsByTagName("p");          // タグ

querySelector / querySelectorAll がより柔軟なため、こちらを使ってください。

テキストの変更

const title = document.querySelector("#title");

// textContent: テキストのみ(HTML無視、安全)
title.textContent = "新しいタイトル";

// innerHTML: HTMLを含む(XSS注意)
title.innerHTML = "新しい<em>タイトル</em>";

セキュリティ: ユーザー入力を innerHTML に直接入れると XSS(クロスサイトスクリプティング) の脆弱性になります。ユーザー入力は必ず textContent を使ってください。

// NG: XSS脆弱性
const userInput = '<img src=x onerror="alert(1)">';
element.innerHTML = userInput;  // 危険!スクリプトが実行される

// OK: 安全
element.textContent = userInput;  // テキストとして表示される

属性の操作

const link = document.querySelector("a");

// 取得
console.log(link.getAttribute("href"));

// 設定
link.setAttribute("href", "https://example.com");
link.setAttribute("target", "_blank");

// 削除
link.removeAttribute("target");

// 直接アクセス(一部の属性)
const img = document.querySelector("img");
img.src = "new-image.jpg";
img.alt = "新しい画像";

// data属性
const card = document.querySelector('[data-id="123"]');
console.log(card.dataset.id);      // "123"
card.dataset.status = "active";     // data-status="active" を追加

CSSの操作

style プロパティ(インラインスタイル)

const box = document.querySelector(".box");

box.style.backgroundColor = "#007bff";
box.style.color = "white";
box.style.padding = "16px";
box.style.borderRadius = "8px";
// CSS: kebab-case → JS: camelCase
// background-color → backgroundColor

classList(クラスの操作 — 推奨)

const btn = document.querySelector(".btn");

btn.classList.add("active");       // クラスを追加
btn.classList.remove("active");    // クラスを削除
btn.classList.toggle("active");    // あれば削除、なければ追加
btn.classList.contains("active");  // 含まれるか(true/false)
btn.classList.replace("old", "new"); // クラスを置換

CSS は CSS ファイルに書き、JavaScript ではクラスの付け外しだけ行う のがベストプラクティスです。

/* style.css */
.hidden { display: none; }
.visible { display: block; }
.fade-in { opacity: 1; transition: opacity 0.3s; }
.fade-out { opacity: 0; transition: opacity 0.3s; }
// JS: クラスの切り替えだけ
element.classList.toggle("hidden");

要素の作成と追加

createElement + appendChild

// 新しい要素を作成
const newCard = document.createElement("div");
newCard.classList.add("card");
newCard.textContent = "新しいカード";

// 親要素に追加
const container = document.querySelector(".container");
container.appendChild(newCard);

insertAdjacentHTML(HTML文字列から追加)

const container = document.querySelector(".container");

container.insertAdjacentHTML("beforeend", `
  <div class="card">
    <h3>新しいカード</h3>
    <p>テキスト</p>
  </div>
`);
位置 説明
"beforebegin" 要素の前
"afterbegin" 要素の最初の子として
"beforeend" 要素の最後の子として
"afterend" 要素の後

要素の削除

const card = document.querySelector(".card");
card.remove();  // 要素を削除

フォームの値の取得

// テキスト入力
const nameInput = document.querySelector("#name");
console.log(nameInput.value);  // 入力された文字列

// チェックボックス
const checkbox = document.querySelector("#agree");
console.log(checkbox.checked);  // true / false

// セレクトボックス
const select = document.querySelector("#category");
console.log(select.value);  // 選択された option の value

// テキストエリア
const textarea = document.querySelector("#message");
console.log(textarea.value);  // 入力された文字列

実践: 動的なリスト

<input type="text" id="itemInput" placeholder="アイテムを入力">
<button id="addBtn">追加</button>
<ul id="list"></ul>
const input = document.querySelector("#itemInput");
const addBtn = document.querySelector("#addBtn");
const list = document.querySelector("#list");

addBtn.addEventListener("click", () => {
  const text = input.value.trim();
  if (text === "") return;

  const li = document.createElement("li");
  li.textContent = text;

  // 削除ボタン
  const deleteBtn = document.createElement("button");
  deleteBtn.textContent = "×";
  deleteBtn.addEventListener("click", () => li.remove());
  li.appendChild(deleteBtn);

  list.appendChild(li);
  input.value = "";  // 入力欄をクリア
  input.focus();     // フォーカスを戻す
});

実践ワーク

  1. ボタンをクリックするとカウンターが増える/減る仕組みを作る
  2. テキスト入力欄にリアルタイムで文字数を表示する
  3. 「ダークモード切り替え」ボタンで body にクラスをトグルする
  4. 上の動的リストを実装し、削除ボタンも付ける

まとめと次回の準備

今回のポイント: - querySelector / querySelectorAll で要素を取得 - textContent(安全)と innerHTML(XSS注意)でテキスト変更 - classList.toggle() で CSS クラスの付け外し - createElement + appendChild で動的に要素を追加 - フォームの値は .value で取得

次回: イベント処理。クリック、入力、スクロール、キーボード — ユーザーの操作に反応するプログラムの書き方を学びます。

参考文献: - MDN Web Docs「DOM の紹介」(https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction) - MDN Web Docs「Document.querySelector()」(https://developer.mozilla.org/ja/docs/Web/API/Document/querySelector) - OWASP「XSS Prevention」(https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)

Lecture 7イベント処理 — クリック・入力・スクロールに反応する

12:00

イベント処理 — クリック・入力・スクロールに反応する

イベントとは

ユーザーがページ上で行う操作(クリック、キー入力、スクロールなど)を イベント と呼びます。JavaScript はイベントを検知して、それに応じた処理を実行できます。

addEventListener(イベントリスナーの登録)

const btn = document.querySelector("#myBtn");

btn.addEventListener("click", () => {
  console.log("ボタンがクリックされました");
});

構文: 要素.addEventListener(イベント名, コールバック関数)

主要なイベント一覧

カテゴリ イベント名 タイミング
マウス click クリック時
dblclick ダブルクリック時
mouseover マウスが乗った時
mouseout マウスが離れた時
キーボード keydown キーを押した時
keyup キーを離した時
フォーム input 入力内容が変わった時(リアルタイム)
change 値が確定した時(フォーカスが外れた時)
submit フォームが送信された時
focus フォーカスを得た時
blur フォーカスを失った時
ウィンドウ scroll スクロール時
resize ウィンドウサイズ変更時
DOMContentLoaded HTML解析完了時
load すべてのリソース読み込み完了時

イベントオブジェクト

コールバック関数は イベントオブジェクト を引数として受け取ります。

const btn = document.querySelector("#myBtn");

btn.addEventListener("click", (event) => {
  console.log(event.type);       // "click"
  console.log(event.target);     // クリックされた要素
  console.log(event.clientX);    // マウスのX座標
  console.log(event.clientY);    // マウスのY座標
  console.log(event.timeStamp);  // イベント発生時刻
});

event.target(クリックされた要素)

document.querySelector(".card-list").addEventListener("click", (e) => {
  console.log(e.target);          // 実際にクリックされた要素
  console.log(e.currentTarget);   // イベントリスナーが付いた要素
});

キーボードイベント

document.addEventListener("keydown", (e) => {
  console.log(e.key);    // "Enter", "a", "ArrowUp" など
  console.log(e.code);   // "Enter", "KeyA", "ArrowUp" など
  console.log(e.ctrlKey); // Ctrl が押されているか
  console.log(e.shiftKey); // Shift が押されているか

  // Ctrl+S でカスタム保存
  if (e.ctrlKey && e.key === "s") {
    e.preventDefault();  // ブラウザのデフォルト保存を抑止
    console.log("カスタム保存を実行");
  }
});

preventDefault(デフォルト動作の抑止)

// フォーム送信時にページ遷移を防ぐ
const form = document.querySelector("form");

form.addEventListener("submit", (e) => {
  e.preventDefault();  // デフォルトの送信(ページ遷移)を抑止

  const name = document.querySelector("#name").value;
  const email = document.querySelector("#email").value;
  console.log({ name, email });
  // ここで fetch() を使ってAPI送信する(第8回で学習)
});

// リンクのデフォルト遷移を防ぐ
const link = document.querySelector("a");
link.addEventListener("click", (e) => {
  e.preventDefault();
  console.log("リンクのURLは:", e.target.href);
});

イベントの伝播(バブリング)

イベントは子要素から親要素に向かって バブリング(泡のように上がる)します。

<div id="parent">
  <button id="child">クリック</button>
</div>
document.querySelector("#parent").addEventListener("click", () => {
  console.log("親がクリックされた");
});

document.querySelector("#child").addEventListener("click", () => {
  console.log("子がクリックされた");
});

// ボタンをクリックすると:
// "子がクリックされた"
// "親がクリックされた"(バブリングで親にも伝播)

stopPropagation(伝播の停止)

document.querySelector("#child").addEventListener("click", (e) => {
  e.stopPropagation();  // 親への伝播を止める
  console.log("子がクリックされた");
});
// ボタンをクリック → "子がクリックされた" のみ

イベント委任(Event Delegation)

多数の子要素にイベントリスナーを付ける代わりに、親要素に1つだけ付けるテクニックです。

<ul id="todoList">
  <li>タスク1 <button class="delete">×</button></li>
  <li>タスク2 <button class="delete">×</button></li>
  <li>タスク3 <button class="delete">×</button></li>
  <!-- 動的に追加されるアイテムにもイベントが効く -->
</ul>
// NG: 各ボタンにリスナーを付ける(動的追加に非対応)
document.querySelectorAll(".delete").forEach(btn => {
  btn.addEventListener("click", () => { /* ... */ });
});

// OK: 親に1つだけリスナーを付ける(イベント委任)
document.querySelector("#todoList").addEventListener("click", (e) => {
  if (e.target.classList.contains("delete")) {
    e.target.closest("li").remove();
  }
});

イベント委任の利点: 1. リスナーが1つだけなのでメモリ効率が良い 2. 動的に追加された要素にも自動的に効く 3. コードがシンプル

実践的なイベント処理パターン

リアルタイム文字数カウント

const textarea = document.querySelector("#message");
const counter = document.querySelector("#charCount");
const MAX_LENGTH = 200;

textarea.addEventListener("input", () => {
  const remaining = MAX_LENGTH - textarea.value.length;
  counter.textContent = `残り ${remaining} 文字`;
  counter.style.color = remaining < 20 ? "red" : "#666";
});

アコーディオン(開閉パネル)

<div class="accordion">
  <button class="accordion-header">セクション1</button>
  <div class="accordion-body">
    <p>コンテンツ1の内容</p>
  </div>
  <button class="accordion-header">セクション2</button>
  <div class="accordion-body">
    <p>コンテンツ2の内容</p>
  </div>
</div>
.accordion-body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
.accordion-body.open {
  max-height: 500px;
}
document.querySelector(".accordion").addEventListener("click", (e) => {
  if (e.target.classList.contains("accordion-header")) {
    const body = e.target.nextElementSibling;
    body.classList.toggle("open");
  }
});

スクロール時のヘッダー変化

let lastScrollY = 0;

window.addEventListener("scroll", () => {
  const header = document.querySelector(".header");
  const currentScrollY = window.scrollY;

  if (currentScrollY > 100) {
    header.classList.add("scrolled");
  } else {
    header.classList.remove("scrolled");
  }

  lastScrollY = currentScrollY;
});

debounce(連続イベントの抑制)

scrollinput イベントは1秒間に数十回発火します。負荷を抑えるために debounce を使います。

const debounce = (fn, delay) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
};

// 入力が止まって300ms後に検索を実行
const searchInput = document.querySelector("#search");
searchInput.addEventListener("input", debounce((e) => {
  console.log("検索:", e.target.value);
}, 300));

実践ワーク

  1. ボタンのクリックでカウンター(+1 / -1 / リセット)を実装
  2. テキストエリアにリアルタイム文字数カウンターを実装
  3. ダークモード切り替えボタン(body.classList.toggle("dark")
  4. アコーディオン(イベント委任で実装)

まとめと次回の準備

今回のポイント: - addEventListener でイベントを登録 - event.target でクリックされた要素を取得 - e.preventDefault() でデフォルト動作を抑止(フォーム送信など) - イベント委任: 親に1つリスナーを付けて、動的要素にも対応 - debounce で連続イベントの負荷を抑制

次回: Module 2 に入り、非同期処理と API 通信を学びます。fetch() で外部データを取得し、ページに表示する方法です。

参考文献: - MDN Web Docs「イベント入門」(https://developer.mozilla.org/ja/docs/Learn/JavaScript/Building_blocks/Events) - MDN Web Docs「EventTarget.addEventListener()」(https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener) - MDN Web Docs「イベントの伝播」(https://developer.mozilla.org/ja/docs/Learn/JavaScript/Building_blocks/Event_bubbling)

Lecture 8非同期処理とAPI通信 — 外部データを取得する

12:00

非同期処理とAPI通信 — 外部データを取得する

同期処理と非同期処理

同期処理(今まで書いてきたコード)

console.log("1番目");
console.log("2番目");
console.log("3番目");
// 必ず 1 → 2 → 3 の順に実行

非同期処理

console.log("1番目");
setTimeout(() => {
  console.log("2番目(1秒後)");
}, 1000);
console.log("3番目");
// 1番目 → 3番目 → 2番目(1秒後)

非同期処理は 待たずに次へ進む のが特徴です。API通信(サーバーとの通信)は数百ミリ秒〜数秒かかるため、非同期で行います。

Promise

Promise は非同期処理の結果を表すオブジェクトです。

const myPromise = new Promise((resolve, reject) => {
  const success = true;

  if (success) {
    resolve("成功しました");   // 成功時
  } else {
    reject("失敗しました");     // 失敗時
  }
});

myPromise
  .then(result => console.log(result))   // "成功しました"
  .catch(error => console.log(error));   // エラー時

Promise の3つの状態

状態 意味
pending 処理中(まだ結果が出ていない)
fulfilled 成功(resolve が呼ばれた)
rejected 失敗(reject が呼ばれた)

async / await(推奨)

ES2017 で追加された、Promise をより読みやすく書くための構文です。

// Promise チェーン(読みにくい)
fetchData()
  .then(data => processData(data))
  .then(result => displayResult(result))
  .catch(error => handleError(error));

// async/await(読みやすい)
const main = async () => {
  try {
    const data = await fetchData();
    const result = await processData(data);
    displayResult(result);
  } catch (error) {
    handleError(error);
  }
};

async 関数内で await を使うと、Promise が解決されるまで待ってから次の行に進みます。見た目は同期処理のように読めますが、実際には非同期です。

fetch API

fetch() は HTTP リクエストを送信するブラウザ標準の API です。

基本: GET リクエスト

const fetchUsers = async () => {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");

  if (!response.ok) {
    throw new Error(`HTTP Error: ${response.status}`);
  }

  const users = await response.json();  // JSONをパース
  console.log(users);
};

fetchUsers();

レスポンスオブジェクト

const response = await fetch(url);

console.log(response.ok);       // true(200-299)
console.log(response.status);   // 200, 404, 500 など
console.log(response.headers);  // レスポンスヘッダー

const json = await response.json();   // JSONとしてパース
const text = await response.text();   // テキストとして取得
const blob = await response.blob();   // バイナリ(画像など)

POST リクエスト

const createUser = async (userData) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(userData),
  });

  if (!response.ok) {
    throw new Error(`HTTP Error: ${response.status}`);
  }

  const newUser = await response.json();
  console.log("作成されたユーザー:", newUser);
};

createUser({ name: "田中", email: "tanaka@example.com" });

HTTPメソッド一覧

メソッド 用途
GET データの取得
POST データの作成
PUT データの全体更新
PATCH データの一部更新
DELETE データの削除

実践: APIデータをページに表示する

ユーザー一覧の表示

<div id="userList"></div>
const displayUsers = async () => {
  const container = document.querySelector("#userList");
  container.textContent = "読み込み中...";

  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);

    const users = await response.json();

    container.innerHTML = users.map(user => `
      <div class="card">
        <h3>${user.name}</h3>
        <p>${user.email}</p>
        <p>${user.company.name}</p>
      </div>
    `).join("");

  } catch (error) {
    container.textContent = `エラーが発生しました: ${error.message}`;
  }
};

displayUsers();

注意: この例では innerHTML に API のデータを直接入れています。信頼できない API の場合は textContent で安全に表示してください。JSONPlaceholder は学習用のモック API なので、この例では問題ありません。

検索機能付き

const searchInput = document.querySelector("#search");
let allUsers = [];

const loadUsers = async () => {
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  allUsers = await response.json();
  renderUsers(allUsers);
};

const renderUsers = (users) => {
  const container = document.querySelector("#userList");
  if (users.length === 0) {
    container.textContent = "該当するユーザーがいません";
    return;
  }
  container.innerHTML = users.map(user => `
    <div class="card">
      <h3>${user.name}</h3>
      <p>${user.email}</p>
    </div>
  `).join("");
};

searchInput.addEventListener("input", (e) => {
  const query = e.target.value.toLowerCase();
  const filtered = allUsers.filter(user =>
    user.name.toLowerCase().includes(query) ||
    user.email.toLowerCase().includes(query)
  );
  renderUsers(filtered);
});

loadUsers();

ローディング状態の管理

const fetchWithLoading = async (url) => {
  const spinner = document.querySelector("#spinner");
  const content = document.querySelector("#content");
  const errorMsg = document.querySelector("#error");

  spinner.style.display = "block";
  content.style.display = "none";
  errorMsg.style.display = "none";

  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();

    content.style.display = "block";
    return data;
  } catch (error) {
    errorMsg.textContent = error.message;
    errorMsg.style.display = "block";
    return null;
  } finally {
    spinner.style.display = "none";  // 成功でも失敗でも実行
  }
};

複数のAPIを並列で呼ぶ

// 直列(遅い): 1つ完了してから次
const user = await fetch("/api/user").then(r => r.json());
const posts = await fetch("/api/posts").then(r => r.json());
// 合計時間 = user取得時間 + posts取得時間

// 並列(速い): 同時にリクエスト
const [user, posts] = await Promise.all([
  fetch("/api/user").then(r => r.json()),
  fetch("/api/posts").then(r => r.json()),
]);
// 合計時間 = max(user取得時間, posts取得時間)

Promise.all は渡したすべての Promise が完了するまで待ちます。1つでも失敗すると全体が失敗になります。

実践ワーク

  1. JSONPlaceholder API (https://jsonplaceholder.typicode.com/posts) から投稿一覧を取得して表示
  2. ローディング中に「読み込み中...」を表示する
  3. 検索ボックスでタイトルのフィルタリングを実装
  4. 投稿をクリックすると詳細(/posts/{id})を取得して表示

まとめと次回の準備

今回のポイント: - 非同期処理: JavaScript はブロックせずに次へ進む - async / await で非同期コードを読みやすく書く - fetch() でAPI通信。response.json() でJSONパース - try / catch でエラーハンドリング - Promise.all で複数リクエストを並列実行

次回: エラー処理とデバッグ。バグを見つけて直す技術は、プログラミングで最も実用的なスキルです。

参考文献: - MDN Web Docs「async function」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function) - MDN Web Docs「Fetch API」(https://developer.mozilla.org/ja/docs/Web/API/Fetch_API) - JSONPlaceholder(https://jsonplaceholder.typicode.com/)— 学習用無料API

Lecture 9エラー処理とデバッグ — バグを見つけて直す技術

10:00

エラー処理とデバッグ — バグを見つけて直す技術

エラーの種類

JavaScript のエラーは大きく3種類に分けられます。

1. 構文エラー(SyntaxError)

コードの書き方が間違っている。実行前にブラウザが検出。

// 括弧の閉じ忘れ
if (x > 5 {          // SyntaxError: Unexpected token '{'
  console.log("OK");
}

// クォートの閉じ忘れ
const name = "田中;   // SyntaxError: Invalid or unexpected token

2. 実行時エラー(RuntimeError)

実行中に発生するエラー。

// 存在しない変数の参照
console.log(userName);  // ReferenceError: userName is not defined

// 関数でないものを呼び出し
const x = 5;
x();  // TypeError: x is not a function

// nullのプロパティにアクセス
const user = null;
console.log(user.name);  // TypeError: Cannot read properties of null

3. 論理エラー(Logic Error)

エラーは出ないが、結果が間違っている。最も発見しにくい。

// 意図: 平均を計算
const average = (a, b) => a + b / 2;  // NG: 演算子の優先順位
// average(10, 20) → 10 + 10 = 20(正解は15)

const average = (a, b) => (a + b) / 2;  // OK
// average(10, 20) → 15

よくあるエラーと対処法

エラー 原因 対処
TypeError: Cannot read properties of undefined 未定義のプロパティにアクセス オプショナルチェイニング ?. を使う
ReferenceError: x is not defined 変数名のタイプミス、スコープ外 スペルを確認、変数の宣言位置を確認
TypeError: x is not a function 関数でないものを呼んでいる 変数名の衝突を確認
SyntaxError: Unexpected token 括弧やカンマの過不足 エディタの括弧マッチング機能を使う
RangeError: Maximum call stack 無限再帰 再帰の終了条件を確認

try / catch / finally

const parseJSON = (jsonString) => {
  try {
    const data = JSON.parse(jsonString);
    return data;
  } catch (error) {
    console.error("JSONのパースに失敗:", error.message);
    return null;
  } finally {
    console.log("パース処理が完了しました");  // 成功・失敗どちらでも実行
  }
};

console.log(parseJSON('{"name": "田中"}'));  // { name: "田中" }
console.log(parseJSON('不正なJSON'));          // null

カスタムエラーを投げる

const validateAge = (age) => {
  if (typeof age !== "number") {
    throw new TypeError("年齢は数値で入力してください");
  }
  if (age < 0 || age > 150) {
    throw new RangeError("年齢は0〜150の範囲で入力してください");
  }
  return true;
};

try {
  validateAge("二十歳");
} catch (error) {
  console.error(`${error.name}: ${error.message}`);
  // TypeError: 年齢は数値で入力してください
}

Chrome DevTools でデバッグ

Console(基本)

console.log("一般的な出力");
console.warn("警告");
console.error("エラー");
console.table([{ name: "田中", age: 30 }, { name: "鈴木", age: 25 }]);
console.time("処理時間");
// ... 処理 ...
console.timeEnd("処理時間");  // "処理時間: 15.2ms"

console.group("ユーザー情報");
console.log("名前: 田中");
console.log("年齢: 30");
console.groupEnd();

ブレークポイント(最強のデバッグツール)

  1. F12 → Sources タブ
  2. 左パネルでJSファイルを選択
  3. 行番号をクリックしてブレークポイントを設定(青い印が付く)
  4. ページを操作してその行が実行されると、実行が一時停止する
  5. 右パネルで変数の値を確認できる

ステップ実行

ブレークポイントで停止中に:

ボタン 動作 ショートカット
Resume 次のブレークポイントまで実行 F8
Step Over 1行ずつ実行(関数内に入らない) F10
Step Into 関数の中に入って実行 F11
Step Out 現在の関数から抜ける Shift+F11

debugger 文

コードに debugger を書くと、DevTools が開いている場合にそこで一時停止します。

const calculate = (a, b) => {
  debugger;  // ここで停止
  const result = a * b;
  return result;
};

本番環境にデプロイする前に debugger を削除してください。

Watch(変数の監視)

Sources パネルの右側「Watch」に変数名を追加すると、ステップ実行中にその変数の値をリアルタイムで確認できます。

デバッグの手順

  1. 再現: バグが起きる操作を特定する
  2. 仮説: 「この部分が原因ではないか」と仮説を立てる
  3. 確認: console.log やブレークポイントで値を確認
  4. 修正: 原因を修正
  5. 検証: バグが直ったか確認。他の部分に影響がないかも確認

二分探索デバッグ

どこでバグが起きているかわからない場合:

  1. コードの真ん中に console.log("ここまでOK") を挿入
  2. 表示されれば前半は正常 → 後半を同様に二分割
  3. 表示されなければ前半に問題 → 前半を同様に二分割
  4. 問題の行を特定するまで繰り返す

防御的プログラミング

入力の検証

const greet = (name) => {
  if (typeof name !== "string" || name.trim() === "") {
    return "名前が不正です";
  }
  return `こんにちは、${name}さん!`;
};

デフォルト値

const getConfig = (options = {}) => {
  const config = {
    theme: "light",
    lang: "ja",
    fontSize: 16,
    ...options,
  };
  return config;
};

console.log(getConfig({ theme: "dark" }));
// { theme: "dark", lang: "ja", fontSize: 16 }

早期リターン(ガード節)

// NG: ネストが深い
const processUser = (user) => {
  if (user) {
    if (user.isActive) {
      if (user.age >= 18) {
        // 処理
      }
    }
  }
};

// OK: ガード節で早期リターン
const processUser = (user) => {
  if (!user) return;
  if (!user.isActive) return;
  if (user.age < 18) return;
  // 処理
};

実践ワーク

  1. Chrome DevTools の Sources タブでブレークポイントを設定し、変数の値を確認する
  2. 意図的にバグを含むコードを書き、Console のエラーメッセージから原因を特定して修正する:
// 以下のコードにはバグが3つあります。見つけて修正してください
const calcDiscount = (price, discountRate) => {
  if (price < 0) {
    console.log("価格が不正です");
  }
  const discounted = price * discountRate;
  return Mathfloor(discounted);
};

const items = ["りんご", "バナナ", "オレンジ"];
console.log(items[3].toUpperCase());

const user = { name: "田中" };
console.log(user.address.city);

まとめと次回の準備

今回のポイント: - エラーの3種類: 構文エラー、実行時エラー、論理エラー - try / catch でエラーを捕捉し、アプリのクラッシュを防ぐ - Chrome DevTools のブレークポイントが最強のデバッグツール - console.log だけでなく console.table, console.time も活用 - 防御的プログラミング: 入力検証、デフォルト値、ガード節

次回(最終回): これまでの全スキルを統合して、ToDo アプリを1から作り上げます。

参考文献: - MDN Web Docs「エラーハンドリング」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#exception_handling_statements) - Google「Chrome DevTools でデバッグする」(https://developer.chrome.com/docs/devtools/javascript/) - MDN Web Docs「Console API」(https://developer.mozilla.org/ja/docs/Web/API/console)

Lecture 10総合演習 — ToDoアプリを作ろう

15:00

総合演習 — ToDoアプリを作ろう

この講座で学んだスキルの全体像

9つの講義を通じて、JavaScript の基礎から実践までを学びました。最終回では、すべてのスキルを統合して ToDo アプリ を完成させます。

講義 学んだスキル 本演習での活用
第1回 JavaScript の基本、Console 開発環境
第2回 変数、データ型、演算子 データの管理
第3回 条件分岐、ループ フィルタリング、描画
第4回 関数、アロー関数、配列メソッド すべての処理を関数化
第5回 配列、オブジェクト、JSON ToDo データの管理
第6回 DOM操作 画面の描画と更新
第7回 イベント処理 クリック、入力、キーボード
第8回 非同期処理、API localStorage で永続化
第9回 エラー処理、デバッグ 入力バリデーション

完成イメージの機能

  1. タスクの追加: テキスト入力 + 追加ボタン(Enter キーでも追加可)
  2. タスクの完了: チェックボックスで完了/未完了を切り替え
  3. タスクの削除: 削除ボタンでタスクを削除
  4. フィルター: すべて / 未完了 / 完了済み
  5. 残タスク数の表示: 未完了のタスク数をリアルタイム表示
  6. 永続化: localStorage でブラウザを閉じてもデータが残る

ファイル構造

todo-app/
├── index.html
├── css/
│   └── style.css
└── js/
    └── app.js

Step 1: HTML

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ToDo App</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div class="app">
    <h1>ToDo App</h1>

    <!-- 入力エリア -->
    <div class="input-area">
      <input type="text" id="todoInput" placeholder="タスクを入力..." autofocus>
      <button id="addBtn">追加</button>
    </div>

    <!-- フィルター -->
    <div class="filters">
      <button class="filter-btn active" data-filter="all">すべて</button>
      <button class="filter-btn" data-filter="active">未完了</button>
      <button class="filter-btn" data-filter="completed">完了済み</button>
    </div>

    <!-- タスクリスト -->
    <ul id="todoList"></ul>

    <!-- ステータスバー -->
    <div class="status-bar">
      <span id="taskCount">0個のタスク</span>
      <button id="clearCompleted">完了済みを削除</button>
    </div>
  </div>

  <script src="js/app.js"></script>
</body>
</html>

Step 2: CSS

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

:root {
  --color-primary: #6366f1;
  --color-primary-dark: #4f46e5;
  --color-bg: #f1f5f9;
  --color-card: #ffffff;
  --color-text: #1e293b;
  --color-text-light: #94a3b8;
  --color-border: #e2e8f0;
  --color-danger: #ef4444;
  --color-success: #22c55e;
  --radius: 8px;
}

body {
  font-family: "Segoe UI", "Noto Sans JP", sans-serif;
  background-color: var(--color-bg);
  color: var(--color-text);
  min-height: 100vh;
  display: flex;
  justify-content: center;
  padding: 40px 16px;
}

.app {
  width: 100%;
  max-width: 520px;
}

h1 {
  text-align: center;
  font-size: 2rem;
  margin-bottom: 24px;
  color: var(--color-primary);
}

/* 入力エリア */
.input-area {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.input-area input {
  flex: 1;
  padding: 12px 16px;
  border: 2px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 1rem;
  transition: border-color 0.2s;
}

.input-area input:focus {
  outline: none;
  border-color: var(--color-primary);
}

.input-area button {
  padding: 12px 24px;
  background-color: var(--color-primary);
  color: white;
  border: none;
  border-radius: var(--radius);
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.2s;
}

.input-area button:hover {
  background-color: var(--color-primary-dark);
}

/* フィルター */
.filters {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.filter-btn {
  flex: 1;
  padding: 8px;
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.875rem;
  transition: all 0.2s;
}

.filter-btn.active {
  background-color: var(--color-primary);
  color: white;
  border-color: var(--color-primary);
}

/* タスクリスト */
#todoList {
  list-style: none;
}

.todo-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: var(--color-card);
  border-radius: var(--radius);
  margin-bottom: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  transition: opacity 0.2s;
}

.todo-item.completed .todo-text {
  text-decoration: line-through;
  color: var(--color-text-light);
}

.todo-item input[type="checkbox"] {
  width: 20px;
  height: 20px;
  cursor: pointer;
  accent-color: var(--color-success);
}

.todo-text {
  flex: 1;
  font-size: 1rem;
}

.delete-btn {
  background: none;
  border: none;
  color: var(--color-text-light);
  font-size: 1.25rem;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 4px;
  transition: all 0.2s;
}

.delete-btn:hover {
  color: var(--color-danger);
  background-color: #fef2f2;
}

/* ステータスバー */
.status-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 0;
  font-size: 0.875rem;
  color: var(--color-text-light);
}

#clearCompleted {
  background: none;
  border: none;
  color: var(--color-text-light);
  cursor: pointer;
  font-size: 0.875rem;
}

#clearCompleted:hover {
  color: var(--color-danger);
  text-decoration: underline;
}

/* 空状態 */
.empty-message {
  text-align: center;
  padding: 32px;
  color: var(--color-text-light);
}

Step 3: JavaScript

// ========================================
// 状態管理
// ========================================

// localStorageからタスクを読み込み(永続化)
const loadTodos = () => {
  try {
    const stored = localStorage.getItem("todos");
    return stored ? JSON.parse(stored) : [];
  } catch {
    return [];
  }
};

// localStorageに保存
const saveTodos = (todos) => {
  localStorage.setItem("todos", JSON.stringify(todos));
};

// アプリの状態
let todos = loadTodos();
let currentFilter = "all";

// ========================================
// DOM要素の取得
// ========================================
const todoInput = document.querySelector("#todoInput");
const addBtn = document.querySelector("#addBtn");
const todoList = document.querySelector("#todoList");
const taskCount = document.querySelector("#taskCount");
const clearCompletedBtn = document.querySelector("#clearCompleted");
const filterBtns = document.querySelectorAll(".filter-btn");

// ========================================
// タスクの操作(データ層)
// ========================================

const addTodo = (text) => {
  const trimmed = text.trim();
  if (trimmed === "") return;

  const newTodo = {
    id: Date.now(),          // 一意のID(タイムスタンプ)
    text: trimmed,
    completed: false,
    createdAt: new Date().toISOString(),
  };

  todos.push(newTodo);
  saveTodos(todos);
  render();
};

const toggleTodo = (id) => {
  const todo = todos.find(t => t.id === id);
  if (todo) {
    todo.completed = !todo.completed;
    saveTodos(todos);
    render();
  }
};

const deleteTodo = (id) => {
  todos = todos.filter(t => t.id !== id);
  saveTodos(todos);
  render();
};

const clearCompleted = () => {
  todos = todos.filter(t => !t.completed);
  saveTodos(todos);
  render();
};

// ========================================
// フィルタリング
// ========================================

const getFilteredTodos = () => {
  switch (currentFilter) {
    case "active":
      return todos.filter(t => !t.completed);
    case "completed":
      return todos.filter(t => t.completed);
    default:
      return todos;
  }
};

// ========================================
// 描画(UI層)
// ========================================

const render = () => {
  const filtered = getFilteredTodos();

  // タスクリストの描画
  if (filtered.length === 0) {
    todoList.innerHTML = '<li class="empty-message">タスクがありません</li>';
  } else {
    todoList.innerHTML = filtered.map(todo => `
      <li class="todo-item ${todo.completed ? "completed" : ""}" data-id="${todo.id}">
        <input type="checkbox" ${todo.completed ? "checked" : ""}>
        <span class="todo-text">${escapeHTML(todo.text)}</span>
        <button class="delete-btn" aria-label="削除">×</button>
      </li>
    `).join("");
  }

  // 残タスク数の更新
  const activeCount = todos.filter(t => !t.completed).length;
  taskCount.textContent = `${activeCount}個の未完了タスク`;

  // フィルターボタンのアクティブ状態
  filterBtns.forEach(btn => {
    btn.classList.toggle("active", btn.dataset.filter === currentFilter);
  });
};

// XSS対策: HTMLエスケープ
const escapeHTML = (str) => {
  const div = document.createElement("div");
  div.textContent = str;
  return div.innerHTML;
};

// ========================================
// イベントリスナー
// ========================================

// 追加ボタン
addBtn.addEventListener("click", () => {
  addTodo(todoInput.value);
  todoInput.value = "";
  todoInput.focus();
});

// Enterキーで追加
todoInput.addEventListener("keydown", (e) => {
  if (e.key === "Enter") {
    addTodo(todoInput.value);
    todoInput.value = "";
  }
});

// タスクリスト(イベント委任)
todoList.addEventListener("click", (e) => {
  const item = e.target.closest(".todo-item");
  if (!item) return;

  const id = Number(item.dataset.id);

  // チェックボックス
  if (e.target.type === "checkbox") {
    toggleTodo(id);
  }

  // 削除ボタン
  if (e.target.classList.contains("delete-btn")) {
    deleteTodo(id);
  }
});

// フィルターボタン
filterBtns.forEach(btn => {
  btn.addEventListener("click", () => {
    currentFilter = btn.dataset.filter;
    render();
  });
});

// 完了済みを削除
clearCompletedBtn.addEventListener("click", clearCompleted);

// ========================================
// 初期描画
// ========================================
render();

コードの解説

設計のポイント

  1. データとUIの分離: todos 配列(データ)と render() 関数(UI)を分離。データを変更したら render() を呼ぶだけでUIが更新される
  2. イベント委任: todoList に1つだけイベントリスナーを付けて、動的に追加されるアイテムにも対応
  3. XSSエスケープ: escapeHTML() でユーザー入力をサニタイズ
  4. localStorage: JSON.stringify / JSON.parse でデータを永続化
  5. Date.now() でID生成: 簡易的な一意ID(本格的なアプリではUUIDを使う)

localStorage の補足

// 保存
localStorage.setItem("key", "value");

// 取得
const value = localStorage.getItem("key");

// 削除
localStorage.removeItem("key");

// 全削除
localStorage.clear();

localStorage はブラウザに文字列としてデータを保存します。オブジェクトや配列は JSON.stringify() で文字列に変換してから保存し、JSON.parse() で復元します。

注意: localStorage は約 5MB の容量制限があり、同じオリジン(ドメイン)内で共有されます。機密データの保存には使わないでください。

機能拡張のアイデア

機能 使う技術
ドラッグ&ドロップで並べ替え HTML Drag and Drop API
タスクの編集(ダブルクリック) イベント処理 + DOM操作
期限の設定 <input type="date"> + Date
カテゴリ / タグ オブジェクトにプロパティ追加
ダークモード classList.toggle("dark") + CSS変数
API連携(クラウド保存) fetch() + バックエンドAPI

この講座の振り返りと次のステップ

学んだこと

文法: 変数、データ型、演算子、条件分岐、ループ、関数、配列、オブジェクト

DOM: 要素の取得・変更・作成・削除、イベント処理、イベント委任

非同期: Promise, async/await, fetch API

実践: エラー処理、デバッグ、XSS対策、localStorage

さらに学びたい方へ

方向性 内容 リソース
フレームワーク React, Vue.js, Svelte 各公式ドキュメント
TypeScript 型安全な JavaScript typescriptlang.org
Node.js サーバーサイド JavaScript nodejs.org
テスト Jest, Vitest 各公式ドキュメント
Web API Canvas, Web Audio, WebSocket MDN Web Docs

最後に

JavaScript はブラウザさえあれば誰でも始められる、最もアクセスしやすいプログラミング言語です。そして、フロントエンド、バックエンド、モバイルアプリ、デスクトップアプリ、ゲーム開発まで、あらゆる領域で使われています。

この講座で作った ToDo アプリは小さなアプリですが、データ管理、UI描画、イベント処理、永続化という Web アプリの基本構造 がすべて含まれています。ここから先、React や Vue.js などのフレームワークを学ぶ際も、この基礎が確実に役立ちます。

「動くものを作る」ことが最大の学習です。 次は自分のアイデアでアプリを作ってみてください。

参考文献: - MDN Web Docs「JavaScript ガイド」(https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide) - MDN Web Docs「Web Storage API」(https://developer.mozilla.org/ja/docs/Web/API/Web_Storage_API) - javascript.info(https://ja.javascript.info/)— 無料の包括的チュートリアル