なぜエンジニアはjQueryが嫌いなのか?脱却したい人が学ぶべきモダンなJavaScript

jQueryのロゴに赤い禁止マークが重なり、その周りをモダンなJavaScriptのロゴ(ES6, React, TypeScriptなど)が囲むコラージュ画像 フロントエンド

皆さん、こんにちは。
今日は、多くのエンジニアが抱える「jQuery嫌い」という感情の正体と、そこから脱却するために本当に学ぶべきモダンなJavaScriptについて、論理的に整理してみたいと思います。

まず率直な問いかけです。
あなたはjQueryを「便利なライブラリ」として使い続けていませんか?あるいは「もう古い」と言われるたびに、なぜそんなに否定されるのか困惑していませんか?

結論から言えば、エンジニアがjQueryを嫌う理由は、技術的な陳腐化と思想的なミスマッチの2つに集約されます。
技術的には、モダンブラウザが標準APIでjQueryの8割以上の機能をカバーするようになりました。
具体的には、querySelectorAllによるDOM選択、fetchによる非同期通信、classListによるクラス操作、そしてaddEventListenerによるイベント管理――これら全てが素のJavaScriptで軽快に動作します。
jQueryが担っていたクロスブラウザ対応の重責は、もはやBabelやPolyfillが代替し、しかもビルド時に最適化される時代です。

  • 不要な依存性がバンドルサイズを肥大化させる
  • リアクティブなデータバインディングやコンポーネント指向と相性が悪い
  • 過去の互換性コードが可読性と保守性を著しく損なう

これらの理由は、コンピューターサイエンスの観点から見ると「技術負債」そのものです。
特に、jQueryの命令的なDOM操作は、状態管理が複雑化する大規模アプリケーションでは破綻しやすい。
これは、関数型や宣言型のパラダイムが主流となった現在のトレンドと真っ向から対立します。

では、どう脱却するか。
学ぶべきは「フレームワーク」ではなく「言語そのもの」です。
ES6以降のモダンJavaScriptでは、次の概念が必須となります。

カテゴリ モダンな機能 jQueryからの差分
変数管理 const / let varのスコープ問題を解消
関数処理 アロー関数 + 高階関数 $.eachやコールバック地獄からの解放
非同期処理 async/await $.ajaxのチェーンより直感的
モジュール import/export グローバル汚染の防止
DOM操作 テンプレートリテラル + 仮想DOM思想 直接操作から差分更新へ

例えば、$.getJSON(url, callback)は、const response = await fetch(url).then(r => r.json())と書けます。
たったこれだけの差が、エラー処理や並列実行の柔軟性を桁違いに向上させるのです。

嫌われているのはjQueryそのものではなく、「jQuery的に考えること」です。
つまり、DOMに全ての状態を委ねる発想が、テスト困難で予測不能なコードを量産する。
モダンなJavaScriptは、データとUIを分離し、副作用を局所化する設計を推奨します。
まずは、既存のjQueryコードを、純粋関数に置き換える練習から始めてみてください。
そして、ビルドツール(Viteやesbuild)を使い、型情報(TypeScript)を取り入れる。
それだけで、あなたのコードは未来に耐えうるものへと変わります。

さて、それでも「ちょっとした装飾にjQueryが楽」という声は聞こえてきそうです。
ですが、その「ちょっと」が積み重なって技術的負債となる。
エンジニアとしての成長を止めたくないなら、今こそ素のJavaScriptの力と向き合うときです。
脱却は決して難しいものではありません。
なぜなら、あなたがすでに知っているjQueryの「処理の目的」は、モダンな文法でより簡潔に表現できるからです。

最後に一つ。
嫌うことは放棄ではなく、進化の第一歩です。
次の記事では、実際のリファクタリング手順をコード例を交えて解説しますが、まずは今日からdocument.querySelectorを愛用してください。
それだけでも、あなたはもうjQuery脱却者への階段を上り始めています。

はじめに:jQueryは本当に「嫌われ者」なのか?

jQueryのロゴが描かれた画面と、疑問符が浮かぶ開発者の横顔

「jQuery、嫌いです」
そう口にするエンジニアの声を、あなたも一度は耳にしたことがあるのではないでしょうか。
あるいは、あなた自身がそう思っているかもしれません。
しかし、本当にjQueryは嫌われるべき存在なのでしょうか。
それとも、ただの「時代遅れ」というレッテル貼りに過ぎないのでしょうか。

まず、事実を確認しましょう。jQueryは2006年に登場し、ブラウザ間のDOM操作やイベント処理の非互換性を劇的に吸収した革命的なライブラリでした。当時は、getElementByIdすらブラウザによって挙動が異なり、XMLHttpRequestの使い方もまちまち。そんな混沌としたフロントエンド開発に、統一されたインターフェースをもたらしたのです。jQueryは、間違いなくWeb開発の歴史を変えた功績があります。

では、なぜ今「嫌い」と言われるのでしょうか。
その理由を技術的観点から整理すると、以下の3つに収束します。

  • モダンブラウザが標準で同等以上のAPIを実装したため、ライブラリの存在意義が薄れた
  • 大規模アプリケーションにおいて、命令的なDOM操作が状態管理の複雑化を招く
  • バンドルサイズが無視できず、パフォーマンスチューニングの妨げになる

