必须掌握的深拷贝和浅拷贝

数据类型

拷贝是对数据的操作,所以在了解拷贝之前,我们需要对数据有个简单的认识; 数据分为简单数据类型和引用数据类型,在这里我们更关注的是他们之前的存储区别;

简单数据类型:

  • 存储在栈中,栈内存中存储的就是本身的值;所以对于简单数据类型而言,拷贝的就是原始数据;

引用数据类型:

  • 数据存储在堆内存中,每一个堆内存都会对应一个引用地址,该引用地址存储在栈中;对于引用数据的拷贝是深浅拷贝的主要区分点;

浅拷贝

对于数据类型有了一个简单的认识,可以先来看看浅拷贝;

  • 浅拷贝拷贝数据的时候,对于简单数据类型而言,会在栈内存中新开辟一个空间,拷贝的就是数据本身;
  • 对于引用数据而言,拷贝的就是栈内存中的引用地址,指向同一个堆内存;所以数据之间会相互影响;

模拟实现浅拷贝也有多种的方式:

  • 只拷贝一层;简单来说就是拷贝得到的对象修改简单数据不会影响原对象,修改复杂数据类型会影响原数据;
function shadowClone (obj) {if (obj instanceof Object) {let newObj = obj instanceof Array ? [] : {}for (let i in obj) {newObj[i] = obj[i]}return newObj} else {let a = objreturn a}
}

// 测试
let obj = { name: 'demo', value: [1, 2] }
let newObj = shadowClone(obj)
newObj.name = 'hello'
newObj.value.push(15)
console.log(obj) // { name: 'demo', value: [ 1, 2, 15 ] } 

这种模拟方式就是修改简单数据类型不会影响原数据,修改复杂数据类型,原数据也是被影响;

  • 直接将引用地址复制一份并赋值给新的对象,这种方式其实更接近引用类型数据拷贝的定义;
function shadowClone1 (obj) {if (obj instanceof Object) {let newObj = objreturn newObj} else {let a = objreturn a}
}

// 测试
let obj = { a: 1, b: 2 }
let obj1 = shadowClone1(obj)
obj1.a = 3
console.log(obj) // { a: 3, b: 2 } 

这种模拟方式无论简单数据类型还是引用数据类型,都会影响原数据;

那么在JS中,存在浅拷贝的方法常见的有哪些呢?

  • Object.assign():修改简单还是复杂数据类型,都会影响原数据;
// Object.assing()
let obj = { a: 1, b: [1, 2] }
let newObj = Object.assign({}, obj)
obj.a = 12
obj.b.push(3)
console.log(obj) //{ a: 12, b: [ 1, 2, 3 ] } 
  • Array.prototype.slice():相当于只复制一层,改变引用数据类型会对原数据造成影响;
const arr = [1, 2, { a: 1 }]
const newArr = arr.slice(0)
newArr[2].a = 3
console.log(arr) // [ 1, 2, { a: 3 } ] 
  • Array.prototype.concat():同slice()相当于只复制一层;
const arr = [1, 2, { a: 1 }]
const newArr = arr.concat()
newArr[0] = 12
newArr[2].a = 'tom'
console.log(arr) // [ 1, 2, { a: 'tom' } ] 
  • 使用扩展运算符实现拷贝:相当于只拷贝一层;
const arr = [1, 2, { a: 1 }]
const newArr = [...arr]
newArr[1] = 'hello'
newArr[2].a = 'tom'
console.log(arr) // [ 1, 2, { a: 'tom' } ] 

深拷贝

在项目开发过程中,我们更希望的是数据之间保持独立,避免相互影响,从而提高复用性;很显然浅拷贝不能满足我们的这个需求,这时我们就应该考虑使用深拷贝;

深拷贝简单来说通过拷贝得到的数据,无论是修改简单数据类型还是复杂数据类型的数据,都不会对原数据产生影响;

  • 对于引用类型的数据,深拷贝新开辟了一个堆内存,专门用来存放拷贝后得到的数据;那么栈中保存的引用地址就指向不同的堆内存空间,进而数据之间不会相互影响;

模拟深拷贝的实现(递归):

function deepClone (newObj, oldObj) {for (let i in oldObj) {let item = oldObj[i]if (item instanceof Array) {newObj[i] = []deepClone(newObj[i], item)} else if (item instanceof Object) {newObj[i] = {}deepClone(newObj[i], item)} else {newObj[i] = item}}
}

// 测试
let obj = { a: 1, b: [1, 2] }
let newObj = {}
deepClone(newObj, obj)
newObj.b.push(3)
console.log(obj) // { a: 1, b: [1, 2] } 

JS中常见的深拷贝的方法有哪些?

  • lodash里面的_.cloneDeep()方法;
const newObj = _.cloneDeep(obj) 
  • JQuery里面的extend()方法;
const newObj = $.extend(true, {}, obj)// true为深拷贝,false为浅拷贝 
  • JSON.stringify()方法;
let obj = { a: 1, b: [1, 2] }
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj.b === obj.b) // false

// 这种方式存在弊端,会忽略undefined,symbol和函数
const obj = {
name: 'A',
name1: undefined,
name3: function() {},
name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj2) // {name: "A"} 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值