はじめに (対象読者・この記事でわかること)

この記事は、プログラミングを始めたばかりの初心者から、ある程度の経験はあるが「JavaScriptの本格的な使い方」がまだ曖昧な方を対象にしています。
- 対象読者:HTML/CSSの基礎があり、これから JavaScript で動的な Web ページや簡単なサーバーサイドスクリプトを書きたい方
- この記事でわかること
1. 変数宣言やデータ型、制御構文といった基本文法
2. ES6 以降で導入されたモジュール、アロー関数、テンプレートリテラルの活用方法
3. 非同期処理(Promise・async/await)の実装とエラーハンドリングのコツ
4. Node.js を使ってローカルで実行できる簡単なスクリプトの作成手順

執筆のきっかけは、質問サイトや SNS で「JavaScript が分からない」「教材が多すぎてどこから手を付ければいいか分からない」といった声を多数目にしたことです。そこで、基礎から実践的な応用までを一つの記事にまとめ、すぐに手を動かせる形にしたいという思いで執筆しました。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。
- HTML/CSS の基本:DOM を操作する際に要素構造をイメージしやすくなります。
- コマンドラインの基本操作:Node.js のインストールやスクリプト実行に必要です。
- Git の基礎(任意):サンプルコードをクローンしたり、変更履歴を管理したい場合に便利です。

JavaScript の全体像と学習のロードマップ

JavaScript は「クライアントサイド」だけでなく、Node.js による「サーバーサイド」でも使える汎用的なプログラミング言語です。1995 年に Netscape が開発した当時は「ブラウザ上で簡単なインタラクションを実装する」ためだけのスクリプト言語でしたが、ECMAScript 標準の策定とともに次第に機能が拡張され、現在ではフロントエンドフレームワーク(React、Vue、Angular)やバックエンド(Express、NestJS)まで幅広くカバーしています。

なぜ「モジュール化」と「非同期処理」が重要なのか

  • モジュール化:大規模プロジェクトになると、1 ファイルにすべてを書き込むとコードが読みにくく、保守が困難になります。ES6 の import / export 文を使うことで、機能ごとにファイルを分割し、再利用性とテスト容易性を向上させられます。
  • 非同期処理:ブラウザ上でネットワーク通信やタイマー処理を行う際、同期的に実行すると UI がフリーズしてしまいます。Promiseasync/await を活用すれば、非同期コードを直感的に記述でき、エラーハンドリングも統一的に行えます。

これらの概念を押さえることで、「実務で求められるコードの可読性・拡張性」を自然に身につけられます。以下では、実際に手を動かしながら学べる具体的な手順を示します。

実践的なコード例で学ぶ JavaScript

この章では、Node.js 環境を前提に、「簡易的な TODO アプリ」を作成しながら、変数宣言、モジュール化、非同期処理の3つの柱を実装します。各ステップごとにコード例と解説を添えているため、エディタにコピペしながら動作確認できます。

ステップ1 環境構築とプロジェクト雛形の作成

  1. Node.js のインストール
    bash # 公式サイトから LTS バージョンを取得しインストール # macOS/Homebrew の場合 brew install node # Windows は公式インストーラを使用
  2. プロジェクトディレクトリの作成
    bash mkdir js-todo-demo && cd js-todo-demo npm init -y # package.json を自動生成
  3. ESモジュールとして実行できるよう設定
    package.json に以下を追記
    json { "type": "module" } これで拡張子 .js でも import / export が利用可能になります。

  4. エディタでファイル構成を作成
    js-todo-demo/ ├─ src/ │ ├─ index.js # エントリーポイント │ └─ todoService.js # TODO データ管理ロジック(モジュール化) └─ data/ └─ todos.json # 永続化用の簡易データベース(JSON)

ステップ2 データ管理モジュール(todoService.js)を実装