しかし、ここで一つ冷静に考えてみてください。
これらの理由は、jQueryそのものが「悪い」のではなく、利用する文脈が変わったという事実を指し示しています。
つまり、jQueryは静的サイトや小規模なインタラクションには今でも有効な選択肢です。
問題は、複雑な状態を持つシングルページアプリケーションや、長期間保守されるプロジェクトでjQueryを使い続けることにあるのです。

コンピューターサイエンスの観点から言えば、これは「トレードオフ」の判断ミスです。
適切なツールを適切な規模と寿命に合わせて選択できていない。
それだけの話であって、jQuery自体に罪はありません。
にもかかわらず、エンジニアが「jQuery嫌い」と公言するのは、自分が過去に書いたjQueryコードへの後悔や、新しいパラダイムへの過剰適合が心理的に作用している場合も少なくありません。

とはいえ、現実として、多くの求人要件やOSSプロジェクトではjQueryは「レガシー」扱いです。
そして、あなたがキャリアを中長期的に考えているなら、jQueryに依存し続けることは戦略的に不利でしょう。
重要なのは、嫌うことではなく、なぜ嫌われるのかを論理的に理解し、その上で脱却する道を選ぶことです。

本記事では、感情論ではなく、JavaScript言語仕様とブラウザAPIの進化を軸に、jQueryからの脱却を実践的に考えていきます。
まずは、次の章でjQueryが生まれた歴史的必然性を振り返り、その上で「いつ、なぜ、どうやって手放すべきか」を明らかにします。

さて、ここで一つ確認です。
あなたは「jQueryが嫌い」という言葉に、どんなニュアンスを感じますか?単なる趣味の話か、それとも技術的な正当性があるのか。
この問いに対する答えは、あなたがこれから学ぶモダンJavaScriptの理解度によって大きく変わるはずです。
まずは、その出発点として、一度jQueryの功績を素直に認めることから始めましょう。
否定は建設的な進化の第一歩ではありません。
しかし、適切な距離感を持って見つめ直すことは、エンジニアとしての成熟を示す証でもあります。

jQueryが生まれた背景と、あの時代の「救世主」だった理由

2000年代半ばのブラウザ戦争時代を象徴する、互換性対応の複雑さを示す図

今でこそ「レガシー」の代名詞のように語られるjQueryですが、その登場は2006年。
当時のWeb開発を取り巻く環境は、今とは比較にならないほど混沌としていました。
この章では、jQueryがなぜ生まれ、なぜこれほどまでに普及したのかを、ブラウザ戦争と標準化の歴史を交えながら振り返ります。

まず、皆さんが知っている現在のブラウザ事情を思い浮かべてください。
Chrome、Firefox、Safari、Edge――これらはほぼ同じタイミングで最新のECMAScript仕様をサポートし、W3Cの勧告に従ったDOM APIを実装しています。
しかし、2005年頃は全く別世界でした。
Internet Explorer 6が依然としてシェアの過半数を占め、Firefox 1.5がようやく台頭し始めた頃。
さらに、SafariはWindows版が存在せず、Operaは独自エンジンを採用。
ブラウザごとにJavaScriptの実装が異なるのは当たり前で、同じgetElementById一つ取っても、IEでは大文字小文字を区別しないといった非互換が山積みでした。

こうした状況下で、開発者は次のような苦労を強いられていました。

  • XMLHttpRequestの作成方法がIEとその他で異なる(ActiveXObject vs new XMLHttpRequest())
  • addEventListenerがIEではattachEventという別名でしか使えない
  • innerHTMLの挙動や、styleオブジェクトのプロパティ名がブラウザ間でバラバラ
  • eventオブジェクトのプロパティ(target vs srcElement)や、デフォルト動作の抑止方法(preventDefault vs returnValue = false)が統一されていない

これらは一部の例に過ぎません。
実際の開発では、ブラウザ判定のif文がコードの3割を占めるような状況も珍しくなく、本来のロジックよりも互換性対応にリソースを割くことが常態化していました。

そこで登場したのがjQueryです。
ジョン・レシグが生み出したこのライブラリは、「Write Less, Do More」というキャッチフレーズの通り、圧倒的に簡潔な構文でクロスブラウザ対応を実現しました。
$()というセレクタエンジンは、CSSセレクタをそのまま使える画期的なもので、document.querySelectorAllが標準化されるはるか前から、開発者に直感的なDOMアクセスを提供しました。

さらに、jQueryはチェーン構文を積極的に採用しました。
$('#foo').css('color', 'red').fadeIn(200).append('<p>Hi</p>')のように、メソッドを連続して記述できるスタイルは、当時のコード量を劇的に削減しました。
これは単なるシンタックスシュガーではなく、メソッドが常にjQueryオブジェクトを返すというデザインパターンに基づいており、関数型プログラミングの考え方を取り入れた先進的なものでした。

また、jQueryの成功を語る上で外せないのがプラグインエコシステムの存在です。
jQuery UIやjQuery Mobileをはじめ、数千ものプラグインが公開され、開発者は複雑なUIコンポーネントを数行のコードで導入できました。
これは、当時のReactやVueに相当する「デファクトスタンダードのエコシステム」を形成したという点で、技術史的に極めて重要な出来事です。

