jQuery源码分析 整体框架部分及部分常用方法

最近尝试看看jQuery的源码。 版本 version = "1.11.1"

 

相对于看一本jQuery如何使用的书,看jQuery源码对它可以有更深层次的理解。jQuery中大量使用了正则表达式,对于全面提升JavaScript的能力也有很大帮助。由于考虑到了各种边界情况,以及对不同版本的浏览器的兼容等,jQuery整体代码都很严谨。

前言

    jQuery源码中大量使用了&&和||  ,在JS中它们更像操作数选择器,它们的返回值是两个操作数的一个,而不是我们通常认为的返回true或者false。

     var a=3;
     var b='cc';
     var c=null;

     console.log(a||b);   //3
     console.log(a&&b);   //cc

     console.log(c||b);   //cc
     console.log(c&&b);   //null

&&和||会先对第一个操作数(也就是a||b中的a)做条件判断。

对于||来说如果条件判断结果为true,则返回第一个操作数的值,如果为false,则返回第二个操作数的值。

对于&&来说如果条件判断结果为true,则返回第二个操作数的值,如果为false,则返回第一个操作数的值。
JavaScript中的假值有(undefined、null、false、+0、-0、NaN、“”)

一 整体框架

去掉其他枝叶代码,先来看看jQuery的整体框架

(function( global, factory ) {

	if ( typeof module === "object" && typeof module.exports === "object" ) {
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "jQuery requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
	
	var jQuery = function( selector, context ) {
		return new jQuery.fn.init( selector, context );
	};
	
	jQuery.fn = jQuery.prototype = {
	 //.........
	}
	
	var init = jQuery.fn.init = function( selector, context ) {
	//.........
	}
	
	init.prototype = jQuery.fn;
	
	
	if ( typeof noGlobal === strundefined ) {
	window.jQuery = window.$ = jQuery;
    }


return jQuery;

}));

       可以看到jQuery整体是一个立即执行函数,JavaScript是函数作用域而非块作用域,因此其中定义的变量不会污染全局空间。if ( typeof module .......){} else  这部分代码用于检测代码运行环境,如果 module === "object"  说明是在CommonJS或者类似于CommonJS的环境中,使用 module.exports 向外暴露API。但是这样的环境中可能不存在window 对象,如果不存在window对象,就向外提供一个function,可以额外传递window对象作为参数。 在一般浏览器环境中则直接运行factory( global )运行。最后window.jQuery = window.$ = jQuery; 向外部暴露jQuery。

 再来看看jQuery的初始化部分。也就是下面这部分。

var jQuery = function( selector, context ) {
		return new jQuery.fn.init( selector, context );
	};
	
	jQuery.fn = jQuery.prototype = {
	 //.........
	}
	
	var init = jQuery.fn.init = function( selector, context ) {
	//.........
	}
	
	init.prototype = jQuery.fn;

 我们很熟练的用类似于$('#cc') $('div')的方式创建jQuery对象,可以看到它返回了new jQuery.fn.init( selector, context ); 使用new实例化的一个对象,由于jQuery.prototype=jQuery.fn=jQuery.fn.init.prototype  因此挂载到jQuery实例上面的方法(定义在jQuery.prototype上的)也就挂载到了使用new jQuery.fn.init()创建的实例对象上。 jQuery.fn.init=jQuery.prototype .init 因此jQuery的实例也可以访问到init方法。

 

 

二  链式操作及pushStack

我们在使用jQuery是常常进行一些链式的操作比如这样:$('li').eq(1).css('background','red').end().length

$('li')获取到文档中所有的li, $('li').eq(1).css('background','red')取得第一个li,然后将它的背景色设置为红色,调用end()回到原来的状态,最后length获取文本中li的个数。

 

下面是一些常用的实例方法:get 、eq、end 

jQuery.fn = jQuery.prototype = {

	get: function( num ) {
		return num != null ?
			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
			slice.call( this );
	},

	pushStack: function( elems ) {

		var ret = jQuery.merge( this.constructor(), elems );
		ret.prevObject = this;
		ret.context = this.context;
		return ret;
	},
	first: function() {
		return this.eq( 0 );
	},

	last: function() {
		return this.eq( -1 );
	},

	eq: function( i ) {
		var len = this.length,
			j = +i + ( i < 0 ? len : 0 );
		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
	},

	end: function() {
		return this.prevObject || this.constructor(null);
	}
};

