浅拷贝与深拷贝

2023/01/22

一、浅拷贝

创建一个新对象,对源对象的属性进行拷贝,两者共享同一个内存地址。只拷贝第一层属性,如果属性是引用类型(如对象、数组),不会递归复制,而是复制引用地址。

1.常用方法

方法说明
Object.assign()将源对象的属性复制到目标对象
展开运算符 ...简洁语法,浅拷贝对象或数组
arr.slice()浅拷贝数组的一种常见方式
arr.concat()合并数组时也可实现浅拷贝
Object.create()创建一个对象并设置原型(非严格意义上的浅拷贝)

2.使用案例

// 案例1:
var obj = {a:1}
var obj2=Object.create(obj)  // 创建对象
obj2.a=2
console.log(obj ===obj2)   // false
console.log(obj.a,obj2.a)   // 2 2
// 案例2:
var obj = {a:1,b:{c:1}}
var obj2=Object.assign({},obj)  // 合并对象属性。  {...obj}同理
obj.a = 2 
obj.b.c= 2
console.log(obj === obj2)  // false
console.log(obj,obj2)     // obj = {a:2,b:{c:2}}  
// obj2 = {a:1,b:{c:2}}  其中基本数据类型没有改变,引用数据类型改变了

二、深拷贝

创建一个新对象,新对象和原始对象的属性完全相同但是指向不同的地址,修改其中一个不会影响另外一个。拷贝所有层级。

1.常用方法

方法说明
JSON.parse(JSON.stringify(obj))适用于纯数据对象(无函数/undefined/循环引用)
缺点:
会丢失:函数、undefined、Symbol
无法处理:循环引用
时间对象(Date)会变成字符串
cloneDeep(obj)使用第三方库Lodash的cloneDeep:可靠、高性能、支持循环引用、各种边界情况
缺点:引入第三方库,体积略大(可做按需引入)
手写深拷贝函数递归遍历拷贝,可处理循环引用、复杂嵌套,不会丢函数、Symbol
优点:
可处理循环引用、复杂嵌套
不会丢函数、Symbol
缺点:代码复杂、性能一般
structuredClone(obj)现代浏览器中的原生 API,支持循环引用、Map、Set、Date 等,高性能、现代浏览器支持良好
缺点:
不支持函数、DOM 节点
Node.js 需 v17+ 开启 -experimental

2.使用案例

// 1.JSON方法
// JSON.stringify():**序列化,将对象转为JSON字符串
// JSON.parse():**反序列化,将JSON字符串转为对象
var obj2 = JSON.parse(JSON.stringify(obj))

// 2.使用第三方库Lodash的cloneDeep
import cloneDeep from 'lodash/cloneDeep'
const clone = cloneDeep(obj)

// 3.手写深拷贝函数
function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj
  if (map.has(obj)) return map.get(obj)

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

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

  return clone
}

// 4.使用 structuredClone(现代浏览器)
const clone = structuredClone(obj)