では、具体的にどのようなコードがjQueryによって簡略化されたのか。
例えば、Ajax通信一つを取っても、IEと標準ブラウザで分岐するコードが数行必要だったものが、$.ajax()という統一インターフェースで済むようになりました。
しかも、JSONPやクロスドメイン対応まで暗黙に処理してくれる。
この抽象化のレベルは、当時の他のライブラリ(Prototype.jsやMooTools)と比較しても一歩抜きんでていました。

もう一つ、見落とせないのが学習コストの低さです。
jQueryは「CSSを知っていれば使える」という設計思想を持ち、セレクタやメソッド名も直感的に理解できるよう配慮されていました。
これにより、バックエンドエンジニアやデザイナーでも容易にフロントエンドのインタラクションを実装できるようになり、Web開発の裾野を大きく広げたのです。

こうした背景を踏まえると、jQueryは単なるライブラリではなく、Web標準が未熟だった時代の「実用的な中間層」 だったと評価できます。
標準化団体が議論している間に、現場の課題を解決するという、実践的なエンジニアリングの結晶でした。

しかし、ここで一つ考えてみてください。
もしjQueryが存在しなければ、モダンなブラウザAPIはこれほど早く標準化されたでしょうか。
実際、querySelectorAllやclassList、fetchといったAPIは、jQueryの人気がブラウザベンダーに「開発者が何を求めているか」を強く意識させた結果として生まれた側面があります。
つまり、jQueryは標準化の触媒としても機能したのです。

このように、jQueryは「救世主」という表現が決して誇張ではない存在でした。
しかし、どんな優れたソリューションにも有効期限があります。
次の章では、その有効期限がなぜ訪れたのかを、ブラウザAPIの進化という観点から厳密に検証していきます。

モダンブラウザが標準搭載したAPIでjQueryの役割はほぼ終わった

最新ブラウザのロゴ群と、querySelectorやfetchなどの標準APIアイコンが並ぶインフォグラフィック

前章では、jQueryが歴史的に果たした役割を称賛しました。
しかし、今この瞬間、あなたが使い慣れたjQueryのメソッドのほとんどは、標準ブラウザAPIで数行のコードに置き換えられます。
しかも、追加のライブラリを読み込むことなく、ネイティブのJavaScriptエンジンによって最適化された速度で動作します。
この章では、具体的にどのAPIがjQueryの機能を代替しているのかを、実コードを交えて示します。

まず、最も基本的なDOM選択から見ていきましょう。
jQueryでは$('.item')と書いていた部分は、document.querySelectorAll('.item')で完全に置き換え可能です。
このメソッドはCSSセレクタをそのまま受け取り、NodeListを返します。
さらに、forEachを使えばjQueryのeachと同様の反復処理が可能です。
一つだけ注意点があります。
querySelectorAllが返すNodeListはライブコレクションではないため、DOMの変更を自動追跡しません。
これはむしろパフォーマンス面で好ましい特性です。

次に、クラス操作です。
$('#foo').addClass('active')は、document.getElementById('foo').classList.add('active')に。
toggleClassはclassList.toggle()に。
hasClassはclassList.contains()に対応します。
classListはIE10以降でサポートされているため、現代のプロジェクトでは互換性問題はほぼありません。

属性操作も同様です。
$('#foo').attr('data-id', 123)は、document.querySelector('#foo').setAttribute('data-id', '123')。
propによるプロパティ操作は、直接element.checked = trueやelement.value = 'text'と書けます。
jQueryが暗黙に型変換していた部分を、開発者が明示的に扱う必要があるだけです。

イベント管理においても、$('#foo').on('click', handler)はelement.addEventListener('click', handler)で十分です。
addEventListenerは第三引数でonceやpassiveオプションも指定可能であり、jQueryのoneやpreventDefaultの挙動をより細かく制御できます。
イベントデリゲーションも、event.targetをチェックする条件分岐を書くことで実現できます。
これは一見冗長に見えますが、逆にロジックが明確になり、デバッグ時に何が起こっているかが追いやすくなります。

では、非同期通信はどうでしょうか。
$.ajaxや$.getJSONの代替として、標準のfetch APIが登場しました。
fetchはPromiseベースであり、async/awaitと組み合わせることで、コールバック地獄を完全に解消します。

// jQuery
$.getJSON('/api/users', function(data) {
  console.log(data);
});

// モダンJavaScript
const response = await fetch('/api/users');
const data = await response.json();
console.log(data);

この差はわずか数行ですが、エラーハンドリングの柔軟性や、Promise.allによる並列処理との親和性は桁違いです。
さらに、fetchはストリーミングやキャッシュ制御など、HTTPの低レベルな制御も可能です。

ここで、jQueryが現在でもカバーするが、標準APIでは複数行を要する機能としてアニメーションが挙げられます。
fadeInやslideUpは、CSS TransitionsやWeb Animations APIで代替できますが、古いブラウザ対応や複雑なタイミング制御にはややコード量が増えます。
しかし、それはアニメーションをライブラリに任せるか、CSSに委譲するかの設計判断の問題であり、核心的なDOM操作とは切り分けられます。

