lodash深拷贝源码解析,详解浅拷贝与深拷贝!
2024-02-02 15:11:08
深入浅出lodash深拷贝源码
在JavaScript中,对象是一个引用类型,这意味着当我们把一个对象赋值给另一个变量时,实际上是将该对象的地址复制给了另一个变量。这意味着任何对该对象的修改都会反映在所有指向该对象的变量上。
这种行为在某些情况下是合理的,但在其他情况下却可能导致意外的结果。例如,如果我们有一个包含敏感数据的对象,并且我们想将该对象复制给另一个变量以便在另一个函数中使用,那么我们就需要确保该副本是独立于原对象的,这样对副本的任何修改都不会影响到原对象。
这就是深拷贝和浅拷贝的区别所在。浅拷贝只复制对象的引用,而深拷贝复制对象的实际值。这意味着对浅拷贝的任何修改都会影响到原对象,而对深拷贝的任何修改都不会影响到原对象。
在JavaScript中,我们可以使用几种不同的方法来实现深拷贝。一种方法是使用Object.assign()
方法。Object.assign()
方法可以将一个或多个对象的属性复制到另一个对象中。例如,我们可以使用以下代码将对象obj1
的属性复制到对象obj2
中:
const obj1 = {
name: 'John Doe',
age: 30
};
const obj2 = Object.assign({}, obj1);
现在,我们可以对obj2
的属性进行修改,而不会影响到obj1
的属性。例如,我们可以使用以下代码将obj2
的age
属性改为31:
obj2.age = 31;
现在,obj1
的age
属性仍然是30,而obj2
的age
属性是31。
另一种实现深拷贝的方法是使用递归函数。递归函数可以遍历对象及其所有子对象,并为每个子对象创建一个新的副本。例如,我们可以使用以下代码实现一个递归的深拷贝函数:
function deepCopy(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(deepCopy);
}
const newObj = {};
for (const key in obj) {
newObj[key] = deepCopy(obj[key]);
}
return newObj;
}
这个函数可以对任何对象进行深拷贝,包括数组、对象和嵌套对象。
现在,我们已经了解了浅拷贝和深拷贝的概念和实现方法,我们就可以看看lodash是如何实现深浅拷贝的。
Lodash是一个JavaScript库,提供了许多有用的函数,包括深拷贝和浅拷贝函数。Lodash的深拷贝函数叫做cloneDeep()
,浅拷贝函数叫做clone()
。
cloneDeep()
函数使用递归函数来实现深拷贝,而clone()
函数使用Object.assign()
方法来实现浅拷贝。
以下是cloneDeep()
函数的源码:
function cloneDeep(value) {
if (!isPlainObject(value) || isBuffer(value)) {
return value;
}
const isArr = Array.isArray(value);
const result = (isArr ? new value.constructor(value.length) : {});
for (const key in value) {
if (hasOwnProperty.call(value, key)) {
result[key] = cloneDeep(value[key]);
}
}
return result;
}
我们可以看到,cloneDeep()
函数首先检查value
是否是一个普通对象或一个缓冲区。如果不是,则直接返回value
。否则,如果value
是一个数组,则创建一个新的数组并将其长度设置为value
的长度。然后,函数遍历value
的所有属性,并对每个属性调用cloneDeep()
函数进行深拷贝。最后,函数返回结果对象。
以下是clone()
函数的源码:
function clone(value) {
if (value == null) {
return value;
}
// 将 `value` 视为 `Cloneable`。
const isCloneable = typeof value.clone === 'function';
// 克隆 `value`。
if (isCloneable) {
return value.clone();
}
// 克隆 `value` 的简单对象或数组。
const result =
isArray(value) ? value.slice() : isArguments(value) ? slice.call(value) : deepAssign({}, value);
// 克隆 `value` 的类实例。
if (isHostObject(value)) {
deepAssign(result, value);
}
// 克隆 `value` 的 DOM 元素。
if (isElement(value)) {
return value.cloneNode(true);
}
// 克隆 `value` 的 Map 或 Set。
if (isSet(value) || isMap(value)) {
constructorClass = value.constructor;
return new constructorClass(value);
}
// 克隆 `value` 的 `RegExp`。
if (isRegExp(value)) {
return new RegExp(value.source, regexpFlags.call(value));
}
// 克隆 `value` 的 `Date`。
if (isDate(value)) {
return new Date(value.valueOf());
}
// 克隆 `value` 的 `Error`。
if (isError(value)) {
const result = new value.constructor(value.message);
result.name = value.name;
return result;
}
// 克隆 `value` 的 `Symbol`。
if (isSymbol(value)) {
return symbolValueOf.call(value);
}
return result;
}
我们可以看到,clone()
函数首先检查value
是否为null
。如果是,则直接返回value
。否则,函数检查value
是否具有clone()
方法。如果有,则调用value
的clone()
方法进行克隆。如果没有,则函数根据value
的类型使用不同的方法进行克隆。
通过以上分析,我们可以看到lodash的深拷贝和浅拷贝函数都非常强大,可以满足我们各种场景下的需求。