JavaScript
ブラウザで動く唯一の言語として生まれ、今やサーバまで覆うWebの共通語。
概要
JavaScriptは、Webページに「動き」を与えるためにブラウザに搭載されたプログラミング言語です。HTML が構造、CSS が見た目を担うのに対し、JavaScriptは振る舞いを担います。ボタンのクリックに反応する、入力内容をその場で検証する、ページを再読み込みせずにサーバからデータを取ってきて画面を書き換える——現代のWebアプリの対話性は、すべてJavaScriptの仕事です。
もともとブラウザ専用の言語でしたが、2009年の Node.js 登場によりサーバ側でも動くようになり、フロントエンドとバックエンドを同じ言語で書けるフルスタック開発が現実になりました。現在では CLI ツール、デスクトップアプリ、サーバレス関数まで、JavaScript が動く場所は広がり続けています。言語仕様は ECMAScript として標準化されており、「ES2015(ES6)」以降、毎年更新されています。
なぜ生まれたか
1990年代半ばのWebページは、サーバから受け取ったHTMLをただ表示するだけの静的な文書でした。フォームの入力チェック一つとっても、送信してサーバの応答を待たなければ結果が分からない。ページ側で即座に反応する手段が何もなかったのです。
1995年、Netscape社のブレンダン・アイクが、ブラウザ内で動く軽量スクリプト言語としてJavaScriptをわずか10日で設計しました(Javaと名前が似ているのはマーケティング上の理由で、言語としては別物です)。その後2005年頃、ページを再読み込みせずにサーバと非同期通信する手法が「Ajax」と名付けられ、Google Maps のようにデスクトップアプリ並みに操作できるWebアプリが登場すると、JavaScriptは「ちょっとした飾り付けの言語」から「アプリケーションプラットフォームの中核」へと一気に地位を変えました。
詳細
DOM操作とイベント駆動
ブラウザ上のJavaScriptの基本は、DOM(HTMLから構築されたツリー)の読み書きと、イベントへの反応です。document.querySelector で要素を取得し、addEventListener で「クリックされたらこの関数を実行する」と登録する——プログラムが上から下へ流れるのではなく、「出来事(イベント)に応じて関数が呼ばれる」イベント駆動のスタイルが、この言語の基本的な考え方です。
シングルスレッドとイベントループ
JavaScriptの実行モデルの核心は、「シングルスレッドで動く」ことです。コードを実行する場所(コールスタック)は一つしかなく、重い処理で塞がるとクリックにもスクロールにも反応できなくなります。そこで時間のかかる処理(通信、タイマーなど)はブラウザや Node.js の裏方に委ね、完了したら結果をキューに積んでもらい、コールスタックが空いたタイミングで受け取る——この仕組みがイベントループです。
たとえば fetch を呼ぶと、通信そのものは裏方に渡され、JavaScriptはすぐ次の処理へ進みます。応答が届くとコールバックがキューに積まれ、コールスタックが空いたタイミングでイベントループがそれを実行します。
「一人の料理人が、オーブン任せにできる作業は任せて、手が空いたら次の注文を捌く」イメージです。スレッドを増やさずに大量の並行リクエストを捌けるこの特性は、Node.js がサーバサイドで受け入れられた理由でもあります。
この動きは、実際のコードを1ステップずつ追ってみると腑に落ちます。
1console.log("A");2setTimeout(() => console.log("B"), 0);3fetch("/data").then(() => console.log("C"));4console.log("D");
(まだ何も出力されていません)「次へ」を押すと、このコードが1ステップずつどう実行されるかを追えます。
非同期処理の書き方の進化
非同期の結果を受け取る書き方は、コールバック関数の入れ子(いわゆる「コールバック地獄」)から始まり、ES2015で「将来の結果」を表すオブジェクトである Promise が導入され、さらに async/await によって非同期処理を同期処理のような見た目で書けるようになりました。const res = await fetch(url) という一行の裏で、上のイベントループが動いています。API 呼び出しの結果は多くの場合 JSON で返り、res.json() でオブジェクトに変換して使う——この一連の流れは、フロントエンド開発でもっとも頻出するパターンです。
動的型付けと TypeScript
JavaScriptは動的型付けの言語で、変数の型を宣言せずに書け、"1" + 1 が "11" になるような暗黙の型変換も行われます。この緩さは書き始めの敷居を下げた一方、規模が大きくなると「実行してみるまで間違いに気づけない」問題を生みました。その答えとして広まったのが TypeScript です。JavaScriptに型注釈を加えた言語で、コンパイル時に型の齟齬を検出し、成果物としては素のJavaScriptを出力します。現在の中規模以上の開発では、TypeScriptを採用するのがほぼ標準になっています。
エコシステムとフレームワーク
npm には世界最大級のパッケージ群が登録されており、React・Vue・Svelte などのUIフレームワークが SPA 開発を支えています。さらに Next.js のようなフレームワークは、同じコードベースで SSR や SSG を実現し、「どこでJavaScriptを実行してHTMLを作るか」を選べるようにしました。一方で、依存パッケージの脆弱性やビルドツールの複雑化といった、巨大エコシステムゆえの悩みも抱えています。
実務での落とし穴
JavaScriptには歴史的経緯による罠がいくつかあります。== は型変換を伴う比較なので原則 === を使う、var は関数スコープで巻き上げが起きるため const/let を使う、this の指す先が呼び出し方で変わる、浮動小数点演算で 0.1 + 0.2 !== 0.3 になる——などは定番です。また、ユーザー入力を無防備にDOMへ挿入すると XSS の温床になります。言語の柔軟さと引き換えの注意点として、早い段階で押さえておきたいところです。