表にまとめると、以下のようになります。

jQueryの機能 標準APIでの代替 サポート開始ブラウザ
セレクタ操作 querySelector / querySelectorAll IE8(部分)、IE9以降完全
クラス操作 classList メソッド群 IE10以降
属性操作 getAttribute / setAttribute IE6以降(完全対応)
イベント登録 addEventListener IE9以降
非同期通信 fetch API IE除く全モダンブラウザ
DOM生成 テンプレートリテラル+insertAdjacentHTML IE9以降

この表から明らかなように、IE11をサポート対象から外せるプロジェクトなら、jQueryが担っていた機能の95%以上が標準APIで代替可能です。
しかも、標準APIはブラウザエンジンが直接実装しているため、jQueryを経由するよりオーバーヘッドが少なく、バンドルサイズもゼロになります。

もちろん、$.extendのようなユーティリティや、$.Deferredのような独自の非同期パターンもありますが、これらはES6のObject.assignやネイティブのPromiseで置き換えられます。
つまり、jQueryは「互換性レイヤー」としての役割を終え、今や「シンタックスシュガーの寄せ集め」に近い状態なのです。

では、なぜまだjQueryが使われているのでしょうか。
それは、既存のコードベースの継承や、開発者の慣習、そして「動けばいい」という考え方によるところが大きい。
しかし、新規プロジェクトにおいてjQueryを選択することは、未来への負債を意図的に積むこととほぼ同義です。
次の章では、エンジニアがそれを嫌う具体的な3つの合理的理由を掘り下げます。
感情ではなく、数字と設計原則に基づいた判断をしていきましょう。

エンジニアがjQueryを嫌う3つの合理的な理由

嫌悪の理由を3つのアイコン(バンドルサイズ、命令的コード、コンポーネント非互換)で表現した図

前章までで、jQueryが標準APIで代替可能であることを示しました。
しかし、代替可能性と「嫌われる理由」は別の話です。
ここでは、エンジニアがjQueryを忌避する合理的な根拠を、パフォーマンス、保守性、アーキテクチャの3軸から論理的に分解します。
いずれも感情論ではなく、ソフトウェア工学の原則に基づいた判断です。

理由1:バンドルサイズとパフォーマンスへの悪影響

まず、最も定量的な指標であるファイルサイズから見ていきましょう。
jQueryの最新版(3.x)はminify+gzip圧縮後でも約30KB程度あります。
これは一見小さく見えますが、モバイル回線や低スペック端末では無視できないコストです。
特に、初回読み込み時にこの30KBがクリティカルレンダリングパスをブロックする可能性があり、Core Web VitalsのLCP(Largest Contentful Paint)に悪影響を及ぼします。

さらに問題なのは、jQueryが提供する全機能のうち、実際のプロジェクトで使われるのはせいぜい20〜30%であるという事実です。
アニメーションも使わなければ、Ajaxも使わない。
それでもライブラリ全体を読み込む必要がある。
これは、必要以上のコードをユーザーに強制するという、パフォーマンス上の非効率を意味します。

バンドルサイズの観点で言えば、モダンなビルドツール(WebpackやVite)はTree Shakingをサポートしており、未使用のコードを自動除去します。
しかし、jQueryは単一のオブジェクトとして設計されているため、Tree Shakingの効果がほとんど期待できません。
結果として、jQueryを使い続ける限り、最適化の余地を自ら放棄していることになります。

実行速度の面でも、ネイティブAPIに対してjQueryは常にオーバーヘッドを伴います。
$()は単なるラッパー関数であり、内部的に複数の条件分岐や正規表現処理を経由します。
querySelectorAllがブラウザエンジンでC++実装されているのに対し、jQueryはJavaScriptのレイヤーで同じ処理を再実装しているため、純粋な速度競争では勝ち目がありません。
実際、ベンチマークではDOM選択で約2〜5倍の差が生じるケースも報告されています。

理由2:命令的DOM操作が保守性とテスト容易性を損なう

2つ目の理由は、より本質的です。
jQueryのコードは典型的に「命令的」です。
つまり、「何を」ではなく「どうやって」 を記述することに焦点が当たります。
例えば、$('#foo').text('bar').css('color', 'red')という1行は、「要素を取得し、テキストを変更し、色を変える」という手順を逐次的に指示しています。
この書き方は小規模なスクリプトでは分かりやすいのですが、アプリケーションが成長するにつれて致命的な弱点となります。

なぜなら、命令的コードは状態をDOMに直接保持するからです。
あるボタンがクリックされたとき、どの要素が表示され、どのクラスが付与されているか――それらはJavaScriptの変数ではなく、ブラウザのDOMツリーの中に散在しています。
この設計では、アプリケーションの状態を一元的に把握することが難しく、バグが発生したときに「いつ、どこで、なぜ」その状態になったのかを追跡するのが極めて困難になります。

テストの観点でも、jQueryコードはシビアです。
DOMに依存する処理をユニットテストしようとすると、jsdomやHappy DOMなどの擬似DOM環境が必要になり、セットアップが複雑化します。
また、jQueryのメソッドは副作用を暗黙に持つため、純粋関数のように入出力だけを検証するテストが書きにくい。
結果として、テストカバレッジが低下し、リファクタリングへの心理的障壁が高まります。

