跳转到内容

深拷贝浅拷贝

一、浅拷贝

浅拷贝是拷贝一层,如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。

下面简单实现一个浅拷贝:

function shallowClone(obj) {
const newObj = {}
for (const key in obj) {
if (obj.hasOwnProperty(key))
newObj[key] = obj[key]
}
return newObj
}

JavaScript 中,存在浅拷贝的现象有:

  • Object.assign()
  • Array.prototype.slice()Array.prototype.concat()
  • 使用拓展运算符实现的复制

二、深拷贝

深拷贝是开辟一个新的栈,两个对象属性完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

常见的深拷贝方式有:

  • _.cloneDeep()
  • jQuery.extend()
  • JSON.stringify()
  • 手写循环递归

_.cloneDeep()

const _ = require('lodash')
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
}
const obj2 = _.cloneDeep(obj1)

jQuery.extend()

const $ = require('jquery')
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
}
const obj2 = $.extend(true, {}, obj1)

JSON.stringify()

const obj2 = JSON.parse(JSON.stringify(obj1))

但是这种方式存在弊端,会忽略 undefinedsymbolfunction

循环递归

function deepClone(obj, hash = new WeakMap()) {
// 如果是 null 或者 undefined 就不进行拷贝操作
if (obj === null)
return obj
if (obj instanceof Date)
return new Date(obj)
if (obj instanceof RegExp)
return new RegExp(obj)
// 如果是普通的值或函数的话是不需要深拷贝
if (typeof obj !== 'object')
return obj
// 是对象的话就要进行深拷贝
if (hash.get(obj))
return hash.get(obj)
const cloneObj = new obj.constructor()
// 找到的是所属类原型上的 constructor,而原型上的 constructor 指向的是当前类本身
hash.set(obj, cloneObj)
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}

三、区别

浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为不一样。

  • 浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象。
  • 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

参考文献