【jQuery-1.7.2ソース分析】jQueryオブジェクト

9713 ワード

前言
他の人が書いたソース分析の文章を見て、多くはコードを貼って、注釈を書きます.个人的にはそこから吸収した知识は限られていると思います.このように技术よりも、设计思想よりも軽いので、このシリーズは古い道を缲り返すつもりはありません.もっと多くののののは私のソースコードを见る心得を分かち合うことです.いくつかの普及に値するテクニックについては、文章の最後に専門的にリストされ、検索されます.
 
jQueryオブジェクトは何ですか.例を挙げると、$('#id')はjQueryオブジェクトを返します.これはjQuery全体の核心です.だから、私は先にそれを分析します.
var jQuery = function( selector, context ) {

    // The jQuery object is actually just the init constructor 'enhanced'

    return new jQuery.fn.init( selector, context, rootjQuery );

};

......

jQuery.fn = jQuery.prototype = {

    constructor: jQuery,

    init: function( selector, context, rootjQuery ),

    selector: "",

    jquery: "1.7.2",

    length: 0,

    size: function(),

    toArray: function(),

    get: function( num ),

    pushStack: function( elems, name, selector ),

    each: function( callback, args ),

    ready: function( fn ),

    eq: function( i ),

    first: function(),

    last: function(),

    slice: function(),

    map: function( callback ),

    end: function(),

    push: push,

    sort: [].sort,

    splice: [].splice

};

jQuery.fn.init.prototype = jQuery.fn;

このコードを初めて見た人は、その構造にめまいがします.このような書き方は本当に珍しいからです.johnの考えについて、私は大胆に推測しました.
1.jQオブジェクトのコンストラクション関数はなぜprotptypeなのか.init?
答え:コンストラクション関数の論理はprototypeの他の属性/方法と関連しています.例えば、selectorやlengthは、一緒に書くと読みやすいです.
Initメソッドを呼び出すと、パラメータが空の場合、jQオブジェクトはprototypeと同等になり、構造が明確に見えます.
パラメータが空でない場合、selectorやlengthなどのprototypeのプロパティが上書きされます.つまり、prototypeのプロパティはデフォルト値を与えるためだけです.また、contextなどのprototypeにリストされていないプロパティも作成されます.これらのプロパティは構造関数に書かれています.しかし同様に,論理をコンパクトに保つためにinitメソッドに併記する.
 
2.jQはいったいどんなオブジェクトを作りたいのですか?答え:このオブジェクトは配列に似ています.例えば、次のコードです.
function FuckYou(who) {

    this[0] = who;

}

var fy = new FuckYou('john');


そこでfy[0]=「john」と、ちょっと気を失ったのではないでしょうか.これは配列ではありません.あなたの目に隠されないでください.これはオブジェクトで、0は属性名です.なぜなら0=whoは文法を間違えるので、ちょっとしたテクニックを使いました.
さらにprototypeの他の方法、例えばeach、slice、map、push、sort、spliceを見て、配列をシミュレートしているわけではありません.
 
3.pushStackを説明しましょうか?
答え:その名の通り、スタック操作で、主にundoに使用されています.
/**

 *               

 *   ctrl + z          

 *                    

 */