保守性においても、命令的コードは「変更に弱い」という特性があります。
デザインの微調整でクラス名や構造が変わると、それに依存するjQueryセレクタを全て見直さなければならない。
この依存関係の網の目が、長期メンテナンスコストを指数関数的に増加させます。

理由3:コンポーネント指向やリアクティブ思想と根本的に相性が悪い

そして最後に、現代のフロントエンド開発の主流であるコンポーネント指向およびリアクティブプログラミングとのミスマッチです。
React、Vue、Svelteといったフレームワークは、UIを状態の関数として宣言的に記述することを基本とします。
データが変われば、自動的にビューが再描画される。
開発者はDOMの更新タイミングを意識する必要がありません。

しかし、jQueryはこのモデルと完全に相反します。
jQueryは「DOMを直接操作する」というレイヤーで動作するため、フレームワークが管理する仮想DOMやリアクティブシステムと競合します。
jQueryでDOMを変更すると、フレームワークの状態と実際のDOMが乖離し、予期せぬバグや無限レンダリングループを引き起こすことが知られています。

さらに、コンポーネント設計では、各コンポーネントが自身のDOMと状態をカプセル化することが推奨されます。
しかし、jQueryはグローバルなセレクタでDOMにアクセスするため、カプセル化を簡単に破壊します。
あるコンポーネントが別のコンポーネントの内部要素を$('.child .button')で変更してしまう。
そうなれば、コンポーネント間の依存関係が密になり、修正の影響範囲が予測不可能になります。

このように、jQueryは「ページ」単位の開発には適していたが、「アプリケーション」単位の開発には適さないのです。
モダンなフロントエンドは後者であり、その文脈でjQueryは明らかに異物です。
エンジニアが嫌うのは、このアーキテクチャ上の不整合であって、文法や記法の好みではないという点を、ぜひ理解しておいてください。

次の章では、この3つの理由を踏まえた上で、脱却のために具体的に何を学ぶべきかを、言語仕様レベルで解説していきます。

脱却のために最初に学ぶべきES6以降の核心文法

const, let, アロー関数, async/await, モジュール構文が書かれたJavaScriptコードエディタのクローズアップ

前章までで、jQueryがなぜモダンな開発にそぐわないかを論理的に説明しました。
では、具体的に何を学べば脱却できるのか。
ここでは、ES6(ECMAScript 2015)以降で導入された3つの言語機能に焦点を当てます。
これらはjQueryからの移行において、最も即効性があり、かつ設計思想の転換を促す核心的な要素です。
いずれもブラウザサポートは成熟しており、トランスパイラなしでもほとんどのモダン環境で動作します。

変数宣言はvarを捨て、constとletでスコープを制御する

まず、最も基本的でありながら影響範囲が広いのが変数宣言の変更です。
jQueryのコードではvarが多用されますが、varには関数スコープという独特な挙動があり、これがバグの温床となります。
例えば、forループ内で宣言した変数がループ外でも参照できたり、同じスコープ内で再宣言が許されたりする。
これらは意図しない値の上書きや、クロージャとの組み合わせで予期せぬ動作を引き起こします。

ES6では、letとconstが導入され、ブロックスコープが言語仕様として確立されました。
letは再代入可能な変数、constは再代入不可な定数を宣言します。
constは一度代入した値を変更できないだけで、オブジェクトや配列のプロパティ変更は許可される点に注意してください。
この区別により、「この変数は変更されるかどうか」がコード上で明示的になり、可読性と保守性が飛躍的に向上します。

実践的なアドバイスとして、原則としてconstをデフォルトにし、どうしても再代入が必要な場合のみletを使うことを推奨します。
varはもはや使う理由がありません。
このルールを守るだけで、スコープ起因のバグが劇的に減り、コードの意図が明確になります。

アロー関数と高階関数でコールバック地獄から卒業する

次に、関数定義の新しい構文であるアロー関数(=>)です。
これは単なる省略記法ではなく、thisの束縛がレキシカル(静的)であるという根本的な違いを持ちます。
jQueryでは$(this)を使うために、従来のfunction()で書く必要がありましたが、アロー関数では外側のthisをそのまま引き継ぐため、self = thisといった回避策が不要になります。

さらに、アロー関数は高階関数との相性が抜群です。
Array.prototype.map、filter、reduceといったメソッドと組み合わせると、ループ処理が宣言的かつ簡潔に記述できます。

// jQuery的発想(命令的)
var items = [];
$('.list li').each(function() {
  items.push($(this).text());
});

// モダンな書き方(宣言的)
const items = [...document.querySelectorAll('.list li')]
  .map(li => li.textContent);

後者のコードは、「何を」変換するかに焦点が当たっており、中間変数やループ制御が消失しています。
このスタイルはテストもしやすく、バグも入り込みにくい。
コールバック地獄と呼ばれるネスト構造も、アロー関数と高階関数を適切に使えばフラットに保てます。

async/awaitで非同期処理を同期的に読めるコードに変える

