浅克隆之所以被称为浅克隆,是因为对象只会被克隆最外部的一层,至于更深层的对象,则毅然是通过引用指向同一块堆内存。
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))
该方法可以解决绝大部分使用场景,但是有以下几个问题:
- 无法实现对函数、RegExp等特殊对象的克隆
- 会抛弃对象的constructor,所有的构造函数会指向Object
- 对象存在循环引用,会报错
针对上面的几个问题,我们来测试一下:
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
