原生js深入理解系列(四)--- 多个实例深入理解js的深拷贝和浅拷贝,多种方法实现对象的深拷贝...

亲们为什么要研究深拷贝和浅拷贝呢,因为我们项目开发中有许多情况需要拷贝一个数组抑或是对象,但是单纯的靠=“赋值”并不会解决所有问题,如果遇到引用类型的对象改变新赋值的对象会造成原始对象也发生同样改变,而要去除影响就必须用到浅拷贝、深拷贝,深拷贝,对于引用对象需要进行深拷贝才会去除影响。如果是值类型直接“=”就好。

简而言之:

赋值:就是两个对象指向的内存地址一样,a=b赋值后的新对象也指向同一个存储地址所以b变化a跟随变化,

浅拷贝:拷贝对象的一级元素的地址,如果一级元素全部为值类型就会互不干扰,如果一级元素有引用类型,改变引用类型的里面的值,会改变原对象。

深拷贝:拷贝对象各级元素的存储地址。

1.引用类型  引用类型通常叫做类(class),也就是说,遇到引用值,所处理的就是对象。new 出来的对象都是引用对象

(1)值类型:String, 数值、布尔值、null、undefined。 对于值类型一个对象一个存储位置所以会互不干扰

(2)引用类型:对象、数组、函数。对于引用类型,a=b赋值后的新对象也指向同一个存储地址所以b变化a跟随变化,

下图如果a是{key:'1', value:'value',children:{key:'child'}}这样一个对象,a赋值给b的话,a,b的存储位相同,里面的值变化也相同。如果a浅拷贝给b,a的值类型的值会复制给b,而a和b指向的引用类型的存储位置会相同,a引用类型里的children.key的值变化会引起b的children.key的值变化。即指针指向位置相同,那么该位置里的值也相同

e88dc914fe411e1d7f0a09af7d16125f094.jpg

而要改变这种情况需要改变b的指向,使其指向b存储位置如下图

20e83231f246201c1419e3c67baf3b3bb1b.jpg

下面是各种实例对比:

第一模块-数组:

数组浅拷贝:

a =[1,2,3];b=a;b.push('change');console.log('a:'+a,'b:'+b)
VM529:1 a:1,2,3,change b:1,2,3,change

数组深拷贝://到每一项都是值类型时可以直接赋值

a =[1,2,3];a.forEach(val => b.push(val));b.push('change');console.log('a:'+a,'b:'+b)
VM529:1 a:1,2,3,change,1,2,3,change,1,2,3,change

第二模块-对象:

1-1对象赋值的代码及结果

be996dc12fabce32514960ab1c5c45b1227.jpg

bb改变后原数组aa也跟随变化,根本原因就是像第一张线框图描述的一样

1-2对象对象浅拷贝的代码及结果代码及结果,使用es6的Object.assign()方法

c16ae73a91a4293b3ae830bc95adb066845.jpg

浅拷贝bb不会影响aa,因为改变是是值类型,但是如果是改变引用类型的值呢?如下图

结果很显然,对于引用对象Object.assign()就不行了,有点鸡肋了。aa的children里面的key值随bb的改变而改变

1-3对象深拷贝使用JSON.parse(JSON.stringify(obj)),即对象序列化

0cfb7b9430a9cb9440be35f34dc3353db78.jpg

给子对象children改变属性如下图所示,aa原对象是不变的。但是JSON.parse(JSON.stringify(obj))实现深拷贝会存在一些问题,比如序列化会将序列化的undefined丢失,序列化正则对象(RegExp)会返回{},obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null,对于序列化构造函数construct()会被丢失
 

1-4对象深拷贝使用es6的Object.keys(obj),Object.values(obj)分别获得键数组和值数组,再通过函数根据条件循环回调deal()得到深拷贝值:《推荐使用回调》,留下个小问题,看官们可以试着这个思路去完成数组和函数回调实现深拷贝。

可复制代码:

aa = {
    key:'1',
    value:'a',
    children:{
        key: '2',
        value: 'b'
    }
};

function deal(obj, bb = {}) {
    let keyArr = Object.keys(obj);
    let valueArr = Object.values(obj);
    valueArr.forEach((val,index) => {
    console.log(Object.prototype.toString.call(val))
        if(Object.prototype.toString.call(val) === "[object Object]") {
             bb[keyArr[index]] = deal(val, bb[keyArr[index]])
        }else {
            bb[keyArr[index]] = val
        }
    })
    return bb
}
BB = {}
BB=deal(aa);
console.log(BB)
BB['add']='addStr';
BB['children']['change']='变'
console.log('aa:',aa,'========','BB:',BB)

截图代码和结果:

第三模块对于对象来说可复制的浅、深拷贝代码和结果事例如下:

实例一

aa = {
    key:'1',
    value:'a',
    children:{
        key: '2',
        value: 'b'
    }
};
bb=aa;bb['add']='addStr';console.log('aa:',aa,'====','bb',bb)
VM1098:9 aa: {key: "1", value: "a", children: {…}, add: "addStr"}add: "addStr"children: key: "2"value: "b"__proto__: Objectkey: "1"value: "a"__proto__: Object ==== bb {key: "1", value: "a", children: {…}, add: "addStr"}add: "addStr"children: key: "2"value: "b"__proto__: Objectkey: "1"value: "a"__proto__: Object

 

实例二
aa = {
    key:'1',
    value:'a',
    children:{
        key: '2',
        value: 'b'
    }
};
bb = {}
bb=Object.assign({},aa);bb['add']='addStr';console.log('aa:',aa,'====','bb',bb)
VM1223:10 aa: {key: "1", value: "a", children: {…}}children: key: "2"value: "b"__proto__: Objectkey: "1"value: "a"__proto__: Object ==== bb {key: "1", value: "a", children: {…}, add: "addStr"}add: "addStr"children: key: "2"value: "b"__proto__: Objectconstructor: ƒ Object()hasOwnProperty: ƒ hasOwnProperty()isPrototypeOf: ƒ isPrototypeOf()propertyIsEnumerable: ƒ propertyIsEnumerable()toLocaleString: ƒ toLocaleString()toString: ƒ toString()valueOf: ƒ valueOf()__defineGetter__: ƒ __defineGetter__()__defineSetter__: ƒ __defineSetter__()__lookupGetter__: ƒ __lookupGetter__()__lookupSetter__: ƒ __lookupSetter__()get __proto__: ƒ __proto__()set __proto__: ƒ __proto__()key: "1"value: "a"__proto__: Object

对于改变引用对象源码

实例如下,浅拷贝改变引用对象里面的值事,原对象也会改变

aa = {
    key:'1',
    value:'a',
    children:{
        key: '2',
        value: 'b'
    }
};
bb = {}
bb=Object.assign({},aa);bb['children']['key']='addStr';console.log('aa:',aa,'====','bb',bb),结果如下:

 

实例三
aa = {
    key:'1',
    value:'a',
    children:{
        key: '2',
        value: 'b'
    }
};
bb = {}
bb=JSON.parse(JSON.stringify(aa));bb['add']='addStr';console.log('aa:',aa,'====','bb',bb)
VM1653:10 aa: {key: "1", value: "a", children: {…}}children: key: "2"value: "b"__proto__: Objectkey: "1"value: "a"__proto__: Object ==== bb 

欢迎转载,转载请注明出处。欢迎大家交流学习

转载于:https://my.oschina.net/u/2542841/blog/2961340

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值