深拷贝和浅拷贝的区别?如何实现深拷贝?

0 阅读2分钟

一、什么是浅拷贝(Shallow Copy)

浅拷贝:只复制对象的第一层,嵌套对象仍然共享引用。

示例

const obj = {
  name: "Tom",
  address: {
    city: "Beijing"
  }
}

const copy = { ...obj }

copy.address.city = "Shanghai"

console.log(obj.address.city) // Shanghai

原因:

obj
 ├ name
 └ address → 指向同一个内存

浅拷贝只复制:

obj.address 的引用

二、什么是深拷贝(Deep Copy)

深拷贝:递归复制对象所有层级,生成完全独立的新对象。

示例

const obj = {
  name: "Tom",
  address: {
    city: "Beijing"
  }
}

const copy = deepClone(obj)

copy.address.city = "Shanghai"

console.log(obj.address.city) // Beijing

结构:

obj.address !== copy.address

三、浅拷贝常见实现方式

1 Object.assign

const copy = Object.assign({}, obj)

2 展开运算符

const copy = { ...obj }

3 数组浅拷贝

const arr = [1,2,3]

const copy1 = arr.slice()
const copy2 = arr.concat()
const copy3 = [...arr]

四、深拷贝常见方法

方法1 JSON序列化(最常见)

const copy = JSON.parse(JSON.stringify(obj))

优点:

简单

缺点:

不能拷贝:

function
undefined
Symbol
Date
RegExp
循环引用

示例:

const obj = {
  fn: () => {}
}

JSON.parse(JSON.stringify(obj)) // fn丢失

方法2 structuredClone(现代方法 ⭐推荐)

浏览器原生 API

const copy = structuredClone(obj)

优点:

支持
Map
Set
Date
ArrayBuffer

缺点:

旧浏览器不支持

方法3 lodash

使用 Lodash

import _ from "lodash"

const copy = _.cloneDeep(obj)

优点:

稳定
支持复杂对象

方法4 手写深拷贝(面试最爱)

function deepClone(obj) {

  if (obj === null || typeof obj !== "object") {
    return obj
  }

  const copy = Array.isArray(obj) ? [] : {}

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepClone(obj[key])
    }
  }

  return copy
}

五、解决循环引用(高级面试)

如果对象这样:

const obj = {}
obj.self = obj

普通递归会:

无限递归

解决:使用 WeakMap

function deepClone(obj, hash = new WeakMap()) {

  if (obj === null || typeof obj !== "object") {
    return obj
  }

  if (hash.has(obj)) {
    return hash.get(obj)
  }

  const clone = Array.isArray(obj) ? [] : {}

  hash.set(obj, clone)

  for (let key in obj) {
    clone[key] = deepClone(obj[key], hash)
  }

  return clone
}

六、浅拷贝 vs 深拷贝总结

对比浅拷贝深拷贝
拷贝层级第一层所有层
引用对象共享独立
性能
实现简单复杂

七、真实开发怎么选?

一般:

浅拷贝 90%
深拷贝 10%

原因:

React / Vue 都鼓励 不可变数据更新

例如:

setState({
  ...state,
  user: {
    ...state.user,
    name: "Tom"
  }
})

八、面试标准回答(背这个)

可以这样回答:

浅拷贝只复制对象的第一层属性,如果属性值是引用类型,则复制的是引用地址,因此修改嵌套对象会影响原对象。
深拷贝会递归复制对象的所有层级,生成一个完全独立的新对象。

常见浅拷贝方式有 Object.assign 和展开运算符。
深拷贝可以使用 JSON.parse(JSON.stringify)、structuredClone、lodash 的 cloneDeep,或者手写递归实现,并通过 WeakMap 解决循环引用问题。