【JavaScript】深拷贝、浅拷贝


JavaScript的变量类型

基本类型: 5种基本数据类型UndefinedNullBooleanNumberString变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。

var a = 1;
b = a; // 栈内存会开辟一个新的内存空间,此时b和a都是相互独立的
b = 2;
console.log(a); // 1

引用类型: 存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
在这里插入图片描述


浅拷贝与深拷贝

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。

JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。

如何区分深拷贝与浅拷贝:简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;如果B没变,那就是深拷贝。

浅拷贝

  • 仅仅是指向被拷贝的内存地址,如果原地址中对象被改变了,那么浅拷贝出来的对象也会相应改变。新旧对象还是共享同一块内存。

深拷贝

  • 会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

实现浅拷贝的方式

for…in只循环第一层

/* 只复制一层的浅拷贝 */
function simpleCopy(obj1) {
    //判断传入的参数是数组还是对象
    //如果是数组,返回的也是数组; 如果是对象则返回对象
    var obj2 = Array.isArray(obj1) ? [] : {};
    for(let i in obj1) {
        obj2[i] = obj1[i]; 
    }
    return obj2
}

只拷贝一层浅拷贝:
拷贝的对象的第一层属性修改时,不会影响原对象
但是拷贝的对象的包括第二层属性之内的属性修改时,会影响原对象

测试数组:

// 测试数组
let arr1 = [1,[2,6],3,4];
let arr2 = simpleCopy(arr1);
console.log('未改变属性之前的数组:');
console.log('待拷贝的数组:',arr1);
console.log('拷贝的数组:',arr2);

arr2[0] = 0;
arr2[1][1] = 10;
console.log('改变属性之前的数组:');
console.log('待拷贝的数组:',arr1);
console.log('拷贝的数组:',arr2);

在这里插入图片描述
测试对象:

//测试对象

// 定义对象obj1,一会对obj1进行浅复制
var obj1 = {
    a: 1,
    b: 2,
    c: {
        d: 3
    }
}


// 对obj1进行浅拷贝
let obj2 = simpleCopy(obj1);


//更改obj2的属性的值
obj2.a = 3;
obj2.c.d = 4;
console.log('obj1.a:',obj1.a);
console.log('obj2.a:',obj2.a);
console.log('obj1.c.d:',obj1.c.d);
console.log('obj2.c.d:',obj2.c.d);

在这里插入图片描述

Object.assign()方法

// 定义对象obj1,一会对obj1进行浅复制
var obj1 = {
    a: 1,
    b: 2,
    c: {
        d: 3
    }
}

// 对obj1进行浅拷贝
var obj2 = Object.assign(obj1);

//更改obj2的属性的值
obj2.a = 3;
obj2.c.d = 4;
console.log('obj1.a:',obj1.a);
console.log('obj2.a:',obj2.a);
console.log('obj1.c.d:',obj1.c.d);
console.log('obj2.c.d:',obj2.c.d);

完全的浅拷贝---->拷贝对象的每一层属性改变都会引起原对象属性的改变
注意与上一个只拷贝的一层进行对比

在这里插入图片描述

直接用=赋值

// 定义对象obj1,一会对obj1进行浅复制
var obj1 = {
    a: 1,
    b: 2,
    c: {
        d: 3
    }
}

// 对obj1进行浅拷贝
var obj2 = obj1

//更改obj2的属性的值
obj2.a = 3;
obj2.c.d = 4;
console.log('obj1.a:',obj1.a);
console.log('obj2.a:',obj2.a);
console.log('obj1.c.d:',obj1.c.d);
console.log('obj2.c.d:',obj2.c.d);

拷贝对象时与使用Object.assign()的结果一致

在这里插入图片描述

//待拷贝的数组
let arr1 = [0,1,2,3,4];

//进行拷贝
let arr2 = arr1;

console.log(arr1===arr2);

//改变拷贝的对象的值
arr1[0] = 1;
console.log(arr1,arr2);

在这里插入图片描述

用slice(start,end)实现对数组的浅拷贝

  • Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
  • 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
  • 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝

