面试题被问到再也不慌,深究JavaScript中的深拷贝与浅拷贝

简介: JavaScript中的深拷贝和浅拷贝是前端面试中频繁被问到的一道题, 于是我也自己去查阅了一些资料, 然后动手敲了代码测试了一下。那么我就用我的理解,给大家来讲解一下这个深拷贝和浅拷贝吧。

01

数据类型介绍


JavaScript中的变量一共有两种类型的值: 基本类型值引用类型值


  • 基本类型值:是指StringNumberundefinedNullboolean等,他们在内存中都是存储在栈中的 , 即直接访问该变量就可以得到存储在栈中的该变量的值。若将一个变量的值赋值给另一个变量, 则这两个变量在内存中是独立的, 修改其中一个变量的值, 不会影响另一个变量。


我们来看一下图片介绍:


   1. 创建了第一个变量 a , 并赋值一个 ’ 哈哈 ’ 给它,则在内存中的表现         形势为:


91cb188360cd222093946861c323ca16.png


   2. 然后又创建了一个变量 b ,将 变量 a赋值给b,则此时在内存中的表现形      式为:


55cdd1ebe94738ab387612dd39910467.png

可以看到在栈中, b独立占有一个位置,并且拥有自己的值为 ’ 哈哈 ’


   3. 然后此时我们将变量a 的值改为 ’ 嘿嘿 ’ , 此时在内存中的情况是:


6e79be8732300b195ae78a0d9aa36c75.png


可以看到, 只有变量a的值变为了 ’ 嘿嘿 ’ , 这是因为 b 已经独立存在于栈中了,他不会受到 变量a 的影响。


  • 引用类型值:是指ObjectArrayFunction等,他们在内存中是存在于栈与堆中, 即我们要访问到引用类型值的话, 需要先访问到该变量在栈中的地址(指向堆中的值), 然后再通过这个地址,访问到存放在堆中的数据。


   1.  我们先创建一个变量 a ,并赋值一个{name: '张三'},此时在内存中的       情况就是:


aa6d88b1a555552270d7b7ac958e02cc.png


我们可以看到, 给变量a 赋值了一个 Object数据类型后,在内存中是,Object存储在堆中,而栈中存储的是该变量名和对应堆中的Object的地址


   2.  然后我们再创建一个变量b,将变量a 赋值给变量b,此时内存中的情况        是:


68617f37b4e57113c4d03a68f4019987.png


我们可以看到, 当把变量a 赋值给变量b 的时候, 只是在栈中创建了一个变量b,然后将 变量a 存储在栈中的地址1 给了变量b , 所以此时变量a 和 变量b都指向堆中的同一个值.


   3.  最后我们 改变一下变量a 中的值, 即a.name = ' 李四 ', 此时内存中      的情况是:


d94c3694a95f155bede20ad5826a6a44.png


我们可以看到,因为变量a 和 变量b 都指向堆中的同一个值,所以当通过变量a 改变该值时, 变量a 和 变量b 对应的值也都都会跟着变。


那么如何使变量b 独立存在而不受变量a 的影响呢?接下来我们看看浅拷贝和深拷贝


02

浅拷贝


首先说一下, 深拷贝和浅拷贝主要是针对像Object 和 Array 这两种比较复杂的对象的。

那么什么是浅拷贝呢?简单来说,就是一个变量赋值给另一个变量,其中一个变量的值改变,两个变量的值都变了,这就叫做浅拷贝。


