返回

Javascript 克隆的那些事

前端

Javascript 中的克隆(拷贝)是一个非常重要的操作,而且我们经常会用到它。克隆就是将一个对象里的属性、方法等复制到另一个对象中,而且互不影响(即克隆之后,对一个对象进行修改,不会影响到另一个对象)。今天我们就来讨论一下原生 Javascript 中克隆的问题。

我们现在想把 obj 里面的每一个属性拷贝到一个空对象 v 中:

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

const v = {};

for (const key in obj) {
  v[key] = obj[key];
}

console.log(v); // {name: 'John', age: 30}

这样就可以实现克隆了,但是这种方法存在一个问题:它不能克隆对象中的嵌套对象或数组。如果 obj 中有一个嵌套对象,那么 v 中的对应属性将是一个指向原始嵌套对象的引用,而不是它的副本。

const obj = {
  name: 'John',
  age: 30,
  address: {
    street: 'Main Street',
    number: 123,
  },
};

const v = {};

for (const key in obj) {
  v[key] = obj[key];
}

console.log(v); // {name: 'John', age: 30, address: {street: 'Main Street', number: 123}}

v.address.street = 'New Street';

console.log(obj); // {name: 'John', age: 30, address: {street: 'New Street', number: 123}}

正如你所看到的,当我们修改 v.address.street 时,obj.address.street 也随之改变了。这是因为 v.address 指向的是 obj.address 的同一个引用。

为了解决这个问题,我们需要使用递归克隆。递归克隆会遍历对象中的每个属性,如果该属性是一个对象或数组,则会递归地克隆它。

const deepClone = (obj) => {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  if (Array.isArray(obj)) {
    return obj.map(deepClone);
  }

  const clonedObj = {};
  for (const key in obj) {
    clonedObj[key] = deepClone(obj[key]);
  }

  return clonedObj;
};

使用递归克隆,我们可以正确地克隆嵌套对象和数组:

const obj = {
  name: 'John',
  age: 30,
  address: {
    street: 'Main Street',
    number: 123,
  },
};

const v = deepClone(obj);

console.log(v); // {name: 'John', age: 30, address: {street: 'Main Street', number: 123}}

v.address.street = 'New Street';

console.log(obj); // {name: 'John', age: 30, address: {street: 'Main Street', number: 123}}

现在,当我们修改 v.address.street 时,obj.address.street 不会改变,因为它们是两个不同的对象。

希望本文能帮助你理解 Javascript 中的克隆问题。如果你有任何疑问,请随时留言。