そして、jQuery脱却において最も劇的な変革をもたらすのが、非同期処理の記法です。
jQueryでは$.ajaxのコールバックや$.Deferredのチェーンを使っていましたが、これらは処理フローが縦方向に伸び、エラーハンドリングが分散するという課題がありました。

ES2017で導入されたasync/awaitは、非同期処理をあたかも同期的なコードのように書ける構文です。
async関数内でawaitを使うと、Promiseが解決されるまでその行で処理が一時停止し、解決値が返ります。
エラーはtry/catchで一元捕捉できるため、従来の.catchチェーンより直感的です。

// jQueryのDeferredチェーン
$.get('/api/user')
  .then(user => $.get('/api/posts/' + user.id))
  .then(posts => console.log(posts))
  .catch(err => console.error(err));

// async/await版
async function fetchUserPosts() {
  try {
    const user = await fetch('/api/user').then(r => r.json());
    const posts = await fetch('/api/posts/' + user.id).then(r => r.json());
    console.log(posts);
  } catch (err) {
    console.error(err);
  }
}

この例では、fetchが標準APIであることにも注目してください。
async/awaitはPromiseとセットで使うことで真価を発揮しますが、jQueryのDeferredもPromise互換であるため、段階的な移行も可能です。
まずは$.ajaxをfetchに置き換え、その上でasync/awaitを導入する。
このステップバイステップのアプローチが、混乱を最小にします。

以上の3つ――const/let、アロー関数、async/await――は、モダンJavaScriptの入り口にして核心です。
これらを体得するだけで、あなたのコードはjQueryに依存しない形へと大きく舵を切れます。
次の章では、さらに具体的なDOM操作の実践テクニックを見ていきましょう。

DOM操作とイベント管理をjQuery抜きで書く実践テクニック

addEventListenerとclassListを使ったモダンなDOM操作コードのスクリーンショット

文法の基礎を押さえたところで、実際のDOM操作とイベント管理に移りましょう。
ここがjQuery依存からの脱却において、最も具体的で即効性のある分野です。
多くのエンジニアが「jQueryがないとDOM操作が書けない」と思い込んでいますが、それは全くの誤解です。
標準APIは十分に強力であり、むしろ細かい制御が可能であることを、実践的なコードで示していきます。

querySelectorAllとforEachでセレクタ処理を置き換える

jQueryの最大の魅力であったセレクタエンジンは、今やdocument.querySelectorAllが完全に代替します。
このメソッドはCSSセレクタをそのまま受け取り、一致するすべての要素をNodeListとして返します。
NodeListは配列ではありませんが、forEachメソッドをサポートしているため、jQueryのeachとほぼ同じ感覚で使えます。

具体的な置き換えパターンを見てみましょう。
jQueryでは$('.item').addClass('highlight')と書いていた処理は、標準APIでは次のようになります。

document.querySelectorAll('.item').forEach(el => {
  el.classList.add('highlight');
});

これは一見するとjQueryより冗長に見えるかもしれません。
しかし、この冗長性こそが明示性であり、何が起きているかを正確に理解できるという利点があります。
さらに、querySelectorAllは複雑なセレクタにも対応しており、div > p:first-childや[data-type="active"]といった記述も問題なく動作します。

また、NodeListをスプレッド構文で配列に変換すれば、mapやfilterなどの高階関数も利用可能です。
これにより、DOM要素のコレクションに対する変換処理が、宣言的かつ関数型的に書けるようになります。

const texts = [...document.querySelectorAll('.item')]
  .filter(el => el.dataset.active === 'true')
  .map(el => el.textContent);

jQueryの$.mapや$.grepに相当する処理が、標準の配列メソッドで完結するわけです。
覚えるべき独自APIが減り、汎用的なJavaScriptスキルがそのまま応用できる。
これがモダンなアプローチの大きなメリットです。

一点、注意すべきはquerySelectorAllが静的なコレクションを返すことです。
jQueryのセレクタは動的でしたが、標準APIではDOMが変更されてもコレクションは自動更新されません。
これは意図しない動作を防ぐ意味ではむしろ好ましい特性であり、必要な場合は再度クエリを実行すれば良いだけです。

イベントデリゲーションはクロージャと条件分岐で実装する

次に、イベント管理です。
jQueryのonメソッドは、イベントデリゲーションも簡潔に記述できることで知られています。
$(document).on('click', '.button', handler)という書き方は、動的に追加される要素にも対応できる強力なパターンでした。
標準APIでは、これをaddEventListenerと条件分岐で再現します。

基本形は以下の通りです。

document.addEventListener('click', function(event) {
  const target = event.target.closest('.button');
  if (target) {
    // ボタンがクリックされたときの処理
    console.log('Button clicked:', target);
  }
});

closestメソッドは、自身または親要素の中でセレクタに一致する最も近い要素を返します。
これにより、デリゲーション先の特定が正確かつ安全に行えます。
また、event.targetとevent.currentTargetの違いを理解しておくことが重要です。
currentTargetはイベントリスナを設定した要素(この場合はdocument)を指し、targetは実際にクリックされた要素を指します。

このパターンの利点は、ロジックが完全にプレーンなJavaScriptで完結することです。
jQueryのマジックのような暗黙的な挙動がなく、コードを読んだだけで何が起こるかが明確です。
さらに、条件分岐を拡張すれば、複数の種類の要素に対するデリゲーションも一つのリスナで管理できます。

