1. 程式人生 > >jQuery原始碼解析(jQuery物件的例項屬性和方法)

jQuery原始碼解析(jQuery物件的例項屬性和方法)

1、記錄版本號 以及 修正constructor指向

   jquery: core_version,
   constructor: jQuery,

  因為jQuery.prototype={ ... } 這種寫法將自動生成的jQuery.prototype.constructor屬性覆蓋刪除,所以需要重新修正(指向其建構函式 jQuery)。
2、init() 初始化方法:

     init()方法最終返回的是this -jQuery(其實是jQuery.prototype.init)方法的例項物件。

     例項物件繼承了jQuery的例項屬性和方法,且具有下標為0、1、2...、length的屬性,

從而 css()等方法可以利用 for...in迴圈處理 $() 集合中DOM元素。火狐下控制檯截圖如下:

下面分步驟分析程式碼:

      ①if:傳入無效的引數selector,直接返回this。

    init: function( selector, context, rootjQuery ) {    
         var match, elem;
     if ( !selector ) {
         return this;
     };
         ...
     }

  ②if :傳入的引數selector是字串, rquickExpr是前面宣告的變數:rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;

          else if :傳入的是DOM元素;

          else if: 傳入的是函式,呼叫$("document").ready(function(){ ... });

          if :傳入的是jQuery物件(存在屬性selector);

init: function( selector, context, rootjQuery ) {   
     ...
     
if ( typeof selector === "string" ) { if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {            match = [ null, selector, null ];         } else {          match = rquickExpr.exec( selector );          }; ... }else if ( selector.nodeType ) {             this.context = this[0] = selector;             this.length = 1;             return this;         } else if ( jQuery.isFunction( selector ) ) {             return rootjQuery.ready( selector );         }         if ( selector.selector !== undefined ) {             this.selector = selector.selector;             this.context = selector.context;         }         return jQuery.makeArray( selector, this ); };

上面程式碼中,if :情況包括類似 $("<div></div>")、$("<div>aaa</div><div>aaa</div>") 兩中情況;
                     else :包括其餘情況,例如:$(“</div>111”)、$("#id")、$(".class")、$("div")、$("#id .class div")等情況。requickExpr匹配的是$(“</div>111”)、$("#id")兩種情況。如果傳入的是$(“</div>111”),則match=["</div>111","</div>",null];如果傳入的是$("#id"),則match=["#id",null,“id”];

         ③ 接下來是if esle 判斷

         if :match不為null 且 match[1]不為null(傳入的是標籤形式),或match不為null且content為null(傳入的是id形式,無需制定上下文);

         else if:  如果context沒有傳入或者傳入是$()物件形式,則呼叫$().find ;

         else:如果context傳入是原生jsDom物件形式,則呼叫$().find

if ( match && (match[1] || !context) ) {
       
}else if ( !context || context.jquery ) {     
    return ( context || rootjQuery ).find( selector );
}else {            
    return this.constructor( context ).find( selector );
}

     ④match不為null時,又分標籤形式 和 id形式

     涉及到的函式:$.parseHTML 、$.merge、$.isPlainObject()

     $.parseHTML :將字串轉換為儲存DOM節點的陣列 。第一個引數為傳入的字串,第二個為指定的根節點,第三個是boolean值 (“<script></script>”是否可以轉化為<script>標籤),預設為false,不轉換。

     $.merge:合併兩個陣列,在jQuery內部不僅可以合併陣列,也可以合併類陣列。例如:

var  a={0:"aaa",1:"bbb",length:2} ,
     b=["ccc","ddd"];
