jQueryのvar jQueryの仕組みについて

このQ&Aのポイント
  • jQueryのvar jQueryの仕組みについて分からない
  • jQuery-1.6.2のソースコードを見ているが、25行目と100行目の意味が分からない
  • JSのクラスの定義や継承について混乱している
回答を見る
  • ベストアンサー

jQueryのvar jQueryの仕組みについて

jQuery-1.6.2のソースコードを見ているのですが質問させてください。 25行目の var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }, そして100行めの jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { の意味が分かりません。 PHP等のclassになれているとJSのクラスは戸惑うことが多いのですが、この例は最たるもので、自分の中のJSでのclassは (1)スーパークラスのプロパティをcall()、apply()で継承 (2)スーパークラスのメソッドをprototypeオブジェクトで継承 (3)インスタンスのデータ型判定に必要なプロパティをconstructorで調整 だったのですが、スーパークラスがどの行のどれなのかも分かりません。 型やメソッド名からこれらがclassを意味しているのは分かるのですが、いったいどの部分がclass定義、継承を行っているのか教えていただけませんでしょうか? なぜこれらがclassになっているのかが理解できません。 分るのは、jQuery.prototype.constructor = jQueryで、それをjQuery.fnに代入((3))している事です。 329行目の、jQuery.fn.init.prototype = jQuery.fn;の右辺がnew {$class}の形であれば(2)のプロトタイプチェーンの形だと分るのですが。 (1)は、init内でメンバ変数を定義したりしているので、classなのかな、という程度しか分っておりません。 jQuery.fnがコンストラクタで、jQueryがインスタンスで、var jQueryは親クラスをinitしたもの?・・・と混乱しております。 質問内容も文章が混乱していて申し訳ないです。

質問者が選んだベストアンサー

  • ベストアンサー
  • Chaire
  • ベストアンサー率60% (79/130)
回答No.2

次のように書き換えてみましょう。 // function jQuery (selector, context) {  return new init(selector, context, rootjQuery); } jQuery.prototype = jQuery.fn; // function init (selector, context, rootjQuery) {  /* ......   this.length = 0;  ...... */ } init.prototype = jQuery.fn; jQuery.fn.init = init; 本質的にはこういうことです(私が書くなら、こんな感じにすると思います)。したがって、new jQuery と new init は、どちらも jQuery.fn をプロトタイプとする新規オブジェクトを生成します。 --- さて、No.1 には書きませんでしたが、JavaScript のコンストラクタには 1 つの珍しい特徴があります。それは、コンストラクタがオブジェクトを return すると、new の戻り値がそのオブジェクトになってしまう、ということです。 上記の jQuery コンストラクタを見て下さい。普通、new jQuery して得られる結果は『jQuery.prototype をプロトタイプとする新規オブジェクト』だと思うでしょう。ところが、jQuery コンストラクタ内では new init の結果を return しています。だから、new jQuery の結果は、new init の結果になるのです。 var o = new jQuery; // 実は o = new init と同じ! この o は init.prototype(= jQuery.fn)をプロトタイプとするオブジェクトですから、ちゃんと目的のものではあります。なら、jQuery.prototype をセットしているのは何のため? とお思いかもしれません。それは、instanceof の結果を調節するためです。 // o instanceof C は、o のプロトタイプが C.prototype なら true を返す o instanceof init; // true o instanceof jQuery; // false これではちょっと格好悪い。そこで jQuery.prototype をセットすれば、 jQuery.prototype = init.prototype; o instanceof jQuery; // true はい、この通り。こんな感じで、JavaScript では instanceof の結果を後でいくらでも変更できる、というわけです。まあ、小手先の話です。 --- それと、No.1 では見落としておましたが、この書き方にはもう一つの目的があると思います。それは、new の付け忘れへの対処(あるいは new を付けなくても良いようにすること)です。 もう一度、上記の init コンストラクタを見て下さい。new init すれば、新規オブジェクトの固有プロパティとして length がセットされます。 var o = new init(...); // o.length = 0 ここで、うっかり new を付け忘れたとしたらどうなるでしょう? init() という関数呼び出しでの this はグローバルオブジェクト(ブラウザ上では Window)になり、length というグローバル変数が作られることになってしまいます。 var o = init(...); // window.length = 0 そこで、上記の jQuery コンストラクタのように書けば、new の有無に関わらず、等しく new init の結果が得られる、というわけです。 var o1 = new jQuery(...); // new init(...) の結果 var o2 = jQuery(...); // new init(...) の結果 利用者に公開するのは jQuery の方であり、init は隠し(?)メンバですから、まずはこれで十分と言えます。これも小手先と言えば小手先の話です。

chopperin
質問者

お礼

Chaireさん、本当に本当にありがとうございました。 だいぶJSの事が分かったように思います。 サイ本、独習、マスター、他数冊本は読んである程度JSは理解していたつもりだったのですが(まぁ結構JSからは離れていて忘れている部分も多いでしょうが)、prototypeの部分などどの本にも載っていないような事を教えていただき非常に分かりやすかったです。 コンストラクタ内のreturn文は禁止と独習、マスター共にハッキリと書かれていましたが、そのような利用法があるとは驚きました。 Prototypoe.jsはザッと見た感じ、素直な書き方だと思いましたが、jQueryは自分には理解できない記述が多く困っておりました。 確か何かに、jQueryは芸術的な美しいコードとか書かれていたのを思い出しましたが、その分自分には難しかったです。 Chaireさんに出会えて本当に良かったと思っております。 親切丁寧にお答えいただいて、本当にありがとうございました。

その他の回答 (1)

  • Chaire
  • ベストアンサー率60% (79/130)
回答No.1

クラスベース言語の「クラス」「継承」という概念をいったん捨てて下さい。とりあえず、個人的な見解ですが: (a). プロトタイプは「デフォルト値を集約したオブジェクト」である。各オブジェクトはどのオブジェクトを「デフォルト値の集合」とするか選ぶ。 (b). デフォルト値以外が必要なときはオブジェクト固有のプロパティを生やす。オブジェクトが固有プロパティを持たなければ「デフォルト値」を探す。 JavaScript の「オブジェクト指向」とは、これだけの話です。new C(p, q) は、C.prototype を「デフォルト値集合」とし、p と q を固有プロパティの値とするオブジェクトを生成します。Object.create(O, props) は、O を「デフォルト値集合」とし、props を固有プロパティとするオブジェクトを生成します。 --- > (2)スーパークラスのメソッドをprototypeオブジェクトで継承 「デフォルト値」はメソッドに限った話ではありません。単に、メソッドは「デフォルト値」のままで十分なことが多い、というだけです。 > (1)スーパークラスのプロパティをcall()、apply()で継承 メソッドでも「デフォルト値」のままで困る場合は固有のメソッドを生やせば良い。call()、apply() を使えば他のコンストラクタから固有プロパティを輸入できますが、他のオブジェクトのプロパティ群を for...in で回して移植しても構いません。固有プロパティを生やすことが重要なわけで、何を使うかは問題ではありません。 > (3)インスタンスのデータ型判定に必要なプロパティをconstructorで調整 上書き自由な constructor でインスタンス判定することは、まずありません。この場合は単に constructor の「デフォルト値」をセットしただけです。そうすれば、new (this.constructor) のように多態的なオブジェクト複製に使われる、かもしれない。とにかく、これ自体は本質的な話ではありません。 --- 前置きが長くなりました。今ダウンロードした jquery-1.6.2.js の行数で書きますが、 99 行目: jQuery.fn = jQuery.prototype = ... 323 行目: jQuery.fn.init.prototype = jQuery.fn; 両者から、次の関係が分かります。 jQuery.fn.init.prototype = jQuery.prototype ゆえに、obj = new (jQuery.fn.init) によって jQuery.prototype(== jQuery.fn)をプロトタイプとするオブジェクト obj が生成されます。また、obj の固有プロパティも init() 内でセットされます。 JavaScript の instanceof 演算子は単純にプロトタイプチェーンを辿るので、jQuery.prototype を書き換えない限り、obj instanceof jQuery も true になります。表面的には「obj は jQuery クラスのインスタンス」のように見えるでしょう。しかし、それはあくまで見かけの話に過ぎません。 繰り返しますが、クラスのない JavaScript で「どれがクラスか」を考えても仕方ありません。重要なのは「どれが obj のプロトタイプか」と、「どれが obj の固有プロパティか」という 2 点だけです。 --- 何でこんな書き方になっているか。想像するに、ひとつは単なるショートカット。ひとつは jQuery.fn に基本機能を集約させ、jQuery コンストラクタにぺたぺた貼付けられるプロパティから保護すること。ひとつは、jQuery の振る舞いを変更したいときに、jQuery コンストラクタそのものを書き換えるのでなく、init を切り替えるだけで済むこと。 今ぱっと思い付いたテキトーな理由ですが、それだけにしては大仰な書き方なので、他の目的もあるでしょう。

chopperin
質問者

お礼

ごめんなさい、"お礼入力"で文章を書くべきでした。

chopperin
質問者

補足

Chaireさん、極めて分かりやすいご説明ありがとうございました。 JSのクラスに関して、考え方が変わりました。 ありがとうございました。 しかし、 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }, の部分の、 >new C(p, q) は、C.prototype を「デフォルト値集合」とし、p と q を固有プロパティの値とするオブジェクトを生成 により、jQuery.fn.init.prototype= jQuery.fn=jQuery.prototypeをデフォルト値集合とするオブジェクトがjQuery自身というのまだよく分かりません。 この構図だとjQuery自身が自分を・・・ってことになっているように思えるのですが、これはどういう意味なのでしょうか? 申し訳ございません。

