jQuery源码解读2:.extend()函数

一、不防从我们所有认识这个函数的功能入手

1. 这个函数把系列对象合并后返回一个新对象

2. 合并有两种方式,规定在第一个参数上指明,true为深度,false为浅度。

第一个参数在源码中被确定用来表示是否深浅合并:如代码段:

target = arguments[ 0 ] || {},

二、逐行解释源码

//.extend函数是jQuery()对象的方法也是jQuery静态工具函数
jQuery.extend = jQuery.fn.extend = function() 
{
	var options, name, src, copy, copyIsArray, clone,
//默认如果不指定合并类型:深度或者否,默认的目标是第一个参数(target定1)
		target = arguments[ 0 ] || {},
		i = 1,
//巧用不定参,可以让这个函数的功能多样化
		length = arguments.length,
//默认为false,默认不深度递归合并
		deep = false;
	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;
		// Skip the boolean and the target 
//上面把 i = 1 就是要跳过第一参数(当检测到第一个参数为布尔型时)
//如果存在指定合并类型,那先这个目标对象就被改成下个参数(target改1)
		target = arguments[ i ] || {};
//如果存在合并类型且已经确定第一参数为目标对象的话,i++就是开始表明现在可以开始从i++开始找源对象了!
		i++;  
	}
	// Handle case when target is a string or something (possible in deep copy)
//如果传进来的目标不是一个对象而是非对象的其他数据类型(null 、字符串、undefined等)
	if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
		target = {};(target改2)
	}

	// Extend jQuery itself if only one argument is passed
//如果传进来就只有一个参数或者两个参数(其中一个是合并类型布尔型),也就是说传进来的参数只有目标对象无源对象,
//那就修改目标对象为this(虽然上面(target定1)有默认表示第一个参数是目标对象),把第一个对象参数当做源对象。
	if ( i === length ) {
		target = this;//(target改3)
//因为上面误以为有两个参数,且第一参数是合并类型的布尔型,第二个参数是目标对象,上面i++。
//既然是误以为造成的i++,实际只有一个参数就得还原回来!
		i--;  
	}
	for ( ; i < length; i++ ) {
//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
//当前的源对象复制属性copy是普通JavaScript对象或数组,则递归合并
				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
					( copyIsArray = Array.isArray( copy ) ) ) ) {

					if ( copyIsArray ) {
						copyIsArray = false;
//如果原始值src不是数组,则修正为空数组
						clone = src && Array.isArray( src ) ? src : [];

					} else {
//如果原始值src不是普通JavaScript对象,则修正为空对象{}。
						clone = src && jQuery.isPlainObject( src ) ? src : {};
					}

					// Never move original objects, clone them
// 递归调用extend方法,继续进行深度遍历
					target[ name ] = jQuery.extend( deep, clone, copy );  //递归
//注意这里的递归得到的一个完整的对象赋给目标对象的[name]属性。而不是把递归中的属性给到目标对象属性

				// Don't bring in undefined values
//如果不是深度合并
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
//如果不是深度合并,碰到源对象的属性为一个完整的对象,也不去递归了!
				}
			}
		}
	}

	// Return the modified object
	return target;
};

三、注意深度递归合并与非深度合并直接的区别:

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

举个例子就能比较直观的看到问题

//默认的非深度递归
jQuery.extend({ name: “John”, location: { city: “Boston” } }, 
{ last: “Resig”, location: { state: “MA” } } 
); 
// 结果:  => { name: “John”, last: “Resig”, location: { state: “MA” } } 


//深度递归
jQuery.extend( true, 
{ name: “John”, location: { city: “Boston” } }, 
{ last: “Resig”, location: { state: “MA” } } 
); 
// 结果 => { name: “John”, last: “Resig”, location: { city: “Boston”, state: “MA” } } 

四、学到的技术点

1. 采用“不定实参”arguments

2. 

猜你喜欢

转载自my.oschina.net/u/3697586/blog/1549027
今日推荐