pushStack: function( elems, name, selector ) {

    //        jQ  ,API prototype    

    var ret = this.constructor();



    // elems    ,  push

    if ( jQuery.isArray( elems ) ) {

        push.apply( ret, elems );



    // elems     ,  merge(),       :

    // 1. elems childNodes      

    // 2. elems this[0], this[1]       ,       

    } else {

        jQuery.merge( ret, elems );

    }



    //   $("p").find("span")

    //       p,             span

    //              ,jQ          ,    (  end())

    ret.prevObject = this;



    ret.context = this.context;



    //        ,            jQ     

    // name        

    // selector       

    //    ,jQ     DOM     ,  after, find   ,            

    if ( name === "find" ) {

        // find         ,  "div span"    

        ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;

    } else if ( name ) {

        //         ,            

        //           .       class,     .  

        //     ,        Sizzle        ,          , 

        ret.selector = this.selector + "." + name + "(" + selector + ")";

    }



    return ret;

}


  
4.initをもう一度説明しましょうか.
答え:説明しなければなりませんね.この方法は本当に重要ですね.jQの構造関数は説明しないで何を説明することができますか.
init: function( selector, context, rootjQuery ) {

    var match, elem, ret, doc;



    //    $(""), $(null), or $(undefined)

    if ( !selector ) {

        return this;

    }



    //    $(DOMElement)

    if ( selector.nodeType ) {

        this.context = this[0] = selector;

        this.length = 1;

        return this;

    }



    //    body       ,        

    if ( selector === "body" && !context && document.body ) {

        this.context = document;

        this[0] = document.body;

        this.selector = selector;

        this.length = 1;

        return this;

    }



    //    HTML    

    if ( typeof selector === "string" ) {

        //      , <p>,             

        if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {

            match = [ null, selector, null ];



        } else {

            // quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/

            //           

            // quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/

            //

            //     ,             ,

            //       location.hash   XSS  

            // 

            //         ,       

            // 1. ^[^#<]*(<[\w\W]+>)[^>]*$

            // 2. ^#([\w\-]*)$

            //       ,   ID 

            //        HTML   :

            //   [^#<]*         # <

            //   (<[\w\W]+>)          , <div>123</div>

            //   [^>]*$         >

            match = quickExpr.exec( selector );

        }



        //     ,     ID     context

        //     ID   context       ?  ,ID       

        if ( match && (match[1] || !context) ) {



            //    $(html) -> $(array)

            if ( match[1] ) {

                //    context     HTMLElement

                context = context instanceof jQuery ? context[0] : context;

                //    doc   document

                doc = ( context ? context.ownerDocument || context : document );



                // rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/

                //         ,  <input />   <div></div>

                //       ,      ,   createElement    

                ret = rsingleTag.exec( selector );



                if ( ret ) {

                    //     ,context        ?

                    //          ,context    {id: 'id', title: 'title'}   

                    if ( jQuery.isPlainObject( context ) ) {

                        selector = [ document.createElement( ret[1] ) ];

                        jQuery.fn.attr.call( selector, context, true );



                    } else {

                        selector = [ doc.createElement( ret[1] ) ];

                    }



                } else {

                    //       ,        

                    ret = jQuery.buildFragment( [ match[1] ], [ doc ] );

                    selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;

                }



                return jQuery.merge( this, selector );



            //    $("#id")

            } else {

                elem = document.getElementById( match[2] );



                // Check parentNode to catch when Blackberry 4.6 returns

                // nodes that are no longer in the document #6963

                //  ,        ,              ,     bugfix   jQ 

                //    ,          IE678?      ,     HTML5 

                //     ,      fix      ?john      

                if ( elem && elem.parentNode ) {

                    // IE   Opera    getElementById    name  ,   ID,  

                    if ( elem.id !== match[2] ) {

                        return rootjQuery.find( selector );

                    }



                    // Otherwise, we inject the element directly into the jQuery object

                    this.length = 1;

                    this[0] = elem;

                }



                this.context = document;

                this.selector = selector;

                return this;

            }



        //    $(expr, $(...)),    $(...).find(expr)

        //            ,jQ        

        //            ,context    HTMLElement      

        //        jQ         ,    

        } else if ( !context || context.jquery ) {

            return ( context || rootjQuery ).find( selector );



        //    $(expr, context),    $(context).find(expr)

        //        

        } else {

            return this.constructor( context ).find( selector );

        }



    //    $(function)

    //               

    } else if ( jQuery.isFunction( selector ) ) {

        return rootjQuery.ready( selector );

    }



    // selector    jQ  ,                   

    if ( selector.selector !== undefined ) {

        this.selector = selector.selector;

        this.context = selector.context;

    }



    //   selector    this    

    //       ,this         

    return jQuery.makeArray( selector, this );

}

 
 
 5. 何か言いたいことがありますか.
答え:最後に言いましょう.
jQuery.fn = jQuery.prototype = {...}

jQuery.fn.init.prototype = jQuery.fn;


どうしてそう書くの?例えば最初の行は、なぜ変数に直接与えずにjQueryに与えられるのか.fn?
1.変数を与えるのはjQというファイルの内部でしか使えないが、jQは強力なプラグインメカニズムがあるので、外部拡張を容易にするためにjQueryに与える.fn.もちろんこの部分を処理しなくても大丈夫です.もっと字を打ってください.しかし、jQはやはり超流行の枠組みで、略語できるのは自分で縮めましょう.他の人はこのような恐れのない労働をします.
 
2.2行目の書き方はさらに卵が痛い.まずinitは構造関数ですが、私は続けますか?まあ、これは基礎知識だと思いますが.
function jQuery() { }

jQuery.prototype = {

    constructor: jQuery

}

jQがこの行を書かなければ、obj instanceof jQueryは永遠にfalseになります.
 
テクニック:
1.オブジェクト上の属性/メソッドは、プロトタイプ上の対応する属性/メソッドを上書きします(overrideの方が良いかもしれません).
2.配列を巧みに使用する方法.
var push = Array.prototype.push;



function ArrayLike() {

    this[0] = 0;

    this[1] = 1;

    this.length = 2;

}



var al = new ArrayLike();

push.apply(al, [2, 3]);

console.log(al)

最後の結果はal.length===4で、jQオブジェクトがなぜlength属性を必要とするのかがわかるでしょう