<p>不知该起什么题目,随便吧。不过我没心情逐一介绍其API,那是文档的事。比起那些随时会被废弃的方法,我更着重其内在的技术,所以我选择读源码。由于我从不用jQuery,可能理解有些偏差。不过,看了这么多类库,有许多东西都是共通。比如这个jQuery对象就是DOM对象的加工工场,把DOM对象包裹其中,外围是许多便捷的方法。我们可以想象一下太阳系,DOM就是太阳,css就其中一个行星,attr是另一个……之所以选择类数组形式,是为了用map,filter等发端于数组的同名方法进行大数量的DOM操作。好了,下面就直接写在注解中吧。</p> <pre class="brush:javascript;gutter:false;toolbar:false"> //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved var window = this, undefined, _jQuery = window.jQuery, _$ = window.$, //把window存入闭包中的同名变量,undefined的情形一样,的确是让更内围的作用域的方法调用时,不要跑到那么远 //_jQuery与_$用于以后重写 jQuery = window.jQuery = window.$ = function( selector, context ) { //用于返回一个jQuery对象 return new jQuery.fn.init( selector, context ); }, //这东西看起来很唬人,其实是用来判定是否是html字符串与ID, //是就调用jQuery.clean方法或document.getElementById quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, //检测是否一个简单的选择器,怎样才为之简单呢? //就是一重类选择器,也就是这个样子.red,如果.red .dd就不合要求了 //看它的限制,一定要是类选择器,后面不能跟伪类选择器,ID选择器,属性选择器,第二重第三重的类选择器 //与并联选择器 isSimple = /^.[^:#\[\.,]*$/; </pre> <pre class="brush:javascript;gutter:false;toolbar:false"> jQuery.fn = jQuery.prototype = { init: function( selector, context ) { //如果为空就把document塞进jQuery对象中 selector = selector || document; //如果第一个参数是DOM对象,那它肯定有nodeType吧, //是就开始创建索引,创建类数组对象 //说白了就是一个hash,只不过键是数字,值是DOM对象 //不过它除了[0],[1]等数字键外,还有"css","attr","addClass"等字符串键,它们的值对应方法 //length属性是就Array.prototype.slice与map,filter等方法准备 //this.context是作为下次搜索的起点,如DOMElement.getElementsByTagName("span") if ( selector.nodeType ) { this[0] = selector; this.length = 1; this.context = selector; return this; } //如果传入的是CSS selector字段 if ( typeof selector === "string" ) { //如果是很单的tagName或ID var match = quickExpr.exec( selector ); //选择器类似h1的情形 if ( match && (match[1] || !context) ) { //如果是标签 if ( match[1] ) //用字符串里创建DOM元素 selector = jQuery.clean( [ match[1] ], context ); // HANDLE: $("#id") else { //如果符合quickExpr的第二种情况 var elem = document.getElementById( match[3] ); // Handle the case where IE and Opera return items // by name instead of ID //在IE与Opera中,ID并不一定返回一个元素,这时用name来查找 if ( elem && elem.id != match[3] ) return jQuery().find( selector ); //由于ID有排他性,因此需要清空前面的context, var ret = jQuery( elem || [] ); //重设搜索起点, ret.context = document; //安装DOM对象 ret.selector = selector; return ret; } // HANDLE: $(expr, [context]) // (which is just equivalent to: $(content).find(expr) } else //处理非常复杂的选择器,如镶嵌的CSS3选择器 return jQuery( context ).find( selector ); // HANDLE: $(function) // Shortcut for document ready //著名的DOMReady的,就是美元符号里面传入一个匿名函数 } else if ( jQuery.isFunction( selector ) ) return jQuery( document ).ready( selector ); // Make sure that old selector state is passed along if ( selector.selector && selector.context ) { this.selector = selector.selector; this.context = selector.context; } //确保以jQuery的类数组对象返回 return this.setArray(jQuery.isArray( selector ) ? selector : jQuery.makeArray(selector)); }, </pre> <p>基本上init就是个大熔炉,根据传入参数的类型做出不同的处理,如DOM对象,字符串,数组对象与NodeList这样的类数组对象转换成jQuery对象,如果是函数,则改成DOM加载。</p> <pre class="brush:javascript;gutter:false;toolbar:false"> //@author 司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/ All rights reserved // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.3.2", //返回jQuery对象所包裹的DOM对象的数量 size: function() { return this.length; }, //jQuery里面的方法都有一个特点,就是功能多 //如著名的css,即是读方法也是写方法 //这个可以返回一个纯数组 //也可以返回一个纯净的DOM对象(根据索引值) get: function( num ) { return num === undefined ? // Return a 'clean' array Array.prototype.slice.call( this ) : // Return just the object this[ num ]; }, </pre> <pre class="brush:javascript;gutter:false;toolbar:false"> // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { //创建一个新的jQuery对象 var ret = jQuery( elems ); //保存原来jQuery对象的引用 ret.prevObject = this; //把原来的context移过来,context在jQuery通常用作搜索的新起点 ret.context = this.context; //把selector标记为一个特殊的字符串,以后再解析为jQuery对象 if ( name === "find" ) 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 //把许多元素一并放置到新的jQuery对象中,由于用Array.prototype.push,不用自己维护长度 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 this.length = 0; Array.prototype.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.) //类似javascript1.6的forEach迭代器 //这里是原型方法调用静态方法 each: function( callback, args ) { return jQuery.each( this, callback, args ); }, //返回(DOM对象)elem在jQuery对象的位置(仅指数字键) //inArray的参数可以是jQuery对象,也可以是DOM对象 //有点类似数组的indexOf index: function( elem ) { // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem && elem.jquery ? elem[0] : elem , this ); }, //这是个异常复杂的方法 //根据参数判断是读方法还是写方法 attr: function( name, value, type ) { var options = name; // Look for the case where we're accessing a style value if ( typeof name === "string" ) if ( value === undefined ) //读方法,获取相应属性 return this[0] && jQuery[ type || "attr" ]( this[0], name ); else { //写方法,设置相应属性 //一个代理对象 options = {}; options[ name ] = value; } // Check to see if we're setting style values //真正是用其静态方法工作,静态方法的优先级是相当高的,排列如下: //foo.abc() 高于 this.abc() //构造函数内的 this.abc() 高于 原型方法foo.prototype.abc //极晚绑定 fooInstance.abc()是优先级最低 return this.each(function(i){ // Set all the styles for ( name in options ) jQuery.attr( type ? this.style : this, name, jQuery.prop( this, options[ name ], type, i, name ) ); }); },