Javascript中的深拷贝和浅拷贝 , 点赞加关注持续更新~


在讲解深拷贝和浅拷贝之前先来了解一下 Object.assign()和递归函数,后面会用到

一、Object.assign()

Object.assign()是一个用于将所有可枚举的自身属性从一个或多个源对象复制到目标对象的方法,返回目标对象。它主要用于合并对象,或者克隆对象。

基本语法如下:

Object.assign(target, ...sources)

其中:

  • target 是目标对象,也是最后返回的对象。
  • ...sources 是一个或多个源对象。

这里有一个简单的例子:

let obj1 = { a: 1 };  
let obj2 = { b: 2 };  
let obj3 = { c: 3 };  
  
let obj = Object.assign({}, obj1, obj2, obj3);  
  
console.log(obj);  // { a: 1, b: 2, c: 3 }  
console.log(obj1); // { a: 1 }, 属性 'b' 和 'c' 没有复制到 obj1  
console.log(obj2); // { b: 2 }, 属性 'a' 和 'c' 没有复制到 obj2  
console.log(obj3); // { c: 3 }, 属性 'a' 和 'b' 没有复制到 obj3

在这个例子中,Object.assign()方法将obj1obj2obj3对象的所有可枚举的自身属性复制到空对象中,并返回这个空对象。需要注意的是,如果源对象的某个属性同名于目标对象已有的属性,那么,该属性将不会被复制到目标对象中。

二、递归函数

  • 如果一个函数在内部可以调用自己本身,那么这个函数就是递归函数
  • 递归函数的作用和循环效果类似
  • 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出案件,否则进入“死循环”

以下是setTimeout()利用递归函数模拟实现setInterval()的效果:

function getTime() {
      document.querySelector('div').innerHTML = new Date().toLocaleString()
      setTimeout(getTime, 1000)
    }
    getTime()

三、深浅拷贝

  • 基本类型存在栈里面。引用类型存在堆里面
  • 基本类型赋值传的是值,相当于把值复制一份给接受变量,改变接受变量的值不会影响原变量
  • 引用类型赋值传的是地址,将地址传给接受的变量,改变接受变量值的同时会改变原变量

深拷贝和浅拷贝只针对引用数据类型,因为简单数据类型赋值给接受变量,改变接受变量的值不会影响原变量

1、浅拷贝

浅拷贝:拷贝的是地址,对于简单数据类型是直接将值复制一份给接受变量,但是对于复杂数据类型还是将地址传过去,对于含有对象的对象,浅拷贝就会出问题 (简单理解: 如果是单层对象,没问题,如果有多层就有问题)

常见方法:

  1. 拷贝对象:拷贝字符串方法Object.assgin() ; 展开运算符 {...obj}
  2. 拷贝数组:连接concat();截取slice() ; 展开运算符[...arr]
 const obj = {
      uname: 'jack',
      age: 18,
    }
const o = { ...obj }
console.log(o)   // {uname:'jack',age:'18'}
o.age = 20
console.log(o)  // {uname:'jack',age:'20'}
console.log(obj)  // {uname:'jack',age:'18'}
  // 这样就解决了会改变源对象属性的问题

浅拷贝会出现的问题:

浅拷贝,因为拷贝的是复杂数据类型的地址,

 const obj = {
      uname: 'jack',
      age: 18,
      family: {
        baby: '小jack'
      }
    }
 
    const o = {}
    Object.assign(o, obj)
    o.age = 20   // 改变拷贝后对象中的属性age的值
    o.family.baby = '老jack' // 改变拷贝后对象中的对象的属性baby的值
    console.log(o)   // { uname:'jack',age:'20',family:{baby:'老jack'}} 拷贝后的对象
    console.log(obj)  // { uname:'jack',age:'18',family:{baby:'老jack'}} 原对象

上面中的代码就发现了问题,改变浅拷贝后对象的属性值,不会影响原对象。但是改变对象中的对象的属性值,会影响原对象。为了解决这个问题,所以我们有了更深层次的拷贝,所以有了下面的深拷贝。

2、深拷贝

2.1利用JSON.stringifyJSON.parse进行深拷贝

​ 缺点 忽略函数、属性值为undefined的属性

const obj = {
        name: 'longge',
        age: 16,
        arr: [],
        o: { a: 1, b: 2 },
      }
      const newObj = JSON.parse(JSON.stringify(obj))
      console.log(newObj)

2.2自己使用递归实现深度拷贝:

  • 深拷贝会用到函数递归
  • 普通拷贝直接赋值即可
  • 如果用到的是对象形式,再次利用递归将对象解决
  • 先筛选Array后筛选object,因为数组也属于对象

以下是个简单的自己利用递归实现的深度拷贝:

      // 定义一个对象,名为obj,包含四个属性:uname、age、hobby和family,其中family对象中又包含一个baby属性
const obj = {
      uname: 'jack', // 属性uname的值是'jack'
      age: 18, // 属性age的值是18
      hobby: ['乒乓球', '足球'], // 属性hobby的值是一个数组,包含两个字符串元素:'乒乓球'和'足球'
      family: { // 属性family的值是一个对象
        baby: '小jack' // family对象中的baby属性的值是'小jack'
      }
    }
// 定义一个空对象,名为o
const o = {}
// 定义一个函数,名为deepCopy,接受两个参数:newObj和oldObj
function deepCopy(newObj, oldObj) {
      debugger // 使用debugger关键字,可以在JavaScript运行时创建一个断点,方便进行调试
      for (let k in oldObj) { // 遍历oldObj对象的每一个属性
        // 如果属性k的值是一个数组
        if (oldObj[k] instanceof Array) {
          // 在newObj中创建一个空数组,名为k
          newObj[k] = []
          // 对这个新创建的数组进行深度复制
          deepCopy(newObj[k], oldObj[k])
        // 如果属性k的值是一个对象
        } else if (oldObj[k] instanceof Object) {
          // 在newObj中创建一个空对象,名为k
          newObj[k] = {}
          // 对这个新创建的对象进行深度复制
          deepCopy(newObj[k], oldObj[k])
        // 如果属性k的值不是数组也不是对象
        } else {
          // 将oldObj的属性k赋值给newObj的属性k,实现浅复制
          newObj[k] = oldObj[k]
        }
      }
    }
// 对o和obj进行深度复制,复制结果存储在o中
deepCopy(o, obj)
// 在控制台打印对象o的内容
console.log(o)
// 修改o的age属性为20
o.age = 20
// 修改o的hobby属性,将第一个元素修改为'篮球'
o.hobby[0] = '篮球'
// 修改o的family属性的baby属性为'老jack'
o.family.baby = '老jack'
// 在控制台打印原始对象obj的内容,可以看到obj并未因为对o的修改而改变
console.log(obj)

2.3使用第三方库Lodash实现深拷贝

可以使用第三方封装的库进行深拷贝,其中就有Lodash,具体使用可以参考官网的使用文档:Lodash中文文档

  • 31
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十七同志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值