document.addEventListener('click', function(event) {
  const deleteBtn = event.target.closest('.delete');
  if (deleteBtn) {
    // 削除処理
    return;
  }
  const editBtn = event.target.closest('.edit');
  if (editBtn) {
    // 編集処理
    return;
  }
});

このように書けば、イベントリスナの数が増えすぎず、パフォーマンス面でも優れています。
また、addEventListenerの第三引数で{ passive: true }を指定すれば、スクロール系イベントでのパフォーマンス改善も図れます。
これはjQueryでは容易に実現できなかった細かなチューニングです。

もう一つ、イベントの解除についても触れておきます。
jQueryのoffに対応するのはremoveEventListenerですが、解除するには同じ関数参照を渡す必要があるため、名前付き関数か変数に関数を代入しておくことが必須です。
無名関数では解除できない点に注意してください。

以上のテクニックを組み合わせれば、jQueryを使わずとも、あらゆるDOM操作とイベント管理が十分に実装可能です。
初期の慣れは少し手間に感じるかもしれませんが、標準APIに慣れるほど、コードの予測可能性とデバッグ容易性が向上することを実感できるでしょう。
次の章では、フレームワークに飛びつく前に鍛えるべき設計力について考察します。

フレームワークに飛びつく前に:素のJavaScriptで設計力を鍛える

フレームワークのロゴ群の背後に、素のJavaScriptの本が置かれている写真

ここまで、jQueryからの脱却に必要な具体的な文法とDOM操作テクニックを見てきました。
しかし、多くのエンジニアが次のステップとしてReactやVueといったフレームワークにすぐに飛びつく傾向があります。
私はあえて警鐘を鳴らします。
フレームワークは銀の弾丸ではなく、むしろ設計力が不足した状態で使うと、複雑性を増幅させるだけです。
まずは素のJavaScriptで、アプリケーションの設計原則を身につけることを強く推奨します。
本章では、そのための二つの核心的な考え方を解説します。

データとUIを分離するMVC的思考をJavaScriptで実践する

jQueryコードの最大の問題点は、データとUIがDOMという一つの媒体に混在していることでした。
状態はDOMのクラスやテキストとして保存され、その状態を変更するロジックも同じDOM操作の中に埋め込まれている。
これでは、変更の影響範囲が局所化できず、バグ修正が連鎖的な修正を呼ぶという悪循環に陥ります。

この問題に対する古典的かつ有効な解決策が、MVC(Model-View-Controller)あるいはその派生パターンです。
難しく考える必要はありません。
まず、データ(Model)はプレーンなJavaScriptオブジェクトや配列として保持し、UI(View)はそのデータをレンダリングする関数として定義します。
Controllerはユーザー入力を受け取り、Modelを更新し、Viewを再描画する。
この循環を明確に分離するだけで、コードの見通しが劇的に変わります。

具体的な例を示しましょう。
jQueryでは、カウンターアプリケーションで$('#count').text(counter)のように直接DOMを更新していました。
これに対し、MVC的アプローチでは以下のように設計します。

// Model:状態を保持
let state = { count: 0 };

// View:状態を受け取りDOMを生成(純粋な関数)
function render(state) {
  const app = document.getElementById('app');
  app.innerHTML = `
    <p>カウント: ${state.count}</p>
    <button id="increment">+1</button>
  `;
}

// Controller:イベントを処理しModelを更新
function handleIncrement() {
  state = { ...state, count: state.count + 1 };
  render(state);
}

このコードでは、ViewはstateからDOM文字列を生成するだけで、DOM操作は一切行いません。
Controllerは状態を変更するだけであり、UIの詳細には関知しません。
この分離により、レンダリングロジックをテストするにはstateを渡して出力文字列を検証すればよく、DOM環境は不要です。
また、状態変更の履歴を記録したり、アンドゥ機能を追加したりすることも容易になります。

このパターンの重要性は、小規模なアプリでは過剰に感じられるかもしれません。
しかし、機能が増えるにつれて、データの流れが一方向に定まることの恩恵は計り知れません。
フレームワークはこの思想を強力にサポートする道具であり、その前に素のJavaScriptでこの思想を体感しておくことが、フレームワーク習得の近道でもあります。

純粋関数と副作用の分離でテストしやすいコードを書く

もう一つ、設計力を鍛える上で欠かせない概念が純粋関数と副作用の分離です。
純粋関数とは、同じ入力に対して常に同じ出力を返し、外部の状態を一切変更しない関数です。
対照的に、DOM操作やAPI呼び出し、コンソールへの出力などは副作用と呼ばれ、テストやデバッグを困難にします。

jQueryコードは本質的に副作用だらけです。
$('#foo').text('bar')という一行は、画面表示を変更するという副作用のみを持ち、何も返しません。
このようなコードは単体テストが事実上不可能であり、統合テストに頼らざるを得なくなります。

そこで、副作用を境界に押し出す設計を心がけます。
つまり、アプリケーションのコアロジックは純粋関数として実装し、副作用(DOM操作やfetch)はその外側で一回だけ実行する。
先ほどのMVC例では、render関数はinnerHTMLへの代入という副作用を持ちますが、それ以外の部分は純粋です。
このように、副作用を可能な限り関数の末端に集約することで、テスト対象となる核心部分を分離できます。