var arr1 = ["1","2","3"]; 
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );

用concat()实现对数组的浅拷贝

  • 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
  • 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝

var arr1 = ["1","2","3"];
var arr2 = arr1.concat();
arr2[1] = "9";
console.log("数组的原始值:" + arr1 );
console.log("数组的新值:" + arr2 );

var arr1 = [{a:1},{b:2},{c:3}];
var arr2 = arr1.concat();
arr2[0].a = "9";
console.log("数组的原始值:" + arr1[0].a ); // 数组的原始值:9
console.log("数组的新值:" + arr2[0].a ); // 数组的新值:9

使用扩展运算符…实现浅拷贝

  • 当value是基本数据类型,比如String,Number,Boolean时,是可以使用拓展运算符进行深拷贝的
  • 当value是引用类型的值,比如Object,Array,引用类型进行深拷贝也只是拷贝了引用地址,所以属于浅拷贝。
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }

实现深拷贝的方式

采用递归去拷贝所有层级属性

/* 采用递归进行所有层级拷贝 */
// 注:typeof [1,2,3]----->object
function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if(obj && typeof obj === 'object') {
        for(let key in obj) {
            if(obj.hasOwnProperty(key)) {
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key] && typeof obj[key] === 'object') {
                    objClone[key] = deepClone(obj[key]);
                }else {
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}

测试数组:

// 测试数组
let arr1 = [1,[2,6],3,4];
let arr2 = deepClone(arr1);
console.log('未改变属性之前的数组:');
console.log('待拷贝的数组:',arr1);
console.log('拷贝的数组:',arr2);

arr2[0] = 0;
arr2[1][1] = 10;
console.log('未改变属性之前的数组:');
console.log('待拷贝的数组:',arr1);
console.log('拷贝的数组:',arr2);

在这里插入图片描述

测试对象:

//测试对象
// 定义对象obj1,一会对obj1进行浅复制
var obj1 = {
    a: 1,
    b: 2,
    c: {
        d: 3
    }
}


// 对obj1进行深拷贝
let obj2 = deepClone(obj1);


//更改obj2的属性的值
obj2.a = 8;
obj2.c.d = 4;
console.log('obj1.a:',obj1.a);
console.log('obj2.a:',obj2.a);
console.log('obj1.c.d:',obj1.c.d);
console.log('obj2.c.d:',obj2.c.d);

在这里插入图片描述

通过JSON对象来实现深拷贝

  • 缺点:无法实现对对象中方法的深拷贝,会显示为undefined
/* 定义进行深拷贝 */
function deepClone2(obj) {
    var _obj = JSON.stringify(obj),
      objClone = JSON.parse(_obj);
    return objClone;
}

测试:

// 定义对象obj1,一会对obj1进行浅复制
var obj1 = {
    a: 1,
    b: 2,
    c: {
        d: 3
    }
}


//进行深拷贝
let obj2 = deepClone2(obj1);
console.log('未修改属性之前的obj1,obj2:')
console.log('obj1:',obj1);
console.log('obj2:',obj2)

//修改属性值
obj2.a = 10;
obj2.c.d = 100;

console.log('修改属性之后的obj1,obj2:')
console.log('obj1:',obj1);
console.log('obj2:',obj2)

在这里插入图片描述

通过jQuery的extend()方法实现深拷贝

var array = [1,2,3,4];
// true为深拷贝,false为浅拷贝
var newArray = $.extend(true,[],array); 

手动实现深拷贝

let obj1 = {
    a: 1,
    b: 2
 }
 let obj2 = {
    a: obj1.a,
    b: obj1.b
 }
 obj2.a = 3;
 console.log(obj1.a); // 1
 console.log(obj2.a); // 3

如果对象的value是基本类型的话,也可以用Object.assign()来实现深拷贝,但是要把它赋值给一个空对象

var obj1 = {
    a: 1,
    b: 2
}
var obj2 = Object.assign({}, obj1); // obj1赋值给一个空{}
obj2.a = 3;
console.log(obj1.a);// 1
console.log(obj2.a);// 3
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南栀~zmt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值