jQueryは有害なのか

ずっと「~は有害なのか」という記事を書いてみたかったんです1:D

まず本題に入る前に、1つ言わせてください。jQueryはWeb業界の発展に大いに役立ったと私は考えています。jQueryがあることで、開発者はこれまで想像もできなかったことをできるようになりましたし、そういった機能をブラウザの製作者がネイティブに実装するきっかけにもなりました(もしjQueryが開発されていなければ、今でもdocument.querySelectorAllは存在していなかったでしょう)。jQueryは、今ある便利なツールを使うことができなかったり、IE8やそれ以下の過去の遺産をサポートしなければならない際に今でも必要になってきます。

しかし、そのようなケースはもはや稀なものとなりました。Web開発者の大半は、マーケットシェアの縮小した古いブラウザをサポートする必要はありません。更に、忘れてはならないのは、webの専門家ではない開発者たちのことです。例えば学生や研究員は、古いブラウザはおろか複数のブラウザをサポートする必要すらないことも多いです。しかし、「学界にいるような人々は皆Open Web Platformのモダンな長所を全て使いこなしている」などと思われるかもしれませんが、実際のところ学界ほどjQueryが重宝されている場所を他に知りません。
なぜでしょうか。それは、学生たちが知っているのがjQueryであるというのが1つと、彼らはOpen Web Platformに関するニュースを追っていく時間もなく、興味もないということが言えます。彼らはjQueryが何のために必要なのか理解しないまま、jQueryをとりあえず使っているだけなのです。しかし、jQuery以外でもこれらのことがネイティブに実装できるようになったという事実だけが、私がjQueryを使わない理由ではありません。

そうです、たぶん使う必要はないのでは…?

jQueryを使わなくてもネイティブに実装できると指摘するのは私が初めてではないと思うので、他の人が書いているような内容は書きません。もし詳しい内容を知りたい方がいれば以下の記事をご覧ください。

* jQueryは必要ないはず
* jQueryなんて必要ない!
* 本当にjQueryは必要ですか?
* jQueryなしでJavaScriptを書く10個のヒント
* 他にも関連する多くの記事があります。単に“jQueryは必要ない”とググってみてください。簡単に検索することができるはずです。

今回の記事ではファイルサイズやネイティブメソッドがどれだけ速いのかというようなことに関しても省きます。これらは以前にも議論したことがあるからです。そこで今回は今まであまり語ってこなかったことに焦点を当てたいと思います。

しかし、それがjQueryを使わない一番の理由でもありません。

ネイティブ要素のプロトタイプを拡張しないように、jQueryは自身のwrapperオブジェクトを使用します。過去に行われたネイティブオブジェクトの拡張は大きな間違いでした。その原因は衝突の可能性があることだけではなく、古いIEではメモリリークが起こりうるからです。つまり、 $("div")を実行した時に返ってくるのは要素の参照やNodeListではなくjQueryオブジェクトなのです。これはjQueryオブジェクトが、DOM要素の参照や要素の配列、NodeListのどんな種類とも全く違うメソッドであることを意味します。しかし、これらのようなネイティブオブジェクトは実際のコードの中に頻繁に登場します。jQueryがネイティブオブジェクトを取り除こうとするように、あなたもネイティブオブジェクトと常に格闘しなければなりません。しかも、それがただ、$().にラッピングされているとしてもです。例えば、コールバックがjQueryの.bind()のメソッドで呼び出された時のコンテキストは、HTML要素の参照で、jQueryのコレクションではありません。言うまでもありませんが、複数のソースからコードを使うと、コンテキストはjQueryオブジェクトであったりそうでなかったりします。つまり、できあがったコードはjQueryオブジェクトやネイティブの要素、NodeListsが混ざったものになります。これが悪夢の始まりです。

せめて開発者が、jQueryオブジェクトを含む変数(通常は変数名の頭に$を付けるのが普通だと思います)やネイティブ要素を含む変数に、命名規則に沿った名づけをしていれば、大した問題ではなかったのです(人はこのような決まりに従うのを忘れがちですが、完璧な世界があると仮定しましょう)。しかし、そういった決まりごとは無視されることが多く、結果として不慣れな人には恐ろしく解読しにくいコードができあがります。編集のたびにトライアンドエラーを課されるのです(「あっ、これはjQueryオブジェクトじゃなかった、$()でラップしなきゃ」とか「あっ、これは要素じゃないや、[0]を使わなきゃ」といった事態です)。このような混乱を避けるため、編集を行う開発者たちは身構えて何でもかんでも$()でラッピングするので、コードのいたるところで、同じ変数が複数回、$()にはさまれている状態になってしまいます。同じ理由で、jQueryのリファクタリングを行うのは非常に困難です。そもそもの始まりから閉じ込められていたというわけです。

また、たとえ命名規則に沿った名称がつけられていても、jQueryオブジェクトだけを扱えば済むというわけでもありません。ネイティブDOMメソッドを使ったり、jQueryに依存していないスクリプトから関数を呼び出したりする必要もたびたびあるはずです。すぐにjQueryオブジェクトへの変換やjQueryオブジェクトからの変換は方々に広がり、コードをめちゃめちゃにします。

加えて、記述したコードベースにコードを追加する時、どんなインプットを取得するか分からないために、あらゆる要素やNodeListへの参照も$()でラッピングしてしまうことが多いでしょう。つまり、今閉じ込められているだけではなく、同じコードベースに将来書くコードもまた閉じ込められていくのです。

他人が書いたjQueryに依存するランダムなスクリプトを用意し、jQueryが要らなくなるようにリファクタリングしてみてください。そうすれば必ず、問題の焦点は、ネイティブAPIを使うためにどう機能性を変換するかではなく、悪夢のような事態を理解することなのだ、と気づくでしょう。

JSをむき出しにする実用的な方法

確かに今日、jQueryを要するライブラリはごまんとあります。そして最近私がツイートしたように、jQueryを完全に避けると、デジタル界のベジタリアンになったような気がするかもしれません。しかし、だからと言ってjQueryを使う必要はないのです。jQueryに取って代わるものができれば、ライブラリも差し替えられる可能性が常にあります。

さらに、ほとんどのライブラリは、jQueryが$変数にエイリアスされていなくても動くように書かれています。jQuery.noConflict()を呼び出しさえすれば、$変数はjQueryから解放され、あなたが思うとおりのものに割り当てることが可能になります。たとえば、私はCommand Line APIにならって、下記のようなヘルパー関数をよく定義しています。

// Returns first element that matches CSS selector {expr}.
// Querying can optionally be restricted to {container}’s descendants
function $(expr, container) {
    return typeof expr === "string"? (container || document).querySelector(expr) : expr || null;
}

// Returns all elements that match CSS selector {expr} as an array.
// Querying can optionally be restricted to {container}’s descendants
function $$(expr, container) {
    return [].slice.call((container || document).querySelectorAll(expr));
}

さらに、jQueryを使うたびに$の代わりにjQueryと入力しなければならないので、本当に必要ではないところで無駄に使っているのではないか、と再考させられます。自分が誤っていることもあります。:)
また、jQuery APIを現に気に入っているけれども、コードの膨張は避けたいという場合は、Zeptoの使用を検討してみてください。


  1. タイトルが皮肉なのは冷酷なほど明らかだと思いましたが、でもまあ、そこはインターネット、何も正しいとは言い切れないのです。タイトルは皮肉、でもEricのエッセイ「『〜は有害なのか』批判」を知った上でやっている、ということで。