$.merge(a,b);   //{0:"aaa",1:"bbb",2:"ccc",3:"ddd",length:4};

        jQuery原始碼中先利用$.parseHTML 將傳入的標籤字串轉化為DOM節點陣列,然後利用$.merge將DOM陣列擴充到jQuery物件。
       $.isPlainObject():判斷傳入的引數是否是由 {}或new Object 建立的物件。

 

   程式碼分析:

   if:標籤形式,(建立DOM並擴充到jQuery物件)

    if ( match[1] ) {
        //傳入的第二個引數如果是jQuery物件,則轉化為原生DOM
        context = context instanceof jQuery ? context[0] : context;
        //利用merge對jQuery例項物件進行擴充
        jQuery.merge( this, jQuery.parseHTML(
            match[1],
            context && context.nodeType ? context.ownerDocument || context : document,
            true
        ) );
        ...
    } else {
        ...
    }

  if:傳入的第一個引數 是單標籤字串 且 第二個引數是純物件 ,例如 $("<div>",{"html":"aaa","title":"aaa"});

  對第二個物件進行for in 迴圈,如果jQuery[match]是函式,呼叫jQuery[match](context[match]); 否則呼叫jQuery.attr(match,context[match])

if ( match[1] ) {
    ...
    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
      for ( match in context ) {
         if ( jQuery.isFunction( this[ match ] ) ) {
             this[ match ]( context[ match ] );
         } else {
            this.attr( match, context[ match ] );
        }
    }
    }
    return this;
} else { 
    ...
}

else:傳入的是#id 形式

if ( match[1] ) {
   ...
} else {
    elem = document.getElementById( match[2] );
    if ( elem && elem.parentNode ) {
        this.length = 1;
        this[0] = elem;
    }
 
    this.context = document;
    this.selector = selector;
    return this;
}

 

 

相關jQuery原始碼:

jQuery.fn = jQuery.prototype = {
    // The current version of jQuery being used
    jquery: core_version,
 
    constructor: jQuery,
    init: function( selector, context, rootjQuery ) {
        var match, elem;
 
        // HANDLE: $(""), $(null), $(undefined), $(false)
        if ( !selector ) {
            return this;
        }
 
        // Handle HTML strings
        if ( typeof selector === "string" ) {
            if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                // Assume that strings that start and end with <> are HTML and skip the regex check
                match = [ null, selector, null ];
 
            } else {
                match = rquickExpr.exec( selector );
            }
 
            // Match html or make sure no context is specified for #id
            if ( match && (match[1] || !context) ) {
 
                // HANDLE: $(html) -> $(array)
                if ( match[1] ) {
                    context = context instanceof jQuery ? context[0] : context;
 
                    // scripts is true for back-compat
                    jQuery.merge( this, jQuery.parseHTML(
                        match[1],
                        context && context.nodeType ? context.ownerDocument || context : document,
                        true
                    ) );
 
                    // HANDLE: $(html, props)
                    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                        for ( match in context ) {
                            // Properties of context are called as methods if possible
                            if ( jQuery.isFunction( this[ match ] ) ) {
                                this[ match ]( context[ match ] );
 
                            // ...and otherwise set as attributes
                            } else {
                                this.attr( match, context[ match ] );
                            }
                        }
                    }
 
                    return this;
 
                // HANDLE: $(#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
                    if ( elem && elem.parentNode ) {
                        // Inject the element directly into the jQuery object
                        this.length = 1;
                        this[0] = elem;
                    }
 
                    this.context = document;
                    this.selector = selector;
                    return this;
                }
 
            // HANDLE: $(expr, $(...))
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );
 
            // HANDLE: $(expr, context)
            // (which is just equivalent to: $(context).find(expr)
            } else {
                return this.constructor( context ).find( selector );
            }
 
        // HANDLE: $(DOMElement)
        } else if ( selector.nodeType ) {
            this.context = this[0] = selector;
            this.length = 1;
            return this;
 
        // HANDLE: $(function)
        // Shortcut for document ready
        } else if ( jQuery.isFunction( selector ) ) {
            return rootjQuery.ready( selector );
        }
 
        if ( selector.selector !== undefined ) {
            this.selector = selector.selector;
            this.context = selector.context;
        }
 
        return jQuery.makeArray( selector, this );
    },
 
    // Start with an empty selector
    selector: "",
 
    // The default length of a jQuery object is 0
    length: 0,
 
        ...
};