関連するQ&A

  • jQueryの仕組みについて質問です

    お世話になります。現在jQueryのコードを読んでいるのですが、どうしてもわからないところがあります。 jQuery1.11.1の2774行目、jQuery.merge(...)と自分自身を呼び出していますが、これはjQuery中でどのような処理をして実現しているのでしょうか?jQuery.merge = function(){...};としているわけでもないのに呼び出せているのが理解できません。 mergeメソッド自体は、450行目、jQuery.extend内で定義されているようです。 以下簡易化したjQueryです。 var window = this; // ブラウザ以外で実行する場合のみ必要 (function(){ jQuery = window.jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }; jQuery.fn = jQuery.prototype = { hello: function(){ console.log("hello from fn!"); // ここをjQuery.logと書けるようにしたい } }; jQuery.extend = jQuery.fn.extend = function() {}; jQuery.extend({ log: function(msg){ console.log(msg); } }); init = jQuery.fn.init = function( selector, context ) { console.log("Hello"); }; init.prototype = jQuery.fn; // ここをjQuery.logと書けるようにしたい } )(); jQuery().hello();

  • JQueryのclick(fnc)で指定された関数中のthis

    JQueryのclick(fnc)で関数中のthisの扱いについてお尋ねします。 下記コードでは仮に2つのクラスをモデルとコントローラとして、コントローラからモデルの関数を呼び出しています。value="HTML Tag"のボタンは期待したとおりに'chicken crows.'を表示しますが、value="JQuery"のボタンはcrow()中のthis.modelがundifinedになってしまいます。後者では$.fn.controllers.chickenController.crowをinputタグにコピーしてそれを実行しているのではないかと思います。そのためかthisはHTML Inputを指します。 thisを使用する以外にモデルのcrow()を実行する方法はありませんでしょうか?条件としてはモデルとコントローラのオブジェクトのペアは複数有りうり、コンストラクタやプロトタイプ中で一意の変数を定義できない点にご注意ください。 <script tyoe="text/javascript" src="jquery-1.3.2.min.js" ></script> <script type="text/javascript"> $(function(){ //名前空間 $.fn.models = new Object(); var models = $.fn.models; // コンストラクタ models.bird = function(type){this.type = type;} // インスタンスメソッド models.bird.prototype = {  crow: function(){ alert(this.type + ' crowed.');} } var chicken = new models.bird('chicken'); //名前空間 $.fn.controllers = new Object(); var controllers = $.fn.controllers; // コンストラクタ controllers.birdController = function(model){this.model = model;} // インスタンスメソッド controllers.birdController.prototype = {  crow: function(){   this.model.crow(); // JQuryの場合thisはHTML Inputを指します。  } } $.fn.controllers.chickenController = new controllers.birdController(chicken); $('#b').click($.fn.controllers.chickenController.crow); }); </script> <html> <body>  <input type="button" onclick="$.fn.controllers.chickenController.crow();" value="HTML Tag" />  <input type="button" id="b" value="JQuery" /> </body> </html>

  • 継承・実装の関係で悩んでいます。

    継承・実装の関係について悩んでいます。 ここでは、アクセス制御を考えずに、インスタンスかstaticかabstract(ここではabstract final staticやabstract classのこと)の違いで、どう継承するのか考えています。 // 継承 はメソッドのオーバーライドのことを考えます。(オーバーロードは考えない) クラスAからクラスBでオーバーライドしたメソッドは、 クラスCでさらにオーバーライドできるのでしょうか? クラスCが クラスBのクラスAからオーバーライドしたメソッド をクラスBのメソッドとして見たときに、オーバーライドすることは可能なのでしょうか? クラスA │ インスタンスフィールドA │ staticフィールドA │ │ クラスA() { } │ │ インスタンスメソッドA () { } │ staticメソッドB() { } ↓ クラスB extends クラスA │ インスタンスフィールドA // 継承 │ インスタンスフィールドB │ staticフィールドB │ │ サブクラス1() { } // コンストラクタは継承しない、super()で呼び出す │ │ インスタンスメソッドA () { } // 継承 │ │ インスタンスメソッドB () { } │ staticメソッドB () { } ↓ クラスC extends クラスB implements インタフェースD, ... ↑ インスタンスフィールドA // クラスBのフィールドを継承 │ インスタンスフィールドB // クラスBのフィールドを継承 │ インスタンスフィールドC │ staticフィールドC │ │ サブクラス2() { } │ │ インスタンスメソッドA () { } // クラスBのメソッドを継承 │ インスタンスメソッドB () { } // クラスBのメソッドを継承 │ インスタンスメソッドD () { } // インタフェースDのメソッドを実装 │ インスタンスメソッドE () { } // インタフェースDのメソッドを実装 │ │ │ インスタンスメソッドC () { } │ staticメソッドC() { } │ interface インタフェースD extends インタフェースE ↑ │ staticフィールドD // public static final │ │ インスタンスメソッドD() { } // public abstract │ インスタンスメソッドE() { } // 継承 │ interface インタフェースE staticフィールドE // public static final インスタンスメソッドE() { } // public abstract

    • ベストアンサー
    • Java
  • 継承について

    下の質問の回答、間違ってませんか? -------------------------------------------- 親クラスのフィールドをメソッドを全て継承することになります。よって、サブクラス2は、サブクラス1とスーパークラスのメンバーを全て持つことになります。サブクラス2をインスタンス化すると、サブクラス1とスーパークラスが全てインスタンス化(コンピューターのメモリ上等に配置)されます。 ただ、可視性というものがあり、サブクラスから親クラスのメンバーを直接取り扱えない場合があります。privateや可視性修飾子無しの場合、サブクラスから直接取り扱えません。ただ、継承はされているので、インスタンスとしては存在します。例えば、 class スーパークラス{ private int abc; public int getAbc(){ return this.abc; } というクラスがあり、これを継承したサブクラスがあったとします。 サブクラスからは、privateなメンバー変数abcは直接取り扱うことができません。ただ、publicなgetAbcメソッドはサブクラスから取り扱えます。これで何が分かるかといいますと、privateなメンバーでもサブクラスに継承されるのです。単に可視性の問題で直接取り扱えないだけなのです。 また、オーバーライドされたメソッドがあったとしても、super.メソッド()で親のメソッドも呼べますので、これも継承されているのです。

  • super( )メソッドについて質問です

    サブクラスのコンストラクタの定義をするときにsuper()メソッドを使うときはどういうときですか?また、どうしてsuper()メソッドを使って、スーパークラスのインスタンスを作成しないといけないのですか?教えてください。

    • ベストアンサー
    • Java
  • PHPでの継承について

    PHPの継承についての質問です。 PHPではクラスを継承する場合 スーパークラスのpublicとprotectedなメンバを継承すると いいます。 継承とは、スーパークラスで定義したメンバが サブクラスで定義されたことと同義である。 という前提で質問します。 ひょっとしたらこの前提も微妙に間違っているかもしれません。 以下の用なコードがあるとします。 <?php class AClass { //サブクラスへ継承されるスーパークラスのメソッド public function superClassPublicMethod(){ //【1】 $this-> subClassPrivateMethod(); } } class BClass extends AClass{ public function subClassPublicMethod(){//【2】 $this->subClassPrivateMethod(); } //継承したスーパークラスで定義されているメソッドからコールするサブクラスで定義したメソッド private function subClassPrivateMethod(){ //【3】 print __CLASS__; } } $obj = new BClass(); //サブクラスで定義した public なメソッドを実行 $obj->subClassPublicMethod(); //【4】 //スーパークラスから継承したpublic なメソッドを実行 $obj-> superClassPublicMethod();//【5】 ?> コレを実行すると【4】はきちんとBClassという結果が返ってきます。 ところがその下の【5】を実行すると Fatal error: Call to private method BClass::subClassPrivateMethod() from context 'AClass' in /usr/local/apache/htdocs/ext06.php on line 9 という実行時エラーが返ってきます。これを読むと、AClassのコンテキスト内ではBClassのprivateなメンバは コールできませんよ。というエラーですね。そもそもAClassのコンテキストということは、これはAClass内で実行されているということですよね? ここで、スーパークラスからこのpublicなメソッドは継承しているはずなのになぜ、サブクラスのBCLass内ではなくスーパークラスのAClass内という エラーが返っているのでしょうか? 継承とは、サブクラスで定義したと同義ではなく、スーパークラスまで内部的にアップキャスト?してコールしにいっているのでしょうか? 詳しい方ご教授ください。 よろしく御願いします。

    • ベストアンサー
    • PHP
  • jqueryのロールオーバーについて

    jqueryのロールオーバー設定に関してご質問です。 あるサイトのコーディングを行ってます。 サイト内のナビーゲーションボタンを瓶の画像でまとめてます。 各瓶の画像にリンクを付け、 マウスを乗せるとその瓶が開いた画像が 切り替わりでフェードインの感じで出てくるという設定を目指しているのですが、 うまくいきません。 色々やった結果マウスを当てると、 切り替わりで空いた瓶がフェードで出るのですが、 その画像奥に閉じた瓶がそのまま残ってて重なってる感じです。 こちらはどのようにすれば上手く画像奥の瓶は消えるのでしょうか? ちなみに画像はpngです。 フェードの切り替わりは是非そのまま使用したいと考えてます。 あとマウスを外せばまた閉じた瓶がフェードで出るという感じを目指してます。 どなたかご教示いただいてもよろしいでしょうか? 一応下記htmlとjsコードも載せておきます。 ▼html <script src="../lib/jquery1.8.1.min.js"></script> <script src="../js/jquery.tgImageRollover.js"></script> <script type="text/javascript"> jQuery(function(){ $(this).tgImageRollover({ selector : 'img.rollover', // セレクタ ←(7) attach : '_on', // ファイル名末尾句 ←(8) onFadeTime : 600, // カーソルON時のアニメーション時間 ←(9) offFadeTime : 400 // カーソルOFF時のアニメーション時間 ←(10) }); }); </script> <a href="#"><img class="rollover" src="../img/nav01.png" alt="" width="100" height="50"></a> <a href="#"><img class="rollover" src="../img/nav02.png" alt="" width="100" height="50"></a> ▼jquery.tgImageRollover.js ;(function($){ $.fn.tgImageRollover = function(options){ var opts = $.extend({}, $.fn.tgImageRollover.defaults, options); $(opts.selector).each( function(){ this.onImage = $(this).attr('src').replace(/^(.+)(\.[a-z]+)$/,"$1"+ opts.attach +"$2"); this.onHtml = $('<img src="'+this.onImage+'" alt="" style="position:absolute; opacity:0;">'); $(this).before(this.onHtml); $(this.onHtml).hover( function(){ $(this).stop().animate({'opacity': '1'}, opts.onFadeTime); }, function(){ $(this).stop().animate({'opacity': '0'}, opts.offFadeTime); } ) } ) } // default option $.fn.tgImageRollover.defaults = { selector : 'img.rollover', attach : '_on', onFadeTime : 600, offFadeTime : 400, }; })(jQuery);

  • 【.NET】コンストラクタをあとで呼出す方法

    .NET初心者です。 今VB.NETでVB6向けの自作COMクラスライブラリを作成しています。 自作クラスはStreamWriterクラスのラッパークラスとしてStreamWriterと継承関係にあります。 COMは引数なしのコンストラクターを一つ用意しなければなりませんが、 StreamWriterクラスは引数なしのコンストラクタを用意されていないため困っています。 自作クラス自身もFileクラスのラッパークラスで引数として使う必要があるため 継承関係を維持してVB6に公開する必要があります。 この問題をなんとか解決する手法って存在するのでしょうか? ちなみにほかに考えた手法は下記です。 ・プロパティーに StreamWriterのインスタンスを用いて継承関係なしであとでinitメソッドを呼び出す   →Fileラッパークラスの引数でStreamWriter型に代入出来ないため不可 ・StreamWriterを引数に取るメソッドの実装をあきらめる   →極力したくない ・StreamWriterラッパークラスの引数なしコンストラクタでStreamWriterクラスの引数ありコンストラクタを呼ぶ   →後でファイルのパスを指定する方法が見つからないため不可 他言語はそれなりにやってきたのでコンストラクターをあとで呼び出すなんて出来ないのはなんとなくわかるのですが何せ.NETを触り始めたのが最近なのでよろしくお願い致します。

  • 継承(extends)について

    extendsについてわからないことがあります。 次の3つのクラスがあった時、 class スーパークラス { ... class サブクラス1 extends スーパークラス { ... class サブクラス2 extends サブクラス1 {... サブクラス2は、サブクラス1でスーパークラスから継承しているフィールドをメソッドを継承するのでしょうか?

    • ベストアンサー
    • Java
  • SQLiteOpenHelperの実行タイミングは

    android(Java) ・「SQLiteOpenHelperクラス」を継承したユーザー定義クラスの、「コンストラクタ」や「onCreateメソッド」はどのタイミングで(どういう経路で)呼び出されるのでしょうか? >データベースの作成がまだ行われていなければ作成を行い、作成されていればそれを開きます ・どうやってそれを知るのでしょうか? ・アクテイビティにアクセスした時点で読み込まれるのかと思ったのですが、アクティビティクラスでは、importしていないみたいだし… ・アクテイビティは関係ない? ・「SQLiteOpenHelperクラス」を継承した定義クラスが、どのタイミングで、どこから呼び出されるか、教えてください ※コンストラクタに引数として渡っているContextが、何か関係しているのでしょうか?

    • ベストアンサー
    • Java