init比较复杂,是jQuery的核心方法,分情况处理selector,返回jQuery实例。 jQuery.fn = jQuery.prototype = { init: function( selector, context ) { var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined) // 处理 "" null undefined选择器 if ( !selector ) { return this; } // Handle $(DOMElement) // 处理 DOMElement , window没有nodeType属性,document的nodeType为9 if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; } // Handle HTML strings // 处理html css字符串 if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? match = quickExpr.exec( selector ); // Verify a match, and that no context was specified for #id // 如果match 没有为#id指定context if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) // 处理html,创建元素 if ( match[1] ) { doc = (context ? context.ownerDocument || context : document); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest ret = rsingleTag.exec( selector ); // 单个标签 if ( ret ) { if ( jQuery.isPlainObject( context ) ) { selector = [ document.createElement( ret[1] ) ]; jQuery.fn.attr.call( selector, context, true ); } else { selector = [ doc.createElement( ret[1] ) ]; } // 创建多标签元素 } else { ret = buildFragment( [ match[1] ], [ doc ] ); selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; } // HANDLE: $("#id") } else { elem = document.getElementById( match[2] ); if ( elem ) { // Handle the case where IE and Opera return items // by name instead of ID // 处理IE和Opera返回多个对象?有点不明白,是说document.getElementById返回items么? if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object // 把取到的元素注入jQuery对象 this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $("TAG") // 处理:标签选择器 } else if ( !context && /^/w+$/.test( selector ) ) { this.selector = selector; this.context = document; selector = document.getElementsByTagName( selector ); // HANDLE: $(expr, $(...)) // 处理有context参数的情况 } else if ( !context || context.jquery ) { return (context || rootjQuery).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) // 用find方法处理复杂表达式 } else { return jQuery( context ).find( selector ); } // HANDLE: $(function) // Shortcut for document ready // 处理$(document).ready(function)的简写形式 } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if (selector.selector !== undefined) { this.selector = selector.selector; this.context = selector.context; } // 返回jquery的类对象数组 // 其它情况都提前ruturn了,下面返回HANDLE: $(html) -> $(array) $("TAG") $(window)的处理结果 return jQuery.isArray( selector ) ? this.setArray( selector ) : jQuery.makeArray( selector, this ); }, // Start with an empty selector // 从空选择器开始 selector: "", // The current version of jQuery being used jquery: "1.4", // The default length of a jQuery object is 0 length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, toArray: function() { return slice.call( this, 0 ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array // 取对象数组中的元素 get: function( num ) { return num == null ? // Return a 'clean' array // 如果num是null返回一个干净数组 this.toArray() : // Return just the object ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) // 返回新匹配元素集合 pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = jQuery( elems || null ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; // 把selector标记为对应的选择器字符传,以便再解析为jquery对象 if ( name === "find" ) {// "div .pp" ret.selector = this.selector + (this.selector ? " " : "") + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } // Return the newly-formed element set return ret; }, // Force the current matched set of elements to become // the specified array of elements (destroying the stack in the process) // You should use pushStack() in order to do this, but maintain the stack setArray: function( elems ) { // Resetting the length to 0, then using the native Array push // is a super-fast way to populate an object with array-like properties // 把length属性重置为0,因为push从0开始添加,这种方式性能非常好。 // push用在类数组元素上,奇淫技巧 // jrj[0] = 0; // push.call(jrj,2) //覆盖了jrj[0] = 0,所以push是从0开始 // alert(jrj[0]); // 结果是 2 this.length = 0; push.apply( this, elems ); return this; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Attach the listeners // 绑定事件监听 jQuery.bindReady(); // If the DOM is already ready // 如果DOM就绪直接执行fn if ( jQuery.isReady ) { // Execute the function immediately fn.call( document, jQuery ); // Otherwise, remember the function for later // 否则把fn添加到readyList中 } else if ( readyList ) { // Add the function to the wait list readyList.push( fn ); } // 有了上面的判断,页面中$(document).ready()位置可以任意 return this; }, // 返回this对象中索引为i的元素 eq: function( i ) { return i === -1 ? this.slice( i ) : this.slice( i, +i + 1 ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, // 类似数组的slice方法 slice: function() { return this.pushStack( slice.apply( this, arguments ), "slice", slice.call(arguments).join(",") ); }, // 把一组元素映射成另一数组 map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, // 返回上一个jQuery对象 end: function() { return this.prevObject || jQuery(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: push, sort: [].sort, splice: [].splice }; // Give the init function the jQuery prototype for later instantiation // 这句保证了 init 方法里的 this 拥有 jQuery 实例的方法 jQuery.fn.init.prototype = jQuery.fn;