jQuery中的extend方法的作用
- 1.给jQuery的原型和对象扩展方法
- 2.数组和对象的深浅合并
jQuery.extend = jQuery.fn.extend = function () {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if (typeof target === "boolean") {
deep = target;
// Skip the boolean and the target
target = arguments[i] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== "object" && !isFunction(target)) {
target = {};
}
// Extend jQuery itself if only one argument is passed
if (i === length) {
target = this;
i--;
}
for (; i < length; i++) {
// Only deal with non-null/undefined values
if ((options = arguments[i]) != null) {
// Extend the base object
for (name in options) {
copy = options[name];
// Prevent Object.prototype pollution
// Prevent never-ending loop
if (name === "__proto__" || target === copy) {
continue;
}
// Recurse if we're merging plain objects or arrays
if (deep && copy && (jQuery.isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
src = target[name];
// Ensure proper type for the source value
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;
// Never move original objects, clone them
target[name] = jQuery.extend(deep, clone, copy);
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
// Return the modified object
return target;
};
jQuery源码中extend方法jQuery.extend = jQuery.fn.extend = function () {};是这么定义的,jQuery对象和jQuery的实例对象都可以直接调用。根据传入的参数不同,实现的功能不同:
1.向jQuery的对象或原型上扩展方法:
$.extend(function(){}); 是向JQ对象上扩展方法,一般扩展的都是工具类的方法。
$.fn.extend(function(){});是向JQ原型上扩展方法,这些方法都是供实例调用的,一般用来添加一些jQuery插件。
- 浅合并:$.extend(obj1,obj2);
obj2替换obj1,最后返回的是obj1,类似于:Object.assign。
特点:如果obj1和obj2中存在相同属性名的引用数据类型值,obj2中的属性值会直接覆盖obj1中的值;
//浅合并 isPlainObjecteach都是用的jQuery源码中工具方法
var shallowMerge = function shallowMerge(obj1, obj2) {
var isPlain1 = isPlainObject(obj1),
isPlain2 = isPlainObject(obj2);
//判断obj1 和obj2 是否为纯粹的对象,哪个是返回那个
if (!isPlain1) return obj2;
if (!isPlain2) return obj1;
each(obj2, function (key, value) {
//遍历obj2中的每一项替换obj1中属性名相同的值
obj1[key] = value;
});
return obj1;
};
- 深合并:$.extend(true,obj1,obj2);
obj2替换obj1,最后返回的是obj1。
特点:
obj1/obj2都是对象:迭代obj2,依次替换obj1;
obj1不是对象,obj2是对象:obj2替换obj1;
obj1是对象,obj2不是对象:依然以obj1的值为主;
obj1/obj2都不是对象:obj2替换obj1
var deepMerge = function deepMerge(obj1, obj2, cache) {
// 防止对象的循环嵌套导致的死递归问题
//如果cache不是一个数组返回一个空数组
cache = !Array.isArray(cache) ? [] : cache;
//判cache中是否存在obj2 如果存在直接返回obj2(说明obj2这个对象是类似嵌套对象let obj2={};obj2.obj2=obj2)
if (cache.indexOf(obj2) >= 0) return obj2;
//将要合并的对象先存到cache中
cache.push(obj2);
// 正常处理
var isPlain1 = isPlainObject(obj1),
isPlain2 = isPlainObject(obj2);
//只要它俩有一个不是对象,走浅合并的逻辑,不存在引用数据类型值覆盖的问题,如果这俩都不是对象,直接obj2覆盖obj1的值返回即可。
if (!isPlain1 || !isPlain2) return shallowMerge(obj1, obj2);
each(obj2, function (key, value) {
//递归调用,多传递cache参数
obj1[key] = deepMerge(obj1[key], value, cache);
});
return obj1;
};
- 浅克隆
特点:对于引用数据类型的值,克隆出来的对象和原对象用的还是一个堆地址。如果操作某一个值两个对象获取到的值都是改变后的。
实现浅克隆的方法有很多:
let obj = {x:100,y:{name:“XXX”,age:18}};
let obj2 = Object.assign({},obj);
let obj = {x:100,y:{name:“XXX”,age:18}};
let obj2 = {…obj};
/*
* 对象或者数组的浅克隆
*/
var shallowClone = function shallowClone(obj) {
var type = toType(obj),
Ctor = null;
// 其他特殊值的处理
if (obj == null) return obj;
//获取obj的构造函数 如果是正则或date类型new一个新的
Ctor = obj.constructor;
if (/^(regexp|date)$/i.test(type)) return new Ctor(obj);
//symbol和bigint类型返回Object(obj)
if (/^(symbol|bigint)$/i.test(type)) return Object(obj);
//error类型需要特殊处理,new Error(obj.message)
if (/^error$/i.test(type)) return new Ctor(obj.message);
//函数类型给它包一层
if (/^function$/i.test(type)) {
return function anonymous() {
return obj.apply(this, arguments);
};
}
// 数组和纯粹对象,我们基于循环的方案来处理
if (isPlainObject(obj) || type === "array") {
var result = new Ctor();
each(obj, function (key, value) {
result[key] = value;
});
return result;
}
return obj;
};
- 深克隆
特点:克隆出来的对象和原对象完全没关系,不存在引用数据类型指向相同的堆地址。对象中的某个属性值改变时,不会影响其它对象中的属性值。
/*
* 对象或者数组的浅克隆
*/
var deepClone = function deepClone(obj, cache) {
var type = toType(obj),
Ctor = null,
result = null;
//如果不是对象或者不是数组 浅克隆
if (!isPlainObject(obj) && type !== "array") return shallowClone(obj);
// 防止死递归
//如果cache不是一个数组返回一个空数组
cache = !Array.isArray(cache) ? [] : cache;
//判cache中是否存在obj 如果存在直接返回obj(说明obj这个对象是类似嵌套对象let obj={};obj.obj=obj)
if (cache.indexOf(obj) >= 0) return obj;
cache.push(obj);
// 正常的迭代处理
Ctor = obj.constructor;
//获取到obj的构造函数 创造一个新的实例
result = new Ctor();
each(obj, function (key, value) {
//递归调用,直到不存在引用数据类型值为止。
result[key] = deepClone(value, cache);
});
return result;
};
ps:以上代码中涉及到的isPlainObject()、each()、toType()函数均使用的是jQuery源码(二)中的工具类方法。
jQuery源码分析(二)之数据类型检测:https://blog.youkuaiyun.com/Lele___/article/details/111588761