js 对象深拷贝_深拷贝与浅拷贝

前言:最近在复习一些面试的知识点,刚刚好复习到了这一部分,于是就写下这篇文章记录一下。

一 、值类型和引用类型

        在学习深拷贝和浅拷贝之前,我们先来了解一下js的变量类型。

值类型 vs 引用类型

值类型:值类型主要有:number,string,boolean,symbol,null,undefined 这6种。

我们来看下代码:

7fb019a3026f3e7d0935a4b169fa6d76.png

我们可以看到将a赋值给了b,然后将a的值改变,再打印b,发现b还是100.

引用类型:Object,Array,RegExp,Date,Function。

看下代码:

c31b40e572bd391501d1270545c538c3.png

a为一个对象,里面有age,将b赋值成a,然后修改b.age,发现a对象里面的age也跟着改变了。

深入分析一下:

        1、当我们的代码中出现变量的时候,浏览器会将变量存放栈中,这个栈就是我们计算机的储存结构。如上面值类型中的定义的变量a和b,就会放到栈中存储起来。

e425b1ae74adfb3d2ddf0d58f7edd2bb.png

由上图可以看到,当重新定义变量b,并且将b赋值为a的时候,栈就会重新开辟一个储存位置,这时候改变b的值,是不会影响到a变量的值得的。

      2、当我们使用到js的引用类型变量的时候,这时候就会涉及到堆的储存(在计算机变量存储的时候,堆和栈是同时存在的,栈是从上往下累加,堆是相反)。

3a3ccb39de9e90037b0dbfaf2792b9ca.png

 当我们将变量a赋值成一个对象的时候,浏览器会在堆中生成一个内存地址(内存地址1),然后把这个对象{age :18},放到堆里面存储起来。这时候,变量a存储的是一个地址,并且在栈中储存起来。这时候将变量b赋值成a,b就会同样的指向内存地址1。这时候去改变b的内容,a中的内容也是会发生改变的。这是因为b拷贝了a在栈中的堆地址,都指向a在堆中的属性值,当a放生改变时,b属性值也会改变,因为b也指向相同的堆内存。

那么有没有办法将 引用类型 像 值类型 赋值一样去赋值呢?

接下来就需要用到js的浅拷贝和深拷贝了来解决上面出现的问题了(而变量 a和 b 指向同一地址,当该地址的数据改变时,所有使用该地址的变量全部改变(同一数据))。

浅拷贝

     既然对象数据类型 是由基本数据类型组成的,而基本数据类型可以正常赋值、比较,那我们就把对象类型变成一个个的基本类型进行操作,所以我们可以遍历对象中的内容,一个一个的进行赋值,进行一层的拷贝,这就是浅拷贝。下面我们来手写一个浅拷贝

        //浅拷贝        function shallowClone(source = {}) {            let result = {},                key;            for(key in source){                if (source.hasOwnProperty(key)) { //这里的意思是判断这一项是否是其自有属性,是的话才拷贝,不是就不拷贝                    result[key] = source[key]                }            }            return result;        }

   然后我们来测试一下,发现是拷贝成功了,

7a504587f3f93a3f3b94edaf915eaa6f.png

但是这只能拷贝一层,要是对象有多层嵌套的话,就不行了。如下:

08fc825c3c55161bdffce2fa1a6e7b9a.png

这时候,我们需要使用到深拷贝来解决这个问题。

深拷贝

      相对于浅拷贝只能拷贝一层,深拷贝可以无限层次的拷贝。

深拷贝实现的思路:

  • 深拷贝,就是遍历那个被拷贝的对象

  • 判断对象里每一项的数据类型

  • 如果不是对象类型,就直接赋值,如果是对象类型,就再次调用deepCopy,递归的去赋值。

代码如下:

        // 深拷贝        function deepClone(source = {}) {            if (typeof source !== 'object' || source == null) {                // source为null,或者不是数组和对象,直接返回                return source;            }            // 初始化返回结果            let result;            if (source instanceof Array) {                result = [];            } else {                result = {};            }            for (let key in source) {                //这里的意思是判断这一项是否是其自有属性,是的话才拷贝,不是就不拷贝                if (source.hasOwnProperty(key)) {                    // 递归调用                    result[key] = deepClone(source[key])                }            }            return result;        }

然后我们来测试一下:

4850e873dceff6102bfc01646ba58fcd.png

会发现,深拷贝成功了。

还有一种非常简单的深拷贝方法,相信大家也经常用到过,就是使用系统自带的JSON来做深拷贝。

        function cloneJSON(source) {            return JSON.parse(JSON.stringify(source));        }

但是他的缺陷就是:对象里面的函数无法被拷贝,原型链里面的属性无法被拷贝。

总结

    其实我们有很多其他的办法实现拷贝,比如:ES6的assign方法(浅拷贝);通过immutableJS实现深拷贝; JQ的extend方法等等。我们可以根据不同的业务场景进行不同的选择。无论是深拷贝还是浅拷贝,一旦数据量大了起来,都会出现性能问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值