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);
// Don't bring in undefined values // 如果拷贝值为null/undefined 则不拷贝
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
};