• js中的深拷贝浅拷贝


    js中的深拷贝浅拷贝

    • 了解深拷贝浅拷贝
    • 代码中的深拷贝浅拷贝
    • 拷贝的实现方法

    一、了解深拷贝浅拷贝

    1.1 深拷贝(值拷贝)

    会创建一个一模一样的对象,新旧对象不共享内存,修改新对象不会影响原对象

    1.2 浅拷贝(引用拷贝)

    只复制指向某个对象的指针,不复制对象本身,新旧对象还是共享同一块内存。修改新对象影响原对象

    1.3 区别

    在拷贝基本数据类型的时候其实是不区分深拷贝与浅拷贝的,因为都是拷贝原始数据类型的值。
    当拷贝的是引用数据类型的时候,则区分浅拷贝、深拷贝,因为浅拷贝只复制引用数据类型的第一层属性,深拷贝可以对引用数据类型的属性进行递归复制。

    二、代码中的深拷贝浅拷贝

    js中的数据类型:

    • 基本数据类型:字符串(string)、数值(number)、布尔值(boolean)、undefined、null ;
    • 引用数据类型:对象(Object)、数组(Array)、函数(Function);

    基本数据类型进行复制,并重新赋值,并不会影响到原来的变量,这就是深拷贝

    let myname = '大华';
    let age = myname;
    age = 22;
    console.log(myname); //大华
    console.log(age); //22
    
    • 1
    • 2
    • 3
    • 4
    • 5

    myname这个变量会在内存中开辟一个地址,存储一个值为大华age这个变量复制了myname所以他们指向的都是同一个地址,同时也指向同一个值,但对 age重新赋值时,会在内存在开辟一个地址来存储另一个值22

    引用数据类型进行复制,并重新赋值,会影响到原来的变量,这就是浅拷贝

    let obj1 = {
        myname: "大华",
        age: 12
    }
    let obj2 = obj1;
    obj2.age = 22;
    
    //里面的age都变成22
    console.log(obj1); 
    console.log(obj2);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对于引用数据类型来说,就有了的说法,obj1会开辟一个内存地址,与值相对应(这就是栈),但这个内存地址的值又会被分配到另一个地址,在另一个地址中又创建了对应的值(也就是堆),obj2在进行复制时,就会通过栈一路找到堆进行复制,一旦修改,那么obj1obj2都会受影响,因为它们指向的都是同一个地址。

    三、拷贝的实现方法

    实际的项目开发过程中,在多数情况下不希望将对象进行浅拷贝,因为值会相互影响,容易出错,可以把浅拷贝转换成深拷贝。

    3.1 深拷贝的实现方法

    1、JSON.stringify()

    JSON.parse(JSON.stringify(obj)) 是目前比较常用的深拷贝方法之一,它的原理就是利用 JSON.stringify 将 js 对象序列化(JSON字符串),再使用 JSON.parse 来反序列化(还原) js 对象。

    这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过 JSON.stringify() 进行处理之后,都会消失。

    let obj1 = {  a: 0,b: {c: 0}};
    let obj2 = JSON.parse(JSON.stringify(obj1));
    obj1.a = 1;
    obj1.b.c = 1;
    console.log(obj1); // {a: 1, b: {c: 1}}
    console.log(obj2); // {a: 0, b: {c: 0}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、函数库lodash的_.cloneDeep方法

    该函数库也有提供_.cloneDeep用来做 Deep Copy

    var _ = require('lodash');
    var obj1 = {
        a: 1,
        b: { f: { g: 1 } },
        c: [1, 2, 3]
    };
    var obj2 = _.cloneDeep(obj1);
    console.log(obj1.b.f === obj2.b.f);// false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、扩展运算符
    使用扩展运算符可以在对对象或数组的第一层的拷贝是深拷贝

    let obj1 = {a:1,b:{c:1}}
    let obj2 = {...obj1};
    obj1.a = 2;
    console.log(obj1); //{a:2,b:{c:1}}
    console.log(obj2); //{a:1,b:{c:1}}
    obj1.b.c = 2;
    console.log(obj1); //{a:2,b:{c:2}}
    console.log(obj2); //{a:1,b:{c:2}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4、实现深拷贝函数(解决循环引用)

    // 深拷贝的实现
    function deepCopy(obj, map) {
    	//判断是否是第一次调用deepCopy方法,是的话创建一个weakmap实例来装遍历过程中出现过的对象
    	if(!map){
    		map = new WeakMap()
    	}
    	//判断传入的obj是否为对象
    	if( obj === null || typeof obj !== 'Object' ){
        	return obj
    	}
    	//如果map中已经存在这个对象说明出现了循环引用问题
    	if(map.get(obj)){
      		return obj
      	}
    	//map中没有就往map中存入该对象
    	map.set(obj,obj)
    	//根据obj的类型来给newObj创建初始值
    	let newObj = Array.isArray(obj) ? [] : {}
    	//遍历obj
    	for(let i in obj){
    		if(obj.hasOwnproperty(i)){ //判断当前属性是否为obj自身的属性
    			if(typeof obj[i] === 'Object'){ //判断当前属性是否为对象类型
    		    	newObj[i] = deepCopy(obj[i],map) //如果是对象类型就使用该方法进行递归处理
    			}else{
    				newObj[i] = obj[i] //不是对象类型就直接拷贝
    			}
    		}
    	}
    	return newObj //返回拷贝完成的newObj
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.2 浅拷贝的实现方法

    1、Object.assign()
    Object.assign() 是 ES6 中对象的拷贝方法,接受的第一个参数是目标对象,其余参数是源对象,用法:Object.assign(target, source_1, ···),该方法可以实现浅拷贝,也可以实现一维对象的深拷贝。

    该方法需要注意的是:

    • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性。
    • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
    • 因为null 和 undefined 不能转化为对象,所以第一个参数不能为null或 undefined,会报错。
    let target = {a: 1};
    let object2 = {b: 2};
    let object3 = {c: 3};
    Object.assign(target,object2,object3);  
    console.log(target);  // {a: 1, b: 2, c: 3}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、扩展运算符
    使用扩展运算符可以在对对象或数组的第二层开始的拷贝是浅拷贝

    let obj1 = {a:1,b:{c:1}}
    let obj2 = {...obj1};
    obj1.a = 2;
    console.log(obj1); //{a:2,b:{c:1}}
    console.log(obj2); //{a:1,b:{c:1}}
    obj1.b.c = 2;
    console.log(obj1); //{a:2,b:{c:2}}
    console.log(obj2); //{a:1,b:{c:2}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、数组方法实现数组浅拷贝

    (1)Array.prototype.slice

    slice() 方法是 JavaScript 数组的一个方法,这个方法可以从已有数组中返回选定的元素:用法:array.slice(start, end),该方法不会改变原始数组。
    该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝

    let arr = [1,2,3,4];
    console.log(arr.slice()); // [1,2,3,4]
    console.log(arr.slice() === arr); //false
    
    • 1
    • 2
    • 3

    (2)Array.prototype.concat

    concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。该方法有两个参数,两个参数都可选,如果两个参数都不写,就可以实现一个数组的浅拷贝

    let arr = [1,2,3,4];
    console.log(arr.concat()); // [1,2,3,4]
    console.log(arr.concat() === arr); //false
    
    • 1
    • 2
    • 3

    4、实现浅拷贝函数

    // 浅拷贝的实现;
    function shallowCopy(object) {
    	// 只拷贝对象
    	if (!object || typeof object !== "object") return;
    	// 根据 object 的类型判断是新建一个数组还是对象
    	let newObject = Array.isArray(object) ? [] : {};
    	// 遍历 object,并且判断是 object 的属性才拷贝
    	for (let key in object) {
    		if (object.hasOwnProperty(key)) {
    			newObject[key] = object[key];
    		}
    	}
    	return newObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    更多内容链接☛ 将浅拷贝转换成深拷贝,Vue中的浅拷贝与深拷贝

  • 相关阅读:
    题目 1240: 生日日数
    基于SSH的二手货交易平台的设计与实现(lunwen+任务书+翻译及原文+项目源码+sql文件)
    Pytorch入门实战(5):基于nn.Transformer实现机器翻译(英译汉)
    包含Uniapp单聊思路,单群聊集群
    总结10.11-11.6号
    Vue组件化编程
    &2_PyTorch神经网络基础
    探索 GLTF 的世界:3D 内容的未来
    时间序列预测 | Python实现Attention-Transformer时间序列预测(TSAT model)
    【从零开始学微服务】01.微服务的过去与现在
  • 原文地址:https://blog.csdn.net/Senora/article/details/125889491