// 純粋関数:計算のみ
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// 副作用を含む関数:入出力を担当
async function displayTotal() {
  const items = await fetchItems(); // 副作用
  const total = calculateTotal(items); // 純粋
  document.getElementById('total').textContent = total; // 副作用
}

この設計では、calculateTotalはDOMやネットワークに依存しないため、Jestなどのテストランナーで即座に検証できます。
テストが書きやすいコードは、自然と保守もしやすいというのが、ソフトウェア工学の長年の知見です。

最終的に、フレームワークに頼る前にこの二つの原則――データとUIの分離、純粋関数と副作用の分離――を身につけることが、jQuery脱却後の真の成長につながります。
フレームワークはこれらを強制する道具に過ぎず、本質はあくまで設計思想にあります。
次の最終章では、これまでの内容を総括し、あなたが次に踏み出すべき具体的なステップを提示します。

まとめ:jQuery嫌いは成長の証。次に踏み出すべきステップとは

jQueryからモダンJavaScriptへの移行ロードマップが描かれた白板

ここまで長きにわたり、jQueryの歴史的功績、標準APIによる代替可能性、エンジニアが嫌う合理的理由、そして脱却のために学ぶべきモダンJavaScriptの核心と設計原則を解説してきました。
最終章である本章では、これらを総括した上で、あなたが明日から実際に踏み出すべき具体的なステップを、ロードマップとして提示します。

まず、本記事の主張を一言で要約しましょう。
jQueryは過去の偉大な解決策であり、現在の標準的な解決策ではない。
この事実を受け入れることが、全ての出発点です。
そして、その事実に気づき、違和感を持ち、改善を求める態度こそが、エンジニアとしての成長の証です。
「jQueryが嫌い」という感情は、しばしば「自分がより良い方法を求めている」という前向きなシグナルとして読み解くべきでしょう。

では、具体的に何をすれば良いのか。
私は以下の5段階のステップを推奨します。

  • ステップ1:既存のjQueryコードを標準APIに置き換える練習を、個人プロジェクトや趣味のスクリプトで始める。最初は$()をquerySelectorに、$.ajaxをfetchに変えるだけでも十分です
  • ステップ2:ES6文法を全ての新規コードで採用する。varを禁止し、アロー関数とasync/awaitを積極的に使う。これにより、コードの見た目と思考が自然とモダンへとシフトします
  • ステップ3:ビルドツール(Viteやesbuild) を導入し、モジュールバンドルとトランスパイルの仕組みを体験する。これにより、import/exportやnpmパッケージの活用が現実的になります
  • ステップ4:TypeScriptを導入する。型システムは大規模開発の生産性と安全性を飛躍的に高め、jQuery時代の「動的に何でも動く」という悪習を根本から断ち切ります
  • ステップ5:フレームワーク(React、Vue、Svelteのいずれか) を学習する。ただし、これはステップ1〜4を経てからにしてください。素のJavaScriptでの設計感覚がないままフレームワークに飛び込むと、単なる「jQueryの置き換え」に終わり、真の恩恵を得られません

これらのステップは、決して短期間で完了するものではありません。
しかし、一つずつ確実に積み上げることで、あなたのスキルセットは根本から進化します。
重要なのは、完璧を目指さないことです。
最初は標準APIで書くコードが冗長に感じられ、アロー関数のthisに戸惑うかもしれません。
それで構いません。
慣れと理解は実践を通じてしか得られません。

また、チームで開発している場合は、段階的な移行戦略を立てることをお勧めします。
既存のjQueryコードを一括置換するのは現実的ではありません。
新しい機能やコンポーネントからモダンな書き方を導入し、徐々にレガシーコードの割合を減らしていく。
この漸進的アプローチが、リスクを最小化しつつ、チーム全体のスキルアップも促進します。

最後に、一つだけ忘れないでほしいことがあります。
技術の移り変わりは常に必然的であり、それを恐れる必要はありません。
あなたが今jQueryに感じている違和感は、5年前にFlashに、10年前にIE6に感じていたものと同じです。
そして、今あなたが学ぼうとしているモダンJavaScriptも、いずれは次の何かに取って代わられるでしょう。
しかし、その時に重要なのは「特定のライブラリを知っていること」ではなく、「言語仕様と設計原則を理解していること」です。
本記事で強調してきたconst/let、アロー関数、async/await、MVC的思考、純粋関数の分離――これらはjQuery脱却のためだけでなく、これからのどのフレームワークやライブラリが登場しても通用する普遍的スキルです。

ですから、jQueryを「嫌う」のではなく、「適切に距離を置く」ことを学びましょう。
そして、その距離の先にある広大なJavaScriptエコシステムを、恐れずに探索してください。
あなたの最初の一歩は、document.querySelectorを今日のコードに書き加えることから始まります。
その一行が、未来のあなたを変える第一歩だと信じて。

本記事が、あなたの技術的成長の一助となれば幸いです。
次なるステップでお会いしましょう。

コメント

タイトルとURLをコピーしました