返回

lodash深拷贝源码解析,详解浅拷贝与深拷贝!

前端

深入浅出lodash深拷贝源码
在JavaScript中,对象是一个引用类型,这意味着当我们把一个对象赋值给另一个变量时,实际上是将该对象的地址复制给了另一个变量。这意味着任何对该对象的修改都会反映在所有指向该对象的变量上。

这种行为在某些情况下是合理的,但在其他情况下却可能导致意外的结果。例如,如果我们有一个包含敏感数据的对象,并且我们想将该对象复制给另一个变量以便在另一个函数中使用,那么我们就需要确保该副本是独立于原对象的,这样对副本的任何修改都不会影响到原对象。

这就是深拷贝和浅拷贝的区别所在。浅拷贝只复制对象的引用,而深拷贝复制对象的实际值。这意味着对浅拷贝的任何修改都会影响到原对象,而对深拷贝的任何修改都不会影响到原对象。

在JavaScript中,我们可以使用几种不同的方法来实现深拷贝。一种方法是使用Object.assign()方法。Object.assign()方法可以将一个或多个对象的属性复制到另一个对象中。例如,我们可以使用以下代码将对象obj1的属性复制到对象obj2中:

const obj1 = {
  name: 'John Doe',
  age: 30
};

const obj2 = Object.assign({}, obj1);

现在,我们可以对obj2的属性进行修改,而不会影响到obj1的属性。例如,我们可以使用以下代码将obj2age属性改为31:

obj2.age = 31;

现在,obj1age属性仍然是30,而obj2age属性是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()方法。如果有,则调用valueclone()方法进行克隆。如果没有,则函数根据value的类型使用不同的方法进行克隆。

通过以上分析,我们可以看到lodash的深拷贝和浅拷贝函数都非常强大,可以满足我们各种场景下的需求。