前端深拷贝与浅拷贝的实现

1、浅拷贝和深拷贝的定义
1.1、浅拷贝

有两种方式,一种是把一个对象里面的所有的属性值和方法都复制给另一个对象,另一种是直接把一个对象赋给另一个对象,使得两个都指向同一个对象。浅拷贝对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。

1.2、深拷贝

深拷贝是指拷贝对象的具体内容,其内存地址是自主分配的,拷贝结束之后俩个对象虽然存的值是一样的,但是内存地址不一样,俩个对象互相不影响,互不干涉。

数据类型的值传递和地址传递

JavaScript的数据类型可以分为基本数据类型和引用数据类型

基本数据类型有:Number、String、Boolean、Null、Undefined、Symbol
引用数据类型有:Object、Function、Array、Regexp、Date

普通数据类型和对象的存储方式:
   普通数据类型一般是存储在栈中的,而对象一般是存储在堆中的,同时在栈中存放了一个地址数据指向堆中的数据。当调用对象时,首先会获取栈中的地址,根据该地址去堆中找到该对象数据。

针对普通类型,在赋值过程中一般是值传递

//Number
var num1=12;
var num2=num1;
num2=34;
console.log(num1,num2);//12,34
//String
var str1="abc";
var str2=str1;
str2="def";
console.log(str1,str2);//abc,def


针对引用类型中的对象和数组,在赋值过程中一般是地址传递

//对象
var obj1 = { name: 'jack' }; 
var obj2 = obj1; //复制obj1的引用
obj2.name = 'mary';
//此时obj1和obj2全都指向一个地址,修改地址值,obj1和obj2的值都会改变
console.log(obj1.name, obj2.name);//mary,mary
// []
a = ['a', 'b', 'c']; b = a; b[1] = 'd';
console.log(a, b)


2、浅拷贝实现的具体方式
2.1、数字方法slice()


var arr1 = [
        { number: 1 },
        { number: 2 },
        { number: 3 }
];
var arr2 = arr1.slice();//复制arr1在栈中的引用地址
arr2[0].number = 9//将复制过来的引用地址的指向修改为9,所以arr和arr2都会改变
console.log(arr1[0].number, arr2[0].number);//9,9

原因:

arr2拷贝了arr1在栈内存中的地址,也就是arr2和arr1共用存储在堆内存的数据;同理得,当arr2发生改变的时候,arr1也会随之改变。



2.2、Object.assign

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

var arr1 = [//两层数据
        { number: 1 },
        { number: 2 },
        { number: 3 }
];
var obj1 = { name: 'jack' }//一层数据
var obj2 = Object.assign({}, obj1);//此时obj1为一层数据,复制的是对象中的属性值
obj2.name = 'mary'
var arr2= Object.assign({}, arr1)//复制的是数组中的对象在栈中的的引用地址
arr2[0].number = 99
console.log(obj1.name, obj2.name, arr1[0].number, arr2[0].number);//jack mary 99 99


2.3、for…in…

通过for…in…遍历对象将源对象的值拷贝到另一个对象上,并返回另一个对象。浅拷贝只能遍历一层对象,当数据是嵌套对象则需要深拷贝。

var qianCopy = function (obj) {
            var result= obj.constructor();//声明参数的一个空构造函数
            // 遍历对象中的属性及方法
            for (var key in obj) {
                // 判断对象有没有相应的key
                if (obj.hasOwnProperty(key)) {
                    // 把对象相应key对象的值赋值给另外一个对象相应的key对应的值
                    result[key] = obj[key];
                }
            }
            return result
}


var obj1 = { name: 'jack' }; 
var obj2 = qianCopy(obj1); //复制obj1的引用
obj2.name = 'mary';
//此时obj1和obj2全都指向一个地址,修改地址值,obj1和obj2的值都会改变
console.log(obj1.name, obj2.name);//jack,mary


3、深拷贝的具体实现
3.1、JSON.parse(JSON.stringify())

JSON.parse(JSON.stringify())一般用于深拷贝:JSON.stringify()将JS对象序列化转化为JSON字符串,再利用JSON.parse()反序列化,将JSON字符串还原成对象。该方法只能用于可枚举属性。

var arr1 = [
      { number: 1 },
      { number: 2 },
      { number: 3 }
];
var arr2 = JSON.parse(JSON.stringfy(arr1))//此时是将arr1中所有属性的值复制给arr2,两者相互独立
arr2[0].number = 199
console.log(arr1[0].number, arr2[0].number);//1,199   


当然JSON.parse(JSON.stringify())并不是一种完美实现深拷贝的方法,存在以下缺陷:

1.当深拷贝对象为函数、Undefined时,会把函数、Undefined丢失,无法拷贝。
2.当深拷贝对象为Regexp、Error对象,序列化结果为空对象
3.当深拷贝对象中存在NaN、infinity、-infinity时,序列化会变成Null


3.2、递归

面试题:使用递归完成深拷贝对象

function shenCopy(obj){
    //创建参数的构造函数对象
    var result=obj.constructor();
    if(typeof obj!==Object){//当传入参数不为Object类型时,直接返回该参数
        return obj;
    }else{
        for(let key in obj){
            //递归核心:将嵌套对象递归得到的返回值赋予新建的构造对象
            result[key]=shenCopy(obj[key]);
        }
    }
    return result;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

圆周率呀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值