1. hosomichi

    Posted

    hosomichi
Changes in title
+jQuery拡張の仕組み 〜 JSおくのほそ道 #013
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,150 @@
+こんにちは、部長です。
+
+今回はjQuery拡張の仕組みについて掘り下げます。
+下記のようなメンバ変数へのアクセスってどのように実現しているのでしょうか?
+
+```js
+jQuery.xxx()
+jQuery('x').xxx()
+```
+
+対象バージョン:jQuery-2.1.1
+[目次はこちら](http://qiita.com/setzz/items/ad9a9c99a4fda041a563)
+
+# jQueryのガチなコア
+
+いきなりですが、部長的には今回の研究の最大の成果です。
+jQueryを本当に骨と皮だけにするとこうなります。ババーン!
+
+```js:ガチコア
+jQuery = function( selector, context ) {
+ return new jQuery.fn.init( selector, context );
+};
+jQuery.fn = jQuery.prototype = {};
+init = jQuery.fn.init = function( selector, context ) {};
+init.prototype = jQuery.fn;
+```
+
+関数やオブジェクトの中身は空にしていますが、これは実際のjQueryソースの構文を抽出したもので、これだけでも問題なく動作します。
+それでは内容をチェックしていきましょう。
+
+## jQuery関数
+ひとつ目の実行文から見ていきます。
+jQuery関数は実行されると、自分が受け取った同じ引数をそのまま`new jQuery.fn.init`に渡して実行させています。
+jQuery.fn.init関数はjQueryインスタンスを生成して返しますので
+
+**「jQuery関数とはjQueryインスタンスを生成する為のコンストラクタである」**
+
+ということが言えると思います。
+
+## init関数
+次に三番目の実行文を見てみます。
+jQuery関数から呼ばれるjQuery.fn.init関数は`init`という変数に代入されます。
+init関数はjQueryインスタンスの内部的なコンストラクタということになります。
+※jQuery関数はさしずめ外向きのコンストラクタといった所でしょうか。
+
+## jQuery.fnオブジェクト
+二番目と四番目の実行文を見ると、二つの文をまたがりながら超重要な代入が行われています。
+二つの文をマージすると下記のようになってます。
+
+```js:超基底オブジェクト
+init.prototype = jQuery.fn = jQuery.prototype = {};
+```
+
+重要なポイントは下記です。
+
+**jQuery.fnというオブジェクトがinit.prototypeに代入されている事。**
+
+つまりは「すべてのjQueryインスタンスはjQuery.fnオブジェクトのメンバを継承する」という事です。
+ちなみに`new jQuery(selector)`とするようなシチュエーションは思いつかなかったので`jQuery.prototype`の部分は余り重要ではないのではないかと思っています。
+
+# jQuery.fnによる拡張を試す
+
+それではjQuery.fnによる機能拡張を実証してみましょう。
+
+![スクリーンショット 2014-06-22 9.54.11.png](https://qiita-image-store.s3.amazonaws.com/0/39330/82563322-2938-3cbf-54e9-1987e84c9a87.png)
+
+問題なく動いてます。
+`jQuery.fn.foo`で関数がセットされ、jQueryインスタンスからの呼び出し`$().foo()`に成功しました。
+
+ちなみにprototype継承を行っているので`$.foo()`はズッコケます。
+この場合はjQueryに直接メンバを突っ込む事で拡張が出来ます。
+
+![スクリーンショット 2014-06-22 9.55.51.png](https://qiita-image-store.s3.amazonaws.com/0/39330/e83856c1-b0f1-9166-6cea-bf7a18e1c81a.png)
+
+# jQuery.fn.extend関数による拡張
+
+さて、jQuery.fnによる機能拡張が出来る事がわかりましたが、
+jQueryは機能拡張について[jQuery.fn.extend](http://api.jquery.com/jQuery.fn.extend/)というAPIを提供しています。
+
+![スクリーンショット 2014-06-22 9.55.51.png](https://qiita-image-store.s3.amazonaws.com/0/39330/d0eb63fd-5b72-1b90-c0b6-a80431202962.png)
+
+
+このようなやり方でもうまく拡張できる事が解ります。
+
+ちなみにjQuery.extendとやる事でjQuery関数のメンバを設定する事も出来ます。
+
+![スクリーンショット 2014-06-22 9.52.10.png](https://qiita-image-store.s3.amazonaws.com/0/39330/7b2cc518-4028-f5ff-95f6-a897ce74790c.png)
+
+# どっちの拡張方式を使うべきか?
+
+さて、2つの拡張方式を見ていきました。
+jQuery.fnに突っ込む方式とjQuery.fn.extend関数を使う方式です。
+どっちを使うべきかと問われれば「どっちでも良いですよ」というのがファイナルアンサーですかね。。
+オブジェクトを設定するときはjQuery.fn.extend関数を使うべきかと思います。
+対して単一の関数を設定するときはjQuery.fnに設定しても良いと思います。
+それでは実際の機能拡張はどのように実装されているのかを見ていきます。
+
+# 実際の拡張実装を見てみる
+## イベントの実装
+
+早速いきましょう。jQueryのイベントの実装は下記のようになってます。
+
+```js:イベントの実装
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+});
+```
+
+イベント関数の定義は、`jQuery.fn`オブジェクトの拡張によって実現していますね。
+
+## Ajaxの実装
+
+続いてAjaxの拡張実装です。
+
+```js:ajaxの実装
+jQuery.extend({
+ /* 設定周りのコード... */
+
+ ajax: function( url, options ) { /*...*/ },
+ getJSON: function( url, data, callback ) { /*...*/ },
+ getScript: function( url, callback ) { /*...*/ }
+});
+```
+
+こちらはインスタンスに依存せず、`$.ajax`などと使用されるのでjQuery.extendを使用しています。
+
+## プラグインの実装
+
+画像をお洒落にサムネイル+拡大表示してくれる[least.js](http://kamilczujowski.github.io/least/)さんを例に見てみます。
+[githubのjsコード](https://github.com/kamilczujowski/least/blob/gh-pages/js/least.js)を拝見いたします。
+
+```js:least.js
+(function($){
+ $.fn.least = function(options) { /.../ };
+})(jQuery);
+```
+
+こちらはjQuery.fnで設定を行っています。
+自分でプラグインを作るときのお作法としてどのやり方で拡張していくのが良いか考えるのも楽しそうです。
+
+今回は以上です。
+jQueryのコアが理解できたので、その先はいろんな機能がそこに拡張されていっている事がイメージ出来るようになりました。
+次回はもう少し具体的な処理に移ろうかと思いますが、まだ思案中です。