Skip to content

浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则毅然是通过引用指向同一块堆内存。

js
// 浅克隆函数
function shallowClone(object) {
  const obj = {}
  for (let k in object) {
    obj[k] = object[k]
  }
  return obj
}

// 被克隆对象
const oldObject = {
  a: 1,
  b: ['e', 'f', 'g'],
  c: { h: { i: 2 } }
}

const newObj = shallowClone(oldObject)
console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }
console.log(oldObj.c.h === newObj.c.h); // true

我们可以看到,虽然oldObj.c.h被克隆了,但是它还与oldObj.c.h相等,这表明它们毅然指向同一段堆内存,这就造成了如果对newObj.c.h进行修改,也会影响oldObj.c.h。若在生产中运用会遇到问题。

当然还有 Object.assign() 方法,但是该方法在第一层为深克隆,第二层对象以后的还是浅克隆会遇到上面的问题。

深克隆

JSON.parse 这是最简单的一种实现方式

const newObj = JSON.parse(JSON.stringify(oldObj))

该方法可以解决绝大部分使用场景,但是有以下几个问题:

  1. 无法实现对函数、RegExp等特殊对象的克隆
  2. 会抛弃对象的constructor,所有的构造函数会指向Object
  3. 对象存在循环引用,会报错

针对上面的几个问题,我们来测试一下:

js
function Person(pname) {
  this.name = pname
}
const Mi = new Person('Mi')

function say() {console.log('hi')}

const oldObj = {
  a: say,
  b: new Array(1),
  c: new RegExp('ab+c', 'i'),
  d: Mi
}
const newObj = JSON.parse(JSON.stringify(oldObj))
// 无法复制函数
console.log(newObj.a, oldObj.a); // undefined [Function: say]
// 稀疏数组复制错误
console.log(newObj.b[0], oldObj.b[0]); // null undefined
// 无法复制正则对象
console.log(newObj.c, oldObj.c); // {} /ab+c/i
// 构造函数指向错误
console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]

深克隆

见deepClone.js

共 20 个模块,1301 篇 Markdown 文档。