JavaScript 浅拷贝与深拷贝实现(分享)

1. 浅拷贝原理

  • 浅拷贝是拷贝的引用
  • 一般情况下,浅拷贝只会拷贝一层
  • 层级结构特别深的话,如果拷贝的不是最深层数据的值,都为浅拷贝

2. 浅拷贝实现

// 通过for in实现浅拷贝
// 注意:for in会遍历原型上面的属性,这里就不考虑这种情况
let obj = {
  name: "小红",
  age: 18,
  hobby: {
    a: "aaa",
    b: "bbb"
  }
}
let newObj = {}
for (let i in obj) {
  newObj[i] = obj[i]
}
// 到这一步的时候,newObj 和 obj 中的内容是一样的

newObj.name = "小黄"
console.log(newObj.name); // 小黄
console.log(obj.name); // 小红
// 很明显第一层数据拷贝的是值,改变newObj的时候,obj没有改变

newObj.hobby.a = "AAA"
console.log(newObj.hobby.a); // AAA
console.log(obj.hobby.a); // AAA
// 修改第第二层的时候,发现是拷贝的引用,所以这两个对象中的 hobby 这个属性是同一个引用

3. 深拷贝原理

  • 深拷贝是拷贝的值
  • 为基本数据类型的话就自接拷贝,引用数据类型的话就进入深层去拷贝
  • 因为引用数据类型是对基本数据类型的存储方式

4. 深拷贝实现

// 准备数据,下面的深拷贝方法都是来拷贝这个对象
let obj = {
  num: 123,
  str: 123,
  bool: true,
  unde: undefined,
  nul: null,
  nan: NaN,
  infin: Infinity,
  sym: Symbol(1),
  date: new Date(),
  reg: new RegExp(/asdfsd/),
  fn() {
    let i = 10
  },
  arr: [1, 2, 3, 4],
  obj: {
    a: "111",
    b: "222"
  },
  set: new Set([1, 2, 3, 4]),
  map: new Map([
    ["aaa", "AAA"],
    ["bbb", "BBB"]
  ])
}
4.1 乞丐版深拷贝
// 如下图,乞丐版深拷贝虽然很简单,但是会丢失和改变很多类型
let newObj = JSON.parse(JSON.stringify(obj))

在这里插入图片描述

  • 拷贝的对象中的函数、undefined、symbol 这几种类型,序列化之后的字符串中这个键值对会消失
  • 拷贝 Date 引用类型会变成字符串;
  • 无法拷贝不可枚举的属性;
  • 无法拷贝对象的原型链;
  • 拷贝 RegExp、Set、Map引用类型会变成空对象;
  • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  • 无法拷贝对象的循环应用(对象内部含有外部的引用)
  • BigInt不能被序列化,会报错
4.2 递归实现深拷贝
// 如下图,这个方法已经可以实现大部分类型的拷贝,还需要针对不同的数据类型做准备
function deepCopy(target) {
  let result = Array.isArray(target) ? [] : {}
  for (let i in target) {
    if (typeof target[i] === "object" && target[i] !== null) {
      result[i] = deepCopy(target[i])
    } else {
      result[i] = target[i]
    }
  }
  return result
}

在这里插入图片描述

4.3 递归升级版
// 这种方法对大多数不太好拷贝的引用数据类型进行了处理,一般面试中会写一个基础版的递归就行
// 但是基础版存在的问题要知道
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)

const deepClone = function (obj, hash = new WeakMap()) {
  if (obj.constructor === Date)
    return new Date(obj)       // 日期对象直接返回一个新的日期对象
  if (obj.constructor === RegExp)
    return new RegExp(obj)     //正则对象直接返回一个新的正则对象
  //如果循环引用了就用 weakMap 来解决
  if (hash.has(obj)) return hash.get(obj)
  let allDesc = Object.getOwnPropertyDescriptors(obj)

  //遍历传入参数所有键的特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  //继承原型链
  hash.set(obj, cloneObj)
  for (let key of Reflect.ownKeys(obj)) {
    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }
  return cloneObj
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值