Javascript
// src/todoService.js import { promises as fs } from 'fs'; import path from 'path'; const DATA_FILE = path.resolve('data', 'todos.json'); /** * JSON ファイルから TODO 配列を読み込む * @returns {Promise<Array<{id:number, text:string, done:boolean}>>} */ export async function loadTodos() { try { const raw = await fs.readFile(DATA_FILE, 'utf-8'); return JSON.parse(raw); } catch (err) { // ファイルが無い場合は空配列で初期化 if (err.code === 'ENOENT') return []; throw err; } } /** * TODO 配列を書き戻す * @param {Array} todos */ export async function saveTodos(todos) { const data = JSON.stringify(todos, null, 2); await fs.writeFile(DATA_FILE, data, 'utf-8'); } /** * 新しい TODO を追加 * @param {string} text */ export async function addTodo(text) { const todos = await loadTodos(); const newTodo = { id: Date.now(), text, done: false }; todos.push(newTodo); await saveTodos(todos); return newTodo; } /** * TODO の完了フラグをトグル * @param {number} id */ export async function toggleTodo(id) { const todos = await loadTodos(); const todo = todos.find(t => t.id === id); if (!todo) throw new Error('対象の TODO が見つかりません'); todo.done = !todo.done; await saveTodos(todos); return todo; }

ポイント解説
- 非同期 I/Ofs.promises を使い、async/await でシンプルに記述。
- エラーハンドリングtry/catch でファイル不存在時に空配列を返すようにし、実運用でも安全に動作。
- モジュール化export / import により、他ファイルから機能を呼び出しやすくしています。

ステップ3 エントリーポイント(index.js)で操作を体験

Javascript
// src/index.js import readline from 'readline'; import { addTodo, loadTodos, toggleTodo } from './todoService.js'; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); function showMenu() { console.log('\n=== TODO アプリ ==='); console.log('1. TODO 一覧表示'); console.log('2. TODO 追加'); console.log('3. TODO 完了/未完了トグル'); console.log('0. 終了'); rl.question('番号を選択してください: ', handleChoice); } async function handleChoice(choice) { switch (choice.trim()) { case '1': const todos = await loadTodos(); console.table(todos); break; case '2': rl.question('追加したい TODO の内容: ', async (text) => { const newItem = await addTodo(text); console.log('✅ 追加しました:', newItem); showMenu(); }); return; case '3': rl.question('対象の TODO ID: ', async (idStr) => { const id = Number(idStr); try { const updated = await toggleTodo(id); console.log('✅ 状態が更新されました:', updated); } catch (e) { console.error('❌ エラー:', e.message); } showMenu(); }); return; case '0': rl.close(); console.log('アプリを終了します。'); return; default: console.log('⚠️ 無効な選択です。'); } showMenu(); } showMenu();

このスクリプトは Node の標準モジュール readline を使って CLI 上で対話的に操作できる簡易 TODO アプリです。
- await loadTodos() で現在のタスクを取得し、console.table で見やすく表示。
- addTodotoggleTodo は先ほど作成したモジュールを呼び出すだけで完結。
- エラーメッセージは try/catch で捕捉し、ユーザーに分かりやすく伝えます。

ハマった点やエラー解決

発生したエラー 原因 解決策
SyntaxError: Cannot use import statement outside a module package.json"type": "module" が設定されていない npm init -y 後に package.json"type": "module" を追加
Error: ENOENT: no such file or directory, open 'data/todos.json' data ディレクトリが存在しない mkdir -p data でディレクトリを先に作成
ReferenceError: readline is not defined readline をインポート忘れ import readline from 'readline'; を追加
TypeError: todos.map is not a function loadTodosnull を返した loadTodoscatch で空配列 [] を返すように修正(上記コード参照)

これらのエラーは、「実装だけでなく環境設定やファイル構成」が正しいかどうかを確認することで回避できます。特に Node.js の ESモジュール化は、従来の CommonJS (require) とは挙動が異なるため、package.json の設定が鍵になります。

まとめ

本記事では、

  • JavaScript の基礎文法から ES6 以降のモジュール化非同期処理までの概念を整理し、
  • Node.js 環境で実際に動く CLI TODO アプリを例に、コードの構造化エラーハンドリングの実践方法を解説しました。

重要ポイント

  1. ESモジュールpackage.json"type": "module" が必須。
  2. 非同期 I/Oasync/await + fs.promises でシンプルに記述でき、エラーハンドリングも統一的に行える。
  3. ファイル・ディレクトリの事前準備は忘れがちだが、実行時エラーの原因の多くを占める。

この手順を踏めば、読者は 「実務で求められる JavaScript の基本設計」 を体感でき、次のステップとしてフロントエンドフレームワークやテスト自動化へスムーズに移行できるはずです。

参考資料