jQueryセレクタの仕組み 〜 JSおくのほそ道 #011

こんにちは、ほそ道です。
今回からjQueryを操る際には書かせない「`jQuery`もとい`$`変数」にディープに迫ります。

対象バージョン：jQuery-2.1.1
[目次はこちら](http://qiita.com/setzz/items/ad9a9c99a4fda041a563)

# 前提の共有

下記の内容はここから先に進む上での前提とします。

**当ブログ内での便宜的な呼び方**
- `jQuery`でアクセス出来る変数の事を「jQuery関数」と呼びます
- jQuery関数の戻り値を「jQueryインスタンス」と呼びます
- jQueryインスタンス[number]でアクセス出来る要素群を「コレクション」と呼びます。

**予備知識**
- `$`と`jQuery`は同じものです
- jQueryインスタンスはArrayに見えますが独自のObjectです。

# jQuery関数の実行パターン

jQuery関数は第一引数の内容によって、その挙動が大きく変わります。
第一引数ベースで挙動を簡単にまとめました。
第二引数はほぼ省略可能で、ここでは端折ります。詳しく知りたい方は[公式APIリファレンス](http://api.jquery.com/jQuery/)を参照してくださいませ。

|No| 実行パターン | 処理 |
|---|------------|-----|
|1|jQuery( 文字列 )| selector文字列から検索されたDOM要素のコレクションを返す |
|2|jQuery( DOM要素 )| 引数のDOM要素をコレクションに入れて返す |
|3|jQuery( DOM要素配列 )| 引数のDOM要素配列をコレクションに入れて返す |
|4|jQuery( オブジェクト )| 引数のオブジェクトをコレクションに入れて返す |
|5|jQuery( jQueryインスタンス )| 引数のjQueryインスタンスのクローンを返す |
|6|jQuery( 無し )| 空のコレクションを返す |
|7|jQuery( HTML文字列 )| 引数の文字列から生成したDOM要素のコレクションを返す |
|8|jQuery( 関数 )| readyイベントでjQuery関数を引数に取るcallback関数を実行 |

# jQuery関数の挙動(概要編)

では次に、jQuery関数が実行されるとどんな事が起こるんでしょうか。
中では下記のような短いコードが実行されます。

```js:jQuery呼び出しの中
jQuery = function( selector, context ) {
  return new jQuery.fn.init( selector, context );
}
```

- jQuery関数が実行されると内部的に`jQuery.fn.init`関数が呼び出されます。
- jQuery関数からjQuery.fn.init関数へは同じ引数が渡されます。
- jQuery.fn.init関数はnewで呼び出されます。よって関数実行時のthisはjQueryインスタンスです。
- jQuery.fn.init関数の戻り値がjQueryインスタントして呼び出し元に返ります。

それでは`jQuery.fn.init`関数の内側も見ていきます。

# jQuery.fn.init関数の挙動(セレクタ文字列編)

上でまとめた実行パターン上、一番複雑な挙動となる実行ケース１の「セレクタ文字列を渡した場合」についてまとめました。

- セレクタ文字が渡ったときのjQuery.fn.init内部挙動

![スクリーンショット 2014-06-14 21.21.37.png](https://qiita-image-store.s3.amazonaws.com/0/39330/261d48fc-f601-3499-b405-98742fe18c1a.png)

３で分岐が発生していますが、ID検索形式(#xxx)かそれ以外かで分岐します。

## 解説

それでは図の内容を解説します。
第一引数selectorが文字列だった場合、その内容によって大きく下記の３ケースに分かれます。
- ケース１、HTML文字列 -> 実行パターン７：DOM要素生成
- ケース２、ID検索形式(「#elemid」のような形) -> 実行パターン１：セレクタ文字列
- ケース３、それ以外 -> 実行パターン１：セレクタ文字列

### ケース１、HTML文字列の場合
この場合は上でまとめた実行パターン７にあたります。
HTML文字列をDOMとして生成して返しますので、要素検索は行われません。
※このパターンは次回取り上げます。

### ケース２、ID検索形式の場合
ネイティブ関数のgetElementByIdにて要素が検索されます。
検索されるよう素数が１つですのでこうした方が処理コストが良いという訳ですね。
結果はインスタンスの「0」という要素に設定されます。
配列っぽくinstance[0]とアクセスできるはこの為です。

### ケース３、それ以外の場合
jQueryインスタンス.find関数がコールされ、内部で
jQuery.find( selector, context, results )関数による検索処理とthis.pushStack( elems )関数によるインスタンス生成が行われます。
検索処理については別項で述べますがヒットした要素は配列で戻ります。
インスタンス生成はjQuery.merge関数がコールされ、検索結果を新規インスタンスの「0〜n」という要素に割り当てます。(プロパティと呼んでいいのかちょっとアレですが。。)
ID検索形式と同様、配列っぽくinstance[1]とアクセスできるはこの為です。

# セレクタ検索(jQuery.find関数)について
jQuery.findは２ついます。
ひとつは **Sizzle** というオブジェクト。
もうひとつは組み込みの関数です。

ん、何いってんだ？と思いますよね。

ちゃんと説明しますと、前回、githubのjqueryに取り組みましたが、そこのREADMEを読むとGruntでjqueryをビルドする際に`grunt custom:-sizzle`というオプションがありました。

このオプションをつけない状態は **Sizzle** が検索エンジンとなります。
オプションをつけた場合は組み込み関数が検索エンジンとなります。
じつはこのオプションをつけるとjQueryソースの行数は3000行ちょっとまで減らせます。

## Sizzle!?
[Sizzle](http://sizzlejs.com/)は「CSS selector engine」なのです。
SizzleはjQueryからスピンアウトされた独立した仕組みらしいです。
その詳細については[jQuery日本語ライブラリ様](http://semooh.jp/jquery/cont/doc/release_1.3/)がまとめていらっしゃいます。
ほそ道的に興味深いのは選択肢として「使わない」ことも出来るという事です。
ぜひともパフォーマンス検証したいところです。

# まとめ＋ソースコードの場所
いかがでしたでしょうか。
今回はソースコードの追跡結果を備忘録的にまとめました。

githubのjQuery上ではどこに何の処理がはいっているのかをまとめておきます。

|処理|ソースコードの場所|
|---|---|
|jQueryインスタンスの生成元|src/core.js|
|jQuery.fn.init|src/core/init.js|
|jQueryインスタンス.find|src/traversing/findFilter.js|
|jQueryインスタンス.pushStack|src/core.js|
|jQueryインスタンス.merge|src/core.js|
|セレクター(Sizzle指定)|src/selector-sizzle.js|
|セレクター(組み込み関数)|src/selector-native.js|
|Sizzleソース|src/sizzle/dist/sizzle.js|

今回は以上です。
DOM要素の読み取りについては理解が深まりましたので
次回は実行パターン7:[DOM要素の作成](http://qiita.com/setzz/items/dd3b2ae9ff413f3289e8)にフォーカスしてみたいと思います。