带你搞懂js的深拷贝

目录
  • js深拷贝
    • 数据存储方式
    • 什么是浅/深拷贝
    • 常用深拷贝实现
      • 1.通过JSON.stringify和JSON.parse
      • 2.扩展运算符
      • 3.手写递归深拷贝函数
  • 总结

js深拷贝

在讲正题之前我们要先了解数据存储的方式

数据存储方式

在讲之前我们要先知道值类型和引用类型的存储方式。

在JavaScript数据类型中有两种数据类型。

值类型:字符串(String)、数字 (Number)、布尔 (Boolean)、空(Null)、未定义(Undefined)、Symbol

存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。

引用数据类型:对象 (Object) 、数组 (Array) 、函数 (Function) 。

存放在堆内存中的对象,在栈内存中存的是一个指针,这个指针指向堆内存一个位置。再从堆内存中取得所需的数据。

存储如下图:

什么是浅/深拷贝

讲完存储的方式,我们来讲讲浅拷贝和深拷贝

拷贝也就是我们常常讲的copy,ctrl+c,ctrl+v,那么我们来看看例子

当我们对分别值类型和引用类型进行赋值。

     var a = 5
     var b = a
     b += 5
     console.log('a=' + a,'b=' + b)
     var arr=[1,2,3]
     var brr=arr
     brr.push(10)
     console.log("arr为",arr)
     console.log("brr为",brr)

现象:我们发现值类型并没有互相受到影响,然而数组(引用类型)brr数组添加元素的时候改变了arr数组。

解释分析:浅拷贝只会发生在引用类型身上,对于引用类型如果之进行简单的赋值,只会赋值指向堆内存的指针,这种称为浅拷贝。而深拷贝就是完全拷贝一个引用类型,为不是地址指针。

浅拷贝看下面这张原理图:

常用深拷贝实现

那么我们在赋值引用类型的时候肯定不能出现浅拷贝的现象,对原数据产生影响了。那么就要进行深拷贝

1.通过JSON.stringify和JSON.parse

可以深拷贝的数组和对象,但是不能拷贝函数,可以进行对象或者数组的嵌套拷贝。

缺点:无法实现对对象中方法的深拷贝

使用

     var brr=JSON.parse(JSON.stringify(arr))

例子:

  var arr = {
         name: '浪漫主义码农',
         age: 20,
         adress: ['jiangxi', 'changsha'],
         friends: {
             friend1: '张三',
             friend2: '李四'
         },
         function(){
             console.log("我是浪漫主义的对象")
         }
     }
     var brr=JSON.parse(JSON.stringify(arr))
     brr.name='法外狂徒张三'
     brr.adress[0]='长沙'
     console.log("arr为", arr)
     console.log("brr为", brr)

2.扩展运算符

利用了对象的结构赋值特性方法。

缺点:无对对象里面嵌套的对象进行深拷贝,相当于只是对一层引用对象进行深拷贝

使用:

     var brr={...arr}

例子:

  var arr = {
         name: '浪漫主义码农',
         age: 20,
         adress: ['jiangxi', 'changsha'],
         friends: {
             friend1: '张三',
             friend2: '李四'
         },
         function(){
             console.log("我是浪漫主义的对象")
         }
     }
     var brr={...arr}
     brr.name='法外狂徒张三'
     brr.adress[0]='长沙'
     console.log("arr为", arr)
     console.log("brr为", brr)

3.手写递归深拷贝函数

完美解决

函数:

  //使用递归实现深拷贝
     function deepClone(obj) {
         //判断拷贝的obj是对象还是数组
         var objClone = Array.isArray(obj) ? [] : {};
         if (obj && typeof obj === "object") { //obj不能为空,并且是对象或者是数组 因为null也是object
             for (key in obj) {
                 if (obj.hasOwnProperty(key)) {
                     if (obj[key] && typeof obj[key] === "object") { //obj里面属性值不为空并且还是对象,进行深度拷贝
                         objClone[key] = deepClone(obj[key]); //递归进行深度的拷贝
                     } else {
                         objClone[key] = obj[key]; //直接拷贝
                     }
                 }
             }
         }
         return objClone;
     }

