jQuery源码解析1(Utilities)

本文深入解析了jQuery 1.3.2版本的源码,详细介绍了其核心结构、工具方法和扩展机制。通过分析,读者可以更好地理解jQuery的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用jquery两年多了,  我最早是使用的是prototype库,后来使用mootools, 直到后来弟弟推荐我使用jquery(他是个UI设计师),就一直用到现在。 我是被它的简洁和精炼所吸引的。

 

我喜欢看源代码,以前开发eclipse rcp时,就喜欢看eclipse的源码,因为有时候API文档并不那么明了,eclipse优秀的架构配合JDT强大的功能,让我们很容易地看懂。 一个设计优秀的产品,它的代码应该也是易于理解的。 现在我做web用的是php的ci,在开发过程中也是时常查看它的源代码。

 

为了更好地使用jquery, 所以我阅读了jquery的源代码。不敢独享,所以写出来,相互讨论,一起进步。

 

我使用的是 1.3.2

 

我们从开始的地方开始

 

Js代码 复制代码
  1. (function() {   
  2.  ...       
  3. })();  

 

所有代码包含在以上的函数中,避免名字冲突。

 

1. 几个"全局变量"

 

Java代码 复制代码
  1. var    
  2.     window = this,   
  3.     undefined,    
  4.     _jQuery = window.jQuery,    
  5.     _$ = window.$,   
  6.   
  7.     jQuery = window.jQuery = window.$ = function(selector, context) {   
  8.         return new jQuery.fn.init(selector, context);   
  9.     },   
  10.   
  11.     // A simple way to check for HTML strings or ID strings   
  12.     quickExpr = /^[^<]*(<(.|/s)+>)[^>]*$|^#([/w-]+)$/,   
  13.     // Is it a simple selector   
  14.     isSimple = /^.[^:#/[/.,]*$/;  

var 
	window = this,
	undefined, 
	_jQuery = window.jQuery, 
	_$ = window.$,

	jQuery = window.jQuery = window.$ = function(selector, context) {
		return new jQuery.fn.init(selector, context);
	},

	// A simple way to check for HTML strings or ID strings
	quickExpr = /^[^<]*(<(.|/s)+>)[^>]*$|^#([/w-]+)$/,
	// Is it a simple selector
	isSimple = /^.[^:#/[/.,]*$/;

 

1.1

 

 window 还是那个window, undefined 还是那个undefined,只是这样让js 解释器更快地找到他们,提高速度。

 

 _jQuery = window.jQuery

_$ = window.$,

 

保存原来的 jQuery 和 $, 因为默认情况下jquery占用名字空间 jQuery 和$

 

搜索一下使用 _jQuery的地方吧:

 

 

Java代码 复制代码
  1. jQuery.extend({   
  2.     noConflict: function( deep ) {   
  3.         window.$ = _$;   
  4.   
  5.         if ( deep )   
  6.             window.jQuery = _jQuery;   
  7.   
  8.         return jQuery;   
  9.     },  

jQuery.extend({
	noConflict: function( deep ) {
		window.$ = _$;

		if ( deep )
			window.jQuery = _jQuery;

		return jQuery;
	},

 

OK, 是为了 noConflict

 

比如我们要同时使用prototype和jquery, 可以先让html页面包含 prototype.js 再包含 jquery.js。

 

然后调用  jq = jQuery.noConflict();   接着就可以用jq来使用jQuery框架, $ 就变回 prototype.js 中的方法了。

 

还有一个deep参数, 可以让另一个jQuery(可能不怎么有名, 但对你的项目有用) 和 现在的jQuery 共存。

 

 

quickExpr是一个正则表达式, 用于快速匹配一个 html tag 或 id 字符串(如 '#id')

isSimple: 也是一个与此同时表达式, 用于匹配一个 selector (css 选择器)

 

1.2

 

 

Java代码 复制代码
  1. jQuery = window.jQuery = window.$ = function(selector, context) {   
  2.     return new jQuery.fn.init(selector, context);   
  3. },  

	jQuery = window.jQuery = window.$ = function(selector, context) {
		return new jQuery.fn.init(selector, context);
	},

 

我们思考: jQuery.fn.init 是构造函数, 那它的实例方法应该由 : jQuery.fn.init.prototype 指定。

 

搜索它, 在541行得到以下代码:

 

 

Java代码 复制代码
  1. jQuery.fn.init.prototype = jQuery.fn;  

jQuery.fn.init.prototype = jQuery.fn;

 

所以 我们平常使用 $('#abc') 或 jQuery('#abc') 

 

$ 就是 jQuery,它返回一个对象。 这个对象的构造函数是 jQuery.fn.init(...

 

这个对象的实例方法和字段就是 jQuery.fn 的成员。

 

 

Java代码 复制代码
  1. jQuery.fn = jQuery.prototype = {   
  2.     init: function( selector, context ) {   
  3.     ...   
  4.     },   
  5.     size: function() {   
  6.         return this.length;   
  7.     },   
  8.         ...  

jQuery.fn = jQuery.prototype = {
	init: function( selector, context ) {
	...
	},
	size: function() {
		return this.length;
	},
        ...

 

从以上可以看出,下面的代码等效。

 

var a = $('#id'); // a = new jQuery('#id');

a.size();

 

 

上面我们隐约看到了jQuery的插件结构, 你可以添加方法到 jQuery.fn,就可以在jQuery对象中使用。

 

但首先,我们先看几个通用工具方法,因为其他很多部分使用它们。

 

2. 几个工具方法

 

2.1  jQuery.extend = jQuery.fn.extend = function() {

 

我们分两部分看这个方法:首先是准备, 然后是拷贝。

 

准备:

 

Js代码 复制代码
  1. jQuery.extend = jQuery.fn.extend = function() {   
  2.     var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;   
  3.   
  4.     if ( typeof target === "boolean" ) {   
  5.         deep = target;   
  6.         target = arguments[1] || {};   
  7.   
  8.         i = 2;   
  9.     }   
  10.   
  11.     if ( typeof target !== "object" && !jQuery.isFunction(target) )   
  12.         target = {};   
  13.   
  14.     if ( length == i ) {   
  15.         target = this;   
  16.         --i;   
  17.     }  

 

可以看到,第一个参数可以指定为 boolean , 表示: 是否进行 深拷贝

 

如果是多于一个 object,那么第一个object 为 target,它将被其他的 object 的成员扩展

如果仅有一个object, 那么 this (jQuery 或 jQuery.fn) 为 target,  object 的成员将添加到 target中

 

 

我们常常这样使用:

 

Js代码 复制代码
  1. var b = $.extend({}, a);  // 复制一份   
  2.   
  3. options = $.extend({ 'name''editor', value: '100' }, options); // 默认参数   
  4.   
  5.   
  6. $.extend({   
  7.   
  8.   myfunc: function() {...  // 为jquery添加 工具方法   
  9.   
  10. });   
  11.   
  12.   
  13. $.fn.extend({   
  14.   
  15.   myplugin: function() {...  // 为jquery添加插件   
  16.   
  17. })  

 

 

另一部分的拷贝代码也很容易:

 

Js代码 复制代码
  1. for ( ; i < length; i++ )   
  2.     if ( (options = arguments[ i ]) != null )   
  3.         for ( var name in options ) {   
  4.             var src = target[ name ], copy = options[ name ];   
  5.   
  6.             if ( target === copy )   
  7.                 continue;   
  8.   
  9.             if ( deep && copy && typeof copy === "object" && !copy.nodeType )   
  10.                 target[ name ] = jQuery.extend( deep,    
  11.                     src || ( copy.length != null ? [ ] : { } )   
  12.                 , copy );   
  13.   
  14.             else if ( copy !== undefined )   
  15.                 target[ name ] = copy;   
  16.   
  17.         }   
  18.   
  19. return target;  

 

2.2  each(object, callback)

 

 

Js代码 复制代码
  1. // args is for internal usage only   
  2. each: function( object, callback, args ) {   
  3.     var name, i = 0, length = object.length;   
  4.   
  5. ..  

 

 

我们只使用到两个参数,args 内部使用。

 

Java代码 复制代码
  1. if ( args ) {   
  2.     if ( length === undefined ) {   
  3.         for ( name in object )   
  4.             if ( callback.apply( object[ name ], args ) === false )   
  5.                 break;   
  6.     } else  
  7.         for ( ; i < length; )   
  8.             if ( callback.apply( object[ i++ ], args ) === false )   
  9.                 break;   
  10.   
  11.     // A special, fast, case for the most common use of each   
  12. else {  

		if ( args ) {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.apply( object[ name ], args ) === false )
						break;
			} else
				for ( ; i < length; )
					if ( callback.apply( object[ i++ ], args ) === false )
						break;

			// A special, fast, case for the most common use of each
		} else {

 

这一部分是是框架内部使用。我们else部分:

 

Java代码 复制代码
  1. else {   
  2.     if ( length === undefined ) {   
  3.         for ( name in object )   
  4.             if ( callback.call( object[ name ], name, object[ name ] ) === false )   
  5.                 break;   
  6.     } else  
  7.         for ( var value = object[0];   
  8.             i < length && callback.call( value, i, value ) !== false;    
  9.             value = object[++i] ){}  

		} else {
			if ( length === undefined ) {
				for ( name in object )
					if ( callback.call( object[ name ], name, object[ name ] ) === false )
						break;
			} else
				for ( var value = object[0];
					i < length && callback.call( value, i, value ) !== false; 
					value = object[++i] ){}

 

 从代码中,我们知道,可以对“数组”或“对象”进行迭代。

 

Java代码 复制代码
  1. var a = ['a''b''c''d''e'];   
  2.   
  3. $.each(a, function() {   
  4.     alert(this);  // this即每次迭代的值   
  5. });   
  6.   
  7. $.each(a, function(index, value) {   
  8.   // ...   
  9. });   
  10.   
  11. $.each(a, function() {   
  12.    if (this == 'c') {   
  13.     return false;    // 'break'   
  14.   }   
  15. }  

var a = ['a', 'b', 'c', 'd', 'e'];

$.each(a, function() {
    alert(this);  // this即每次迭代的值
});

$.each(a, function(index, value) {
  // ...
});

$.each(a, function() {
   if (this == 'c') {
    return false;    // 'break'
  }
}

 

最后,each 返回 object 以支持链式调用

 

 

 

 

2.3  isFunction(obj),  isArray(obj), isXMLDoc(elem)

 

 

 

注: isXMLDoc 没有收录在API文档中, 所以不要使用。

 

 

 

Java代码 复制代码
  1. // See test/unit/core.js for details concerning isFunction.   
  2. // Since version 1.3, DOM methods and functions like alert   
  3. // aren't supported. They return false on IE (#2968).   
  4. isFunction: function( obj ) {   
  5.     return toString.call(obj) === "[object Function]";   
  6. },   
  7.   
  8. isArray: function( obj ) {   
  9.     return toString.call(obj) === "[object Array]";   
  10. },   
  11.   
  12. // check if an element is in a (or is an) XML document   
  13. isXMLDoc: function( elem ) {   
  14.     return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||   
  15.         !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );   
  16. },  

	// See test/unit/core.js for details concerning isFunction.
	// Since version 1.3, DOM methods and functions like alert
	// aren't supported. They return false on IE (#2968).
	isFunction: function( obj ) {
		return toString.call(obj) === "[object Function]";
	},

	isArray: function( obj ) {
		return toString.call(obj) === "[object Array]";
	},

	// check if an element is in a (or is an) XML document
	isXMLDoc: function( elem ) {
		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument );
	},

 

2.4  trim(text)

 

 

这个太熟悉不过了:

 

 

Java代码 复制代码
  1. trim: function( text ) {   
  2.     return (text || "").replace( /^/s+|/s+$/g, "" );   
  3. },  

	trim: function( text ) {
		return (text || "").replace( /^/s+|/s+$/g, "" );
	},

 

没有什么特殊的技巧, 使用简单的正则替换。

 

prototype.js是这样的:

 

Js代码 复制代码
  1. strip: function() {   
  2.   return this.replace(/^/s+/, '').replace(//s+$/, '');   
  3. },  

 

2.5  grep(array, callback, insert),  map(array, callback)

 

Js代码 复制代码
  1. grep: function( elems, callback, inv ) {   
  2.     var ret = [];   
  3.     for ( var i = 0, length = elems.length; i < length; i++ )   
  4.         if ( !inv != !callback( elems[ i ], i ) )   
  5.             ret.push( elems[ i ] );   
  6.   
  7.     return ret;   
  8. },  

 

所以我们这样使用:

 

Js代码 复制代码
  1. var a = [2, 3, 6, 1, 8, 9];   
  2.   
  3. $.grep(a, function(v) {  //   只要>= 3的数   
  4.     return v >= 3;   
  5. });   
  6.   
  7. $.grep(a, function(v) {   
  8.     return v >= 3;   
  9. }, false); // 这样相当于 < 3  

 

下面是map

 

Java代码 复制代码
  1. map: function( elems, callback ) {   
  2.     var ret = [];   
  3.     for ( var i = 0, length = elems.length; i < length; i++ ) {   
  4.         var value = callback( elems[ i ], i );   
  5.   
  6.         if ( value != null )   
  7.             ret[ ret.length ] = value;   
  8.     }   
  9.   
  10.     return ret.concat.apply( [], ret );   
  11. }  

	map: function( elems, callback ) {
		var ret = [];
		for ( var i = 0, length = elems.length; i < length; i++ ) {
			var value = callback( elems[ i ], i );

			if ( value != null )
				ret[ ret.length ] = value;
		}

		return ret.concat.apply( [], ret );
	}

 

最后一句话是干嘛? return ret.concat.apply( [], ret );

上面的相当于:  return [].concat(ret[0], ret[1], ...);

 

var a = [].concat(1, 2, 3, [4, 5]) ; // a = [1, 2, 3, 4, 5];

 

原来这样是让函数更灵活, 让我们可以这样使用:

 

Js代码 复制代码
  1. a = [1, 2, 3, 4];   
  2.   
  3. b = $.map(a, function(v) {   
  4.     return [v, 2 * v];   
  5. });   
  6.   
  7. // b = [1, 2, 2, 4, 3, 6, 4, 8];  

 

返回值为null 会被过滤

 

 

 

 

 

---

 

2.6 makeArray(obj),  inArray(value, array)

 

Js代码 复制代码
  1. makeArray: function( array ) {   
  2.     var ret = [];   
  3.   
  4.     if( array != null ){   
  5.         var i = array.length;   
  6.         // The window, strings (and functions) also have 'length'   
  7.         if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )   
  8.             ret[0] = array;   
  9.         else  
  10.             while( i )   
  11.                 ret[--i] = array[i];   
  12.     }   
  13.   
  14.     return ret;   
  15. },  

 

 

如果没有length属性,或者是 string, function window 之一, 就返回一个元素的数组。

 

 

 

Js代码 复制代码
  1. inArray: function( elem, array ) {   
  2.     for ( var i = 0, length = array.length; i < length; i++ )   
  3.     // Use === because on IE, window == document   
  4.         if ( array[ i ] === elem )   
  5.             return i;   
  6.   
  7.     return -1;   
  8. },  

 

如果元素elm在 array中,返回位置,否则返回-1

 

  2.7 merge(first, second)

 

 

 

Js代码 复制代码
  1. merge: function( first, second ) {   
  2.     // We have to loop this way because IE & Opera overwrite the length   
  3.     // expando of getElementsByTagName   
  4.     var i = 0, elem, pos = first.length;   
  5.     // Also, we need to make sure that the correct elements are being returned   
  6.     // (IE returns comment nodes in a '*' query)   
  7.     if ( !jQuery.support.getAll ) {   
  8.         while ( (elem = second[ i++ ]) != null )   
  9.             if ( elem.nodeType != 8 )   
  10.                 first[ pos++ ] = elem;   
  11.   
  12.     } else  
  13.         while ( (elem = second[ i++ ]) != null )   
  14.             first[ pos++ ] = elem;   
  15.   
  16.     return first;   
  17. },  

 

但是我不知道 jQuery.support.getAll 是哪里来的?

 

 

只有 jQuery.support.objectAll, 这里会不会是笔误?

 

 

 整理一下先:

 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值