JavaScript shallowClone or DeepClone?

@spiritree  October 23, 2017

数据类型

  • 基本类型:Undefined、Null、Boolean、Number、String、Symbol

  • 引用类型:Object、Array、Date、RegExp、Function

基本类型是按值访问的,可以操作保存在变量中的实际的值,而引用类型的值是保存在内存中的对象,JavaScript不能直接操作直接访问内存中的位置,也就是不能直接操作对象的内存空间,在操作对象时,实际上操作的是对象的引用,所以,引用类型的值是按引用访问的。

引用类型的数据被重新引用时不会复制数据,而是将指针指向之前的数据地址。

  • Stack 栈:存放基本类型数据和引用类型数据的指针

  • Heap 堆:存放被引用的引用类型数据

如果一个值是引用类型的,那么它的存储空间将从堆中分配。由于引用值的大小会改变,所以不能把它放在栈中,否则会降低变量查寻的速度。 相反,放在变量的栈空间中的值是该对象存储在堆中的地址。地址的大小是固定的,所以把它存储在栈中对变量性能无任何负面影响。

shallowClone

常见的浅复制函数

  • Array.prototype.slice

  • Array.prototype.concat

  • Array.copyWithin()

  • Array.from()

  • Object.assign()

...

特征:改变新复制后的对象属性,原属性也会随之改变。

let oldObj = { name: 'old' }
let newObj = oldObj
newObj.name = 'new'
console.log(oldObj) // {name: "new"} wtf???

DeepClone

为了避免上面的情况,我们要实现深复制,让新复制的对象独立。

1.序列化

function jsonClone(source) {
  return JSON.parse(JSON.stringify(obj))
}

2.第三方库的实现

在这三个常见库中lodash的实现更为完备,花了很多代码来实现ES6引入的新的标准对象,并且对存在环的对象进行针对处理。

上面引用lodash中的_.cloneDeep()是1.0.0的版本,为了大家可以更好地读懂源码。

各个DeepClone方法的比较

特性JSON.parsejQuerylodash
浏览器兼容性IE8+IE6+ (1.x) & IE9+ (2.x/3.x)IE6+ (Compatibility) & IE9+ (Modern)
能够深复制存在环的对象抛出异常 TypeError: Converting circular structure to JSON抛出异常 RangeError: Maximum call stack size exceeded支持
对 Date, RegExp 的深复制支持xx支持
对 ES6 新引入的标准对象的深复制支持xx支持
复制数组的属性xx仅支持RegExp#exec返回的数组结果
复制函数xxx

3.自己实现(层层递归,较简易,不完美)

function deepClone(source) {
   if (!source && typeof source !== 'object') {
     throw new Error('error arguments', 'shallowClone')
   }
   const targetObj = source.constructor === Array ? [] : {}
   for (const keys in source) {
     // 遍历一个对象的所有属性时忽略掉继承属性
     if (source.hasOwnProperty(keys)) {
       if (source[keys] && typeof source[keys] === 'object') {
         targetObj[keys] = source[keys].constructor === Array ? [] : {}
         // 递归
         targetObj[keys] = deepClone(source[keys])
       } else {
         targetObj[keys] = source[keys]
       }
     }
   }
   return targetObj
 }

参考资料

《JavaScript高级程序设计》
你做的拷贝是真的深拷贝吗
深入剖析 JavaScript 的深复制


添加新评论