例子:

      var arr = {
         name: '浪漫主义码农',
         age: 20,
         adress: ['jiangxi', 'changsha'],
         friends: {
             friend1: '张三',
             friend2: '李四'
         },
         fun: function(){
             console.log("我是" + this.name + "的对象")
         }
     }
     var brr = deepClone(arr)
     brr.name = '法外狂徒张三'
     brr.adress[0] = '长沙'
     console.log("arr为", arr)
     arr.fun()
     console.log("brr为", brr)
     brr.fun()
 ​
     //使用递归实现深拷贝
     function deepClone(obj) {
         //判断拷贝的obj是对象还是数组
         var objClone = Array.isArray(obj) ? [] : {};
         if (obj && typeof obj === "object") { //obj不能为空,并且是对象或者是数组 因为null也是object
             for (key in obj) {
                 if (obj.hasOwnProperty(key)) {
                     if (obj[key] && typeof obj[key] === "object") { //obj里面属性值不为空并且还是对象,进行深度拷贝
                         objClone[key] = deepClone(obj[key]); //递归进行深度的拷贝
                     } else {
                         objClone[key] = obj[key]; //直接拷贝
                     }
                 }
             }
         }
         return objClone;
     }

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • JS对象复制(深拷贝和浅拷贝)

    一.浅拷贝 1.Object.assign(target,source,source...) a.可支持多个对象复制 b.如果source和target属性相同 source会复制target的属性 c.target只能为Object对象 var obj = {a:1,b:2} undefined Object.assign({c:3},obj) {c: 3, a: 1, b: 2} obj {a: 1, b: 2} 兼容性写法if(Object.assign){//兼容}else{//不兼容}

  • JavaScript深拷贝的一些踩坑记录

    前言 之前去一家公司面试的时候,面试官问了我一个问题,说:"如何才能深拷贝一个对象".当时我心里有些窃喜,这么简单的问题还用想吗?于是脱口而出:"平时常用的有两种办法,第一种用JSON.parse(JSON.stringify(obj)),第二种可以使用for...in加递归完成".面试官听了以后点了点头觉得挺满意的. 当时我也并没有太过在乎这个问题,直到前段时间又想起这个问题,发现上面说的两种方法都是有Bug的. 提出问题 那么上面所说的Bug是什么呢? 特殊对象

  • 详解JS深拷贝与浅拷贝

    一.预备知识 1.1.JS数据类型 基本数据类型:Boolean.String.Number.null.undefined 引用数据类型:Object.Array.Function.RegExp.Date等 1.2.数据类型的复制 基本数据类型的复制,是按值传递的 var a = 1; var b = a; b = 2; console.log(a); // 1 console.lob(b); // 2 引用数据类型的复制,是按引用传值 var obj1 = { a: 1; b: 2; }; v

  • js中的赋值 浅拷贝和深拷贝详细

    目录 1.js内存 2.赋值 3.浅拷贝 4.深拷贝 前言: 在学习下面文章前我们简单了解一下的内存的知识,以下先简要提一下 1.js内存 js内存,或者说大部分语言的内存都分为栈和堆.基本数据类型的变量值分配在栈上,引用数据类型的变量值分配在堆上,栈中只是存储具体堆中对象的地址. 2.赋值 对于基本数据类型,赋值操作是拷贝,即新旧变量不会相互影响. var a = 1; var b = a; b = 2; console.log(b); // 2 对于引用数据类型,赋值操作只是在栈中新增一个指

  • 详解JS变量存储深拷贝和浅拷贝

    变量类型与存储空间 栈内存和堆内存 基本数据类型 string.number.null.undefined.boolean.symbol(ES6新增) 变量值存放在栈内存中,可直接访问和修改变量的值 基本数据类型不存在拷贝,好比如说你无法修改数值1的值 引用类型 Object Function RegExp Math Date 值为对象,存放在堆内存中 在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址. 当访问引用类型的时候,要先从栈中取出该对象的地址指针,然后再从堆内存中取得所需的数据

  • 浅谈JavaScript浅拷贝和深拷贝

    目录 一.直接赋值 二.浅拷贝 三.深拷贝 1. JSON对象的方式 2. 递归复制 网上关于这个话题,讨论有很多了,根据各路情况我自己整理了一下,最后还是能接近完美的实现深拷贝,欢迎大家讨论. javascript中的对象是引用类型,在复制对象的时候就要考虑是用浅拷贝还是用深拷贝. 一.直接赋值 对象是引用类型,如果直接赋值给另外一个对象,那么只是赋值一个引用,实际上两个变量指向的同一个数据对象,如果其中一个对象的属性变更,那么另外一个也会变更. 示例1,简单示例: let human1 =

  • 带你搞懂js的深拷贝

    目录 js深拷贝 数据存储方式 什么是浅/深拷贝 常用深拷贝实现 1.通过JSON.stringify和JSON.parse 2.扩展运算符 3.手写递归深拷贝函数 总结 js深拷贝 在讲正题之前我们要先了解数据存储的方式 数据存储方式 在讲之前我们要先知道值类型和引用类型的存储方式. 在JavaScript数据类型中有两种数据类型. 值类型:字符串(String).数字 (Number).布尔 (Boolean).空(Null).未定义(Undefined).Symbol. 存放在栈内存中的简

  • 一文带你搞懂JS中六种For循环的使用

    目录 一.各个 for 介绍 1.for 2.for ... in 3.for ... of 4.for await...of 5.forEach 6.map 二.多个 for 之间区别 1.使用场景差异 2.功能差异 3.性能差异 三.for 的使用 for 循环在平时开发中使用频率最高的,前后端数据交互时,常见的数据类型就是数组和对象,处理对象和数组时经常使用到 for 遍历,因此下班前花费几分钟彻底搞懂这 5 种 for 循环.它们分别为: for for ... in for ... o

  • 一文带你搞懂Numpy中的深拷贝和浅拷贝

    目录 1. 引言 2. 浅拷贝 2.1 问题引入 2.2 问题剖析 3. 深拷贝 3.1 举个栗子 3.2 探究原因 4. 技巧总结 4.1 判断是否指向同一内存 4.2 其他数据类型 5. 总结 1. 引言 深拷贝和浅拷贝是Python中重要的概念,本文重点介绍在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理. 闲话少说,我们直接开始吧! 2. 浅拷贝 2.1 问题引入 我们来举个栗子,如下所示我们有两个数组a和b,样例代码如下: import numpy as np a = np.ar

  • 一篇文章带你搞懂Vue虚拟Dom与diff算法

    前言 使用过Vue和React的小伙伴肯定对虚拟Dom和diff算法很熟悉,它扮演着很重要的角色.由于小编接触Vue比较多,React只是浅学,所以本篇主要针对Vue来展开介绍,带你一步一步搞懂它. 虚拟DOM 什么是虚拟DOM? 虚拟DOM(Virtual   Dom),也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的. 真实的元素节点: <div id="wr

  • 菜鸟也能搞懂js中typeof与instanceof区别

    目录 一.typeof 二.instanceof 三.区别 一.typeof typeof 操作符返回一个字符串,表示未经计算的操作数的类型 使用方法如下: typeof operand typeof(operand) operand表示对象或原始值的表达式,其类型将被返回 举个例子 typeof 1 // 'number' typeof '1' // 'string' typeof undefined // 'undefined' typeof true // 'boolean' typeof

  • 一文带你搞懂Vue3的基本语法

    目录 1.通过 CDN 使用 Vue3 2.Vue3 模板语法 文本 Html 属性 表达式 指令 参数 3.模板用户输入双向绑定 1.通过 CDN 使用 Vue3 你可以借助 script 标签直接通过 CDN 来使用 Vue: <script src="https://unpkg.com/vue@next"></script> 通过 CDN 使用 Vue 时,不涉及“构建步骤”.这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架集成 接下来我

  • 通过示例彻底搞懂js闭包

    例1 function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { console.log(text); } sayAlert(); } sayHello("Bob") // 输出"Hello Bob" 在sayHello()函数中定义并调用了sayAlert()函数:sayAlert()作为内层函数,可以访问外层函数sayHello()中的text变量. 例2 f

  • 详解帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    提示:不要排斥,静下心来,认真读完,你就搞懂了!(可以先看一下最后的总结部分再回过头来完整看完) 1. 前言 作为一名前端工程师,必须搞懂JS中的prototype.__proto__与constructor属性,相信很多初学者对这些属性存在许多困惑,容易把它们混淆,本文旨在帮助大家理清它们之间的关系并彻底搞懂它们.这里说明一点,__proto__属性的两边是各由两个下划线构成(这里为了方便大家看清,在两下划线之间加入了一个空格:_ _proto_ _),实际上,该属性在ES标准定义中的名字应该

  • 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    提示:不要排斥,静下心来,认真读完,你就搞懂了!(可以先看一下最后的总结部分再回过头来完整看完) 1. 前言 作为一名前端工程师,必须搞懂JS中的prototype.__proto__与constructor属性,相信很多初学者对这些属性存在许多困惑,容易把它们混淆,本文旨在帮助大家理清它们之间的关系并彻底搞懂它们.这里说明一点,__proto__属性的两边是各由两个下划线构成(这里为了方便大家看清,在两下划线之间加入了一个空格:_ _proto_ _),实际上,该属性在ES标准定义中的名字应该

  • 无惧面试,带你搞懂python 装饰器

    写在之前 「装饰器」作为 Python 高级语言特性中的重要部分,是修改函数的一种超级便捷的方式,适当使用能够有效提高代码的可读性和可维护性,非常的便利灵活. 「装饰器」本质上就是一个函数,这个函数的特点是可以接受其它的函数当作它的参数,并将其替换成一个新的函数(即返回给另一个函数). 可能现在这么看的话有点懵,为了深入理解「装饰器」的原理,我们首先先要搞明白「什么是函数对象」,「什么是嵌套函数」,「什么是闭包」.关于这三个问题我在很久以前的文章中已经写过了,你只需要点击下面的链接去看就好了,这

随机推荐