一个很老的话题,只是自己做下记录,免得忘记。写得比较简单还请见谅,有不对的地方也请告知,多谢:) jQuery的版本2.1.1pre
如何生成一个jQuery对象呢?
直接调用$(selector, context)即可,让我们来看看源码都做了什么
// Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' // Need init if jQuery is called (just allow error to be thrown if not included) return new jQuery.fn.init( selector, context ); },
实例化一个jQuery.fn.init对象(别看那么长的名字,就当成是一个function name就行),那在这个构造函数里都做了什么呢?
init = jQuery.fn.init = function( selector, context ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } ........ if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); };
其实就是返回来一个伪数组的自身实例,也就是jQuery.fn.init的实例,这也就是我们常用到的jQuery对象。
那jQuery怎么实现链式语法呢?
我们接着源码往下看,下面这句是关键。
// Give the init function the jQuery prototype for later instantiation init.prototype = jQuery.fn;
也就是jQuery.fn.init的实例都拥有jQuery.fn相应的方法,也就是我们所使用的方法都在这个上面。那jQuery.fn最开始是什么样的?没有jQuery.fn也就没有jQuery.fn.init。
jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: version, constructor: jQuery, // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, toArray: function() { return slice.call( this ); }, ..................... };
这就是他的赋值来,相当于一个Object对象。那怎么给他新增方法呢?看看他给我们提供了什么?
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // skip the boolean and the target target = arguments[ i ] || {}; i++; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; };
通过extend可以扩展方法,其中jQuery.extend是给jQuery扩展方法,这个我的理解他就像一个工具类,比如我们常用的$.each,$.trim等都源自于此。而jQuery.fn.extend则相当于给我们的jQuery对象扩展方法,上下文就是jQuery.fn.init实例对象本身,链式中方法都源自于此。
既然是链式语法,那方法调用后都返回了什么呢?
很显然的就是jQuery对象自身,我们看看removeAttr的实现
jQuery.fn.extend({ attr: function( name, value ) { return access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); } });
jQuery.fn = jQuery.prototype = { ..................... // 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 ); }, ..................... }
从之前的init.prototype = jQuery.fn;可以看出this实际上就是init的实例。
jQuery.extend({ .................................. // args is for internal usage only each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); if ( args ) { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } } return obj; }, ..................... }
直接看返回的结果obj, 他就是之前传入的this参数,也就是jQuery.fn.init的实例对象,removeAttr最后调用this.each返回的就是这个对象。这个对象拥有着jQuery.fn上的所有方法,所以我们还能接着调用jQuery的方法。