本文是在閱讀了Aaron艾倫的jQuery源碼解析(地址:http://www.imooc.com/learn/172)后的個人體會以及筆記。在這里感謝艾倫老師深入淺出的講解!!
先來看看如何生成一個jQuery對象,源碼:
var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); };
當我們使用jQuery('something')或者$('something')時,我們得到的是一個 jQuery.fn.init()對象。那么jQuery.fn是什么鬼?
jQuery.fn = jQuery.prototype = { // jQuery版本 jquery: version, constructor: jQuery, // 構造函數 // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, // 省略..... }
jQuery.fn 實際上是jQuery構造函數的原型對象的引用!! 所以我們以后看到 jQuery.fn時,把他當成jQuery構造函數的原型對象就可以了。
知道了jQuery.fn , 接下來看看jQuery.fn.init()函數
jQuery.fn.init = function( selector, context ) { // 省略....
return this; };
jQuery.fn.init.prototype = jQuery.prototype; // 注意這里! 這句代碼讓init對象可以使用jQuery的原型方法。
這樣,我們在創建jQuery對象時就不用使用new關鍵字了。
整體看一下源碼架構:
var $ = jQuery = function(selector,context){ return new jQuery.fn.init(selector,context) // 返回一個jQuery.fn.init()對象 } jQuery.fn = jQuery.prototype = { constructor:jQuery, init:function(){ // 省略..... return this; } } jQuery.fn.init.prototype = jQuery.fn
直觀的感受一下相互之間的關系:
調用jQuery函數,我們得到的是一個jQuery.fn.init實例,這個實例的原型對象被重新指向到了jQuery函數的原型對象,所以這個實例可以使用jQuery原型對象的屬性和方法,而如果我們給jQuery函數附加方法,那么這個方法就變成了靜態方法。
然后來看一下jQuery.fn.init函數的源碼:

var rootjQuery, // A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, // <any+>--任意個非右尖括號字符 或者 以#開頭的 init = jQuery.fn.init = function( selector, context ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings if ( typeof selector === "string" ) { // 如果selector的格式是字符串類型,且字符串長度大於等於3,並且內容格式為: <something> if ( selector[0] === "<" && selector[ 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; // Option to run scripts is true for back-compat // Intentionally let the error be thrown if parseHTML is not present 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] ); // Support: Blackberry 4.6 // gEBID returns nodes 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 typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); };
配張思路圖: