jquery.extend

312 阅读2分钟

jquery extend

实现extend 所需要的功能性函数

// 判断是不是函数
function isFunction(obj) {
    // Support: Chrome <=57, Firefox <=52
    /* 
    	在有些浏览器 typeof document.createElement( "object" ) 会返回function 
    	所以判断是不是dom 节点
    */
    return typeof obj === "function" && typeof obj.nodeType !== "number";
};
// 创建一个计划对象
let class2type = {};
// 代理 hasOwnProperty 访问Object.hasOwnProperty的时候可以节省代码
let hasOwn = class2type.hasOwnProperty;
// hasOwn toString方法 注意是函数的toString方法,而不是{}toString 方法
let fnToString = hasOwn.toString;
// Object() 函数转成字符串 "function Object() { [native code] }"
let ObjectFunctionString = fnToString.call(Object);
// 代理
let getProto = Object.getPrototypeOf;
// 拷贝计划对象方法
toString = class2type.toString;

// 判断是否为计划对象
/* 
    //在当前页面内追加换行标签和指定的HTML内容
    function w( html ){
    	document.body.innerHTML += "<br/>" + html;
    }
    
    w( $.isPlainObject( { } ) ); // true
    w( $.isPlainObject( new Object() ) ); // true
    w( $.isPlainObject( { name: "CodePlayer"} ) ); // true
    w( $.isPlainObject( { sayHi: function(){} } ) ); // true
    w( $.isPlainObject( "CodePlayer" ) ); // false
    w( $.isPlainObject( true ) ); // false
    w( $.isPlainObject( 12 ) ); // false
    w( $.isPlainObject( [ ] ) ); // false
    w( $.isPlainObject( function(){ } ) ); // false
    w( $.isPlainObject( document.location ) ); // false(在IE中返回true)
    
    function Person(){
    	this.name = "张三";
    }
    w( $.isPlainObject( new Person() ) ); // false
    window false
    new Date false
*/
function isPlainObject(obj) {
    var proto, Ctor;
    // obj false,或者obj不是对象,排除null和undefined 否则getProto(obj) 报错
    if (!obj || toString.call(obj) !== "[object Object]") {
    	return false;
    }
    proto = getProto(obj);
    // 如果是对象但是没有原型则是由 Object.create( null ) 创建
    if (!proto) {
    	return true;
    }
    // 原型的构造函数是 Object(),则为计划对象
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
}

extend 代码分析

// extend 代码分析
var jQuery = {};
/* 
	jQuery.extend()函数用于将一个或多个对象的内容合并到目标对象。
	jQuery.extend( [ deep ], target , object1 [, objectN... ] )
*/
jQuery.extend = jQuery.prototype.extend = function () {
    // 定义后边用到的变量
    var options, name, src, copy, copyIsArray, clone,
    	// 第一个参数为目标对象
    	target = arguments[0] || {},
    	i = 1,
    	length = arguments.length,
    	// 是否为深拷贝
    	deep = false;
    
    // 深度拷贝
    /* 如果第一个参数为布尔值则代表深拷贝 */
    if (typeof target === "boolean") {
    	deep = target;
    
    	// Skip the boolean and the target 将第二个参数设置为目标对象
    	target = arguments[i] || {};
    	i++;
    }
    
    // 参数不是对象的情况
    if (typeof target !== "object" && !isFunction(target)) {
    	target = {};
    }
    
    // 如果只有一个参数或者两个参数且第一个参数为 deep  扩展自身
    if (i === length) {
    	target = this;
    	i--;
    }
    
    for (; i < length; i++) {
    
    	// 参数不是null 或者undefined
    	/* 
    		null == undefined // true
    		null == null // true
    	*/
    	if ((options = arguments[i]) != null) {
    
    		// 扩展对象
    		for (name in options) {
    			src = target[name];
    			copy = options[name];
    			/*
    				target = {a:1,b:2}
    				options = {
    					test: target
    				}
    				target.test = target; //会出现无法遍历
    			*/
    			if (target === copy) {
    				continue;
    			}
    			// 判断是深拷贝,且copy为数组或者计划对象
    			/* 
    				计划对象
    				{ } new Object()
    			*/
    			if (deep && copy && (jQuery.isPlainObject(copy) ||
    				(copyIsArray = Array.isArray(copy)))) {
    				// 数组
    				if (copyIsArray) {
    					copyIsArray = false;
    					clone = src && Array.isArray(src) ? src : [];
    
    				} else {
    					// 对象
    					clone = src && jQuery.isPlainObject(src) ? src : {};
    				}
    
    				// Never move original objects, clone them
    				target[name] = jQuery.extend(deep, clone, copy);
    
    				// 如果拷贝值为null/undefined 则不拷贝
    			} else if (copy !== undefined) {
    				target[name] = copy;
    			}
    		}
    	}
    }
    
    return target;
};