JS 浅拷贝和深拷贝


前置了解


当对象的属性是 object 类型时,其实该属性保存的只是 object 的内存地址,除非将该属性重新指向了其他 object,
否则对该属性的所有操作,都会通过该地址找到真正的内存区域并在其中操作。


浅拷贝


浅拷贝: object 类型的属性保存的是该 object 的内存地址,所以复制到目标对象的也是该 object 的内存地址,
代表着源对象和目标对象的该属性指向的是同一块内存地址,因为对 object 属性的所有操作都是在其保存地址的
内存区域内进行,所以源对象和目标对象的 object 类型属性会互相影响。


浅拷贝方式:浅拷贝的方式有很多, 我个人比较喜欢用 Object.assign(target, source) 或 扩展符的方式。

1. 适合浅拷贝的场景


源对象的属性全部是基本类型时, 适合浅拷贝。
请添加图片描述
上面浅拷贝演示过程的对应代码:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>拷贝</title>
</head>
<body>
<script>
  // 源对象
  let source = {
    name: 'ares5k',
    age: 27
  }
  // 目标对象
  let target = {}
  // 浅拷贝
  Object.assign(target, source)
</script>
</body>
</html>

2. 不适合浅拷贝的场景


源对象的属性包含 object 类型,且该 object 的属性变化时,
不想让源对象和目标对象互相影响,这种场合不适合浅拷贝。

请添加图片描述

演示过程分析:
(1) 源对象的 info 属性是 object 类型, 所以 info 属性保存的是该 object 的内存地址 0x0002。
(2) 浅拷贝后,目标对象复制过来的也是该 object 的内存地址 0x0002。
(3) 对 info 属性的 sex 属性进行操作时,实际上是在内存地址为 0x0002 的内存区域上进行的。
(4) 因为源对象和目标对象的 info 属性保存的都是 0x0002,所以源对象和目标对象都会收到影响。


上面浅拷贝演示过程的对应代码:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>拷贝</title>
</head>
<body>
<script>
  // 源对象
  let source = {
    name: 'ares5k',
    info: {
      sex: '男'
    }
  }
  // 浅拷贝
  let target = {...source}
  target.name = '修改性名'
  target.info.sex = '修改性别'

  // 比较
  console.log(source.name === target.name) // 结果: false,基本类型修改不会影响源对象
  console.log(source.info.sex === target.info.sex) // 结果: true,对象类型修改会影响源对象
</script>
</body>
</html>

深拷贝


源对象的属性中,有 object 类型时,适合使用深拷贝。
在此介绍两种深拷贝方式,一个是利用内置的 JSON 序列化函数,另一个是自定义深克隆函数。
请添加图片描述

1. 内置的 JSON 序列化函数


缺点:不能复制函数,适合只有属性的场合

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>拷贝</title>
</head>
<body>
<script>
  // 源对象
  let source = {
    name: 'ares5k',
    info: {
      sex: '男'
    },
    say(){
      console.log('Hello!')
    }
  }
  // 深克隆
  let target = JSON.parse(JSON.stringify(source))
  target.name = '修改性名'
  target.info.sex = '修改性别'

  // 比较
  console.log(source.name === target.name) // 结果: false,基本类型修改不会影响源对象
  console.log(source.info.sex === target.info.sex) // 结果: false,对象类型修改不会影响源对象
  console.log(target) // 新对象内没有 say() 函数
</script>
</body>
</html>

2. 自定义深拷贝函数


自定义函数:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>拷贝</title>
</head>
<body>
<script>
  function deepClone(source) {
    // 防止循环引用
    let dependsAddressMap = new Map()
    function doDeepClone(source) {
      // 浅拷贝
      let target = Array.isArray(source) ? [...source] : typeof source === 'object' ? {...source} : source
      if (target === source) {
        return target
      }
      // 如果处理过直接返回对象,防止循环引用
      if (dependsAddressMap.get(source)) {
        return dependsAddressMap.get(source)
      }
      dependsAddressMap.set(source, target)
      // 深拷贝
      Reflect.ownKeys(source).forEach(key => {
        if (typeof source[key] === 'object') {
          target[key] = doDeepClone(source[key])
        }
      })
      return target
    }
    return doDeepClone(source);
  }
</script>
</body>
</html>

使用上边自定义的深拷贝函数,来看看一下效果:

 // 源对象
  let source = {
    name: 'ares5k',
    info: {
      sex: '男'
    },
    say(){
      console.log('Hello!')
    }
  }
  // 深克隆
  let target = deepClone(source)
  target.name = '修改性名'
  target.info.sex = '修改性别'

  // 比较
  console.log(source.name === target.name) // 结果: false,基本类型修改不会影响源对象
  console.log(source.info.sex === target.info.sex) // 结果: false,对象类型修改不会影响源对象
  console.log(source) // 对象结构和值正常
  console.log(target) // 对象结构和值正常
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值