共计 3619 个字符,预计需要花费 10 分钟才能阅读完成。
jQuery 中 extend 的使用方式大多是这样的:
jQuery.extend({
expando: "jQuery" + (version + Math.random()).replace(/D/g, ""),
isReady: true,
error: function (msg) {
throw new Error(msg);
},
})
jQuery 几乎在每一部分模块代码的下方,都会加上这样的一段代码,来添加操作该模块的属性和方法。
如果去搜索 jQuery.extend 会发现,这个函数还可以这么用:
var udataCur = jQuery.extend({}, udataOld);
var anim = Animation(this, jQuery.extend({}, prop), optall);
这样的使用方法,可不太像是添加扩展了。一个函数不只能实现一个功能?不如去看看,函数内部是怎么实现的,以及这个函数到底能用来干什么。
.extend 源码:
jQuery.extend = jQuery.fn.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;
target = arguments[i] || {};
i++;
}
if (typeof target !== "object" && !isFunction(target)) {
target = {};
}
if (i === length) {
target = this;
i--;
}
for (; i length; i++) {
if ((options = arguments[i]) != null) {
for (name in options) {
copy = options[name];
if (name === "__proto__" || target === copy) {
debugger
continue;
}
if (deep && copy && (jQuery.isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
src = target[name];
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
return target;
};
好家伙,72 行代码实现了一个函数,看看里面都写了点儿啥。
首先,这个函数并没有指定入参,而是通过 arguments 加下标的形式,来动态取参数,从函数内部的注解中也能看出些端倪,注释中有提到 Handle a deep copy situation 即 处理深拷贝的场景,那么目前可知,这个函数,除了能为 jQuery 本身提供相应模块的扩展,还可以对对象进行拷贝
代码最开始部分定义了函数中用到的变量:
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
接下来是一些容错和函数应用何种场景的判断
if (typeof target === "boolean") {
deep = target;
target = arguments[i] || {};
i++;
}
if (typeof target !== "object" && !isFunction(target)) {
target = {};
}
if (i === length) {
target = this;
i--;
}w
再往下,就是函数处理拷贝的相关操作了:
for (; i length; i++) {
if ((options = arguments[i]) != null) {
for (name in options) {
copy = options[name];
if (name === "__proto__" || target === copy) {
continue;
}
if (deep && copy && (jQuery.isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
src = target[name];
if (copyIsArray && !Array.isArray(src)) {
clone = [];
} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
clone = {};
} else {
clone = src;
}
copyIsArray = false;
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
说一下有关深浅拷贝的概念:
浅拷贝,只复制对象内部的基本类型数据,如果对象内部还有对象,则新旧对象共享同一个对象引用
深拷贝,是完全复制一个新对象出来,两个对象间没有任何联系。
写个例子对比一下:
let obj1 = {a: 1, b: {c: 2}};
let obj2 = Object.assign({}, obj1);
obj2.a = 'k'
obj2.b.c = 3
console.log(obj1)
let obj1 = {a: 1, b: {c: 2}};
let clone = function () {
let src = arguments[0]
let target = {}
for (const name in src) {
if (typeof src[name] === 'object') {
src = src[name]
target[name] = clone(src)
} else {
target[name] = src[name]
}
}
return target
}
let obj2 = clone(obj1)
obj2.a = 'k'
obj2.b.c = 3
console.log(obj1)
了解深浅拷贝的概念后,继续回到源代码,在处理拷贝的开始,有一个 for 循环,执行条件为 i
if (deep && copy && (jQuery.isPlainObject(copy) ||
(copyIsArray = Array.isArray(copy)))) {
src = target[name];
if (copyIsArray && !Array.isArray(src)) {
clone = [];;
} else if (!copyIsArray && !jQuery.isPlainObject(src)) {
clone = {};
} else {
clone = src
}
copyIsArray = false;
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy;
}
jQuery 设计的这个 extend 函数,通过 arguments 动态获取参数以及对不同传参类型的判断,来实现了对扩展和拷贝这两种应用场景的重载,其实也不应该算两种情况,因为本身扩展就是一种拷贝行为。
上面代码中有两个 jQuery 提供的工具函数,正好也看看这两个函数是如何实现的
isFunction
var isFunction = function isFunction(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number" &&
typeof obj.item !== "function";
};
isPlainObject
isPlainObject: function (obj) {
var proto, Ctor;
if (!obj || toString.call(obj) !== "[object Object]") {
return false;
}
proto = getProto(obj);
if (!proto) {
return true;
}
Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
},
原文地址: jQuery 的 extend 方法仅仅是字面意思上的扩展吗?