在理解代码之前先看看$('li')长什么样吧,就长下面这样,是一个类数组。包含了所有满足筛选条件的DOM对象,所以我们可以用$('li')[0]这样的方式获取到对应的DOM。

使用$('li').eq(0)可以获取到对应的jQuery对象,其中pushStack 在jQuery的源码中多次使用到。pushStack做了这些事:创建一个新的jQuery对象,将一个DOM数组的内容整合到里面,保存当前的环境和前一个jQuery对象。 因此在调用end的时候就可以由

prevObject属性找到前一个对象并返回。

 

三 extend扩展

       使用jQuery.extend 可以用于扩展jQuery的静态方法。使用 jQuery.fn.extend 可用于扩展jQuery的实例方法。第一个参数是true的话代表深拷贝。如果没有提供目标对象的话则扩展自身,否则扩展目标对象。

jQuery.extend = jQuery.fn.extend = function() {
	var src, copyIsArray, copy, name, options, clone,
		target = arguments[0] || {},
		i = 1,
		length = arguments.length,
		deep = false;

	
	if ( typeof target === "boolean" ) {
		deep = target;

		// 跳过 boolean 和target
		target = arguments[ i ] || {};
		i++;
	}

	// 排除target是string等情况
	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
		target = {};
	}

	// 扩展JQuery本身,如果只传递了一个参数
	if ( i === length ) {
		target = this;
		i--;
	}

	for ( ; i < length; i++ ) {
		//只处理非null/undefined 值
		if ( (options = arguments[ i ]) != null ) {
			// Extend the base object
			for ( name in options ) {
				src = target[ name ];
				copy = options[ name ];

				// 有环
				if ( target === copy ) {
					continue;
				}

				// 递归如果我们合并普通对象或数组
				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 : {};
					}

					target[ name ] = jQuery.extend( deep, clone, copy );

				// 排除掉 undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
};

     深拷贝指的是对象属性所引用的对象全部进行新建对象复制,以保证深复制的对象不包含任何原有对象及原对象的引用。在进行深拷贝时,如果子属性是数组或者对象,就进行递归调用直到全部拷贝完毕。

     jQuery.extend 和jQuery.fn.extend 使用同一个方法。如果要扩展自身的话,可以通过target = this区别出是对jQuery这个function进行扩展,还是对jQuery的一个实例进行扩展。

四 一些静态方法

jquery自身扩展的一些静态方法,比如jQuery.each 、 jQuery.map等都是常用的方法。这部分的源码都比较好理解,基本上了解这部分可以更加熟练地利用jQuery了。jQuery.merge函数用于合并两个数组内容到第一个数组。在jQuery内部也经常用到这个方法。

jQuery.extend({
	each: function( obj, callback, args ) {
		//....................
	},

	
	merge: function( first, second ) {
		var len = +second.length,
			j = 0,
			i = first.length;

		while ( j < len ) {
			first[ i++ ] = second[ j++ ];
		}

		// Support: IE<9
		// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
		if ( len !== len ) {
			while ( second[j] !== undefined ) {
				first[ i++ ] = second[ j++ ];
			}
		}

		first.length = i;

		return first;
	},

	map: function( elems, callback, arg ) {
	   //..........................
	}

});

五 冲突检测

无论如何引入jQuery的js包都会在window中创建一个全局的jQuery和$。如果我们自己的代码中定义了同名的全局变量的话,就会引起冲突,可以通过jQuery.noConflict(true)来避免冲突。

var
	_jQuery = window.jQuery,

	_$ = window.$;

jQuery.noConflict = function( deep ) {
	if ( window.$ === jQuery ) {
		window.$ = _$;
	}

	if ( deep && window.jQuery === jQuery ) {
		window.jQuery = _jQuery;
	}

	return jQuery;
};

在window.jQuery = window.$ = jQuery之前,使用变量_jQuery、_$ 存储当前环境的同名变量。在调用noConflict 之后,还原 window.$ = _$; 

 

 

 

 

 

 

 

 

        

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值