我们来看一个最简单的浅拷贝例子:


    let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = {}for(let i in a) {  b[i] = a[i]}a.name = '李四'a.like[0] = '睡觉'console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }


    我们可以看到,变量b 通过一个一个获取变量a 中的元素, 已经独立存在, 改变了变量a 的 name,变量b 的 name却没有跟着改变。但是又能发现,变量a 改变了 like 值, 变量b 也随之改变了,好像并没有独立存在。其实这是因为变量b 在获取变量a 的每一个元素时,遇到 like , 发现它是我们上边说到的引用类型值, 所以变量b 就获得了一个地址,指向了堆中的值, 所以变量b 中的 like 仍然不是独立存在的。


    这就是一个浅拷贝的例子。总结来说就是, 浅拷贝只是将外层的值独立了出来, 例如这个例子中的 name 、 age , 都不会随着变量a 的改变而改变了;但是由于对象结构复杂,内部的引用类型值没有独立出来, 例如这个例子中的 like , 变量b 获取到以后只是获取到了一个地址, 它跟变量a 的 like 指向同一个对象, 所以变量b 的 like 仍然会随着 变量a 的 like 改变而改变。


    Object.assign


    ES6语法中也给我们提供了一个浅拷贝的方法Object.assign(target, sources)


    • target:拷贝的目标


    • sources:被拷贝的对象

    let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = {}Object.assign(b, a)a.name = '李四'a.like[0] = '睡觉'console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }


    03

    深拷贝


    那么浅拷贝, 是拷贝后,新拷贝的对象内部仍然有一部分数据会随着源对象的变化而变化, 那么深拷贝就是,拷贝后, 新拷贝的对象内部所有数据都是独立存在的,不会随着源对象的改变而改变。


    深拷贝的话一共有两种方式: 递归拷贝利用JSON函数深拷贝


    • 递归拷贝


    实现原理:对变量中的每个元素进行获取,若遇到基本类型值,直接获取;若遇到引用类型值, 则继续对该值的内部每个元素进行获取。


    这里就不多做演示了,因为用递归来实现深拷贝很少,简单运用的话,用的最多的就是第二种深拷贝方式了。


    • 利用JSON函数深拷贝

    实现原理:将变量的值转变成字符串形式, 然后再转化成对象赋值给新的变量


      let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}let b = JSON.parse(JSON.stringify(a))
      a.name = '李四'a.like[0] = '睡觉'
      console.log(a)console.log(b)// { name: '李四', age: 19, like: [ '睡觉', '唱歌', '跳舞' ] }// { name: '张三', age: 19, like: [ '打篮球', '唱歌', '跳舞' ] }


      可以看到, 变量b 已经完全独立存在了, 无论变量a 怎么变, 变量b 都保持不变了。

      相关文章
      |
      18天前
      |
      前端开发 JavaScript 网络协议
      前端最常见的JS面试题大全
      【4月更文挑战第3天】前端最常见的JS面试题大全
      40 5
      |
      2月前
      |
      JSON 前端开发 JavaScript
      JavaScript拷贝大作战:浅拷贝vs深拷贝
      JavaScript拷贝大作战:浅拷贝vs深拷贝
      50 0
      |
      1月前
      |
      编译器 C++ Python
      【C/C++ 泡沫精选面试题02】深拷贝和浅拷贝之间的区别?
      【C/C++ 泡沫精选面试题02】深拷贝和浅拷贝之间的区别?
      32 1
      |
      7天前
      |
      JavaScript 前端开发 测试技术
      「一劳永逸」送你21道高频JavaScript手写面试题(上)
      「一劳永逸」送你21道高频JavaScript手写面试题
      33 0
      |
      12天前
      |
      JavaScript 前端开发
      js中浅拷贝和深拷贝的区别
      js中浅拷贝和深拷贝的区别
      18 1
      |
      1月前
      |
      设计模式 JavaScript 前端开发
      最常见的26个JavaScript面试题和答案
      最常见的26个JavaScript面试题和答案
      44 1
      |
      1月前
      |
      存储 JavaScript 前端开发
      【JavaScript】面试手撕浅拷贝
      引入 浅拷贝和深拷贝应该是面试时非常常见的问题了,为了能将这两者说清楚,于是打算用两篇文章分别解释下深浅拷贝。 PS: 我第一次听到拷贝这个词,有种莫名的熟悉感,感觉跟某个英文很相似,后来发现确实Copy的音译,感觉这翻译还是蛮有意思的
      45 6
      |
      1月前
      |
      JavaScript 前端开发
      【JavaScript】面试手撕节流
      上篇我们讲了防抖,这篇我们就谈谈防抖的好兄弟 -- 节流。这里在老生常谈般的提一下他们两者之间的区别,顺带给读者巩固下。
      53 3
      |
      1月前
      |
      JavaScript 前端开发 API
      javascript中的浅拷贝和深拷贝
      javascript中的浅拷贝和深拷贝
      |
      2月前
      |
      前端开发 JavaScript UED
      【JavaScript】面试手撕防抖
      防抖: 首先它是常见的性能优化技术,主要用于处理频繁触发的浏览器事件,如窗口大小变化、滚动事件、输入框内容改变等。在用户连续快速地触发同一事件时,防抖机制会确保相关回调函数在一个时间间隔内只会被执行一次。
      36 0