前端高频面试题-JavaScript中的浅拷贝和深拷贝

JS中有2种数据类型:基础数据类型和引用数据类型。

一、基础数据类型存在栈中,其拷贝是值拷贝,其实就是深拷贝,如下:a将值10赋给了b,栈中a和b各存了一份值10。
let a = 10
let b = a
a++
console.log(a) //11
console.log(b) //10
二、引用数据类型,变量标识符和堆内存地址存在栈中,堆中存了具体的值,如数组[1,3,4]或者对象{a:1,b:2}。变量根据堆内存地址指向了堆中的值。
const a = [1,3,4]
const b = a //浅拷贝
a[0] = 2
console.log(a) //[2,3,4]
console.log(b) //[2,3,4]

如上,a变量在栈中有一个堆内存地址,比如是0x001,堆中有个值是[1,3,4]就是0x001指向的值。栈中a将堆内存地址0x001赋值给了b,b也指向了堆中的[1,3,4]。所以修改a指向的数组的内容,也会修改b指向的数组的内容。其实这就实现了浅拷贝。

三、引用数据类型的深拷贝实现方式以及优劣、最终实现方式
(1)用ES6中的...实现(不能用于多维数组或对象拷贝)
const a = [1,3,4]
const b = [...a] //这里b就是重新在堆中开辟了一片空间,存放a解构出来的各个元素
a[0] = 2
console.log(a) //[2,3,4]
console.log(b) //[1,3,4]

这个方法很简单,但有个弊端:当源数据是多层数组时,这时就不能实现深拷贝。如下:

const a = [1,3,4,[5,6]]
const b = a 
a[3][0] = 7
console.log(a) //[1,3,4,[7,6]]
console.log(b) //[1,3,4,[7,6]]

这是因为a指向的堆内存中的值看似是二维数组[1,3,4,[5,6]],实际上大数组中的小数组又是一个引用数据类型,保存的是地址,而不是值,即[1,3,4,0x1001],而不是[1,3,4,[5,6]]。 这里的0x1001指向了另一片堆内存空间[5,6]。a赋值给b时,将[1,3,4,0x1001]赋值给了b,b指向的堆中的值中0x1001也指向了这片堆内存空间[5,6]。所以修改a中的小数组,b中的小数组也会变化。

(2)用JSON方法

简单的用JSON.parse(JSON.stringfy())来实现拷贝,能实现多层嵌套的深拷贝,但缺点是当对象中有函数时不能拷贝。

const obj = {
    a:1,
    b:[2,3,[6,7]],
    c:function(){
      console.log(this.a)
  }
}
const obj2 = JSON.parse(JSON.stringify(obj)) 
obj.b[2][0] = 5
console.log(obj2) // {a:1,b:[2,3,[6,7]]},函数没有拷贝过来
console.log(obj) // {a:1,b:[2,5,[5,7]],c:function(){console.log(this.a)}},可以实现多层嵌套拷贝

其实函数的拷贝可以用(1)方法中的ES6中的解构运算符...,如下,obj.c重新定义了一个函数,相当于在堆中重新开辟了一块空间存放新函数。之前的函数和新函数是2块不同的空间值,所以互相不影响。

const obj = {
    a:1,
    b:2,
    c:function(){
      console.log(this.a)
  }
}
const obj2 = {...obj} 
obj.c = function(){
    console.log(this.b)
}
console.log(obj2) // {a:1,b:2,c:function(){console.log(this.a)}},函数拷贝过来了
console.log(obj) // {a:1,b:2,c:function(){console.log(this.b)}} 
(3)终极版实现既可以拷贝多层嵌套结构,又可以拷贝函数的手写深拷贝函数(递归方法)。
function deepClone(oldData){
     if(typeof oldData === 'object' && oldData !== null){ //typeof oldData[key] === 'object'可能是对象,数组和null
           const res = Array.isArray(oldData) ? [] : {} //重新开辟一块堆空间
           for(let key in oldData){
                if(oldData.hasOwnProperty(key)){ //排除原型链上的属性,只保留自身的属性
                     res[key] = deepClone(oldData[key])
           }
         }
         return res
     }
     else{ //基本数据类型直接返回
           return oldData
     }
}

const obj = {
    a:1,
    b:'wo shi yy',
    c:[1,2,[4,5]],
    d(){
        console.log(this.a)
    }
}

const obj2 = deepClone(obj)
obj.c[2][0] = 6
obj.d = function(){
    console.log(this.b)
}
console.log(obj2) //{a:1,b:'wo shi yy',c:[1,2,[4,5]],d(){console.log(this.a)}}
console.log(obj) //{a:1,b:'wo shi yy',c:[1,2,[6,5]],d(){console.log(this.b)}}

小结:1、如果没有嵌套多层结构或对象中包含函数属性,可以直接用...赋值给新数组或者新对象

           2、嵌套多层结构,可以用JSON.parse(JSON.stringify())来实现深拷贝

           3、终极办法:用递归实现。  

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值