详细讲解js中的深拷贝与浅拷贝
1 概述
深拷贝与浅拷贝在其它语言中也经常被提及到,在实际项目开发过程中也常常需要区分当前使用的到底是深拷贝还是浅拷贝,有时候在该使用深拷贝的地方,我们使用了浅拷贝,会导致深藏不露的bug。
2 数据类型
在探讨深浅拷贝之前,我们先梳理一下js中的数据类型,js的数据类型分为两类:基本数据类型和引用数据类型,前者是存储在栈内存中,后者是将其地址存在栈内存中,而真实数据存储在堆内存中。
如下图所示,基本类型如number、string、boolean、Null和undefined等存储在栈内存中,而引用数据类型如Array、Object和函数等则是分别存储数据1的地址、数据2的地址和数据3的地址。
3 深浅拷贝
3.1 拷贝对象为基本数据类型
js中的基本数据类型:String Number Boolean Null Undefined,在赋值的过程中都是深拷贝。
例如,let a = 10 ; b = a , 修改其中一个变量的值,不会影响到另一个变量的值。
3.2 拷贝对象中有引用数据类型
浅拷贝:会在栈中开辟另一块空间,并将被拷贝对象的栈内存数据完全拷贝到该块空间中,即基本数据类型的值会被完全拷贝,而引用类型的值则是拷贝了“指向堆内存的地址”。
深拷贝:不仅会在栈中开辟另一块空间,若被拷贝对象中有引用类型,则还会在堆内存中开辟另一块空间存储引用类型的真实数据。
深浅拷贝的示意图如下图:
浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
4、js中的深浅拷贝
4.1 浅拷贝
4.1.1 slice()
<script>let arr1 = [1, 42, [3, 4]]let arr1Copy = arr1.slice()arr1Copy[0] = 10arr1Copy[2][0] = 100console.log(arr1) // [1, 42, [100, 4]]console.log(arr1Copy) // [10, 42, [100, 4]]</script>
arr1中的元素1是基本数据类型,所以arr1Copy能够改变其值,而不影响arr1的值。
而[3, 4]是引用数据类型,arr1和arr1Copy指向同一块堆内存地址,所以这两个变量中3都变成了100。
4.1.2 concat()
let arr2 = ['cat', 'dog', 'pig', {'name': 'xia', 'age': 18}]let arr2Copy = [].concat(arr2)arr2Copy[2] = 'big pig'arr2Copy[3]['name'] = 'aa'console.log(arr2)console.log(arr2Copy)
执行结果如下:
类似的还有…扩展运算符、Array.from、Object.assign()方法。
4.2 深拷贝
4.2.1 JSON.parse(JSON.stringify(obj))
万能转换器 JSON.parse(JSON.stringify(obj))深拷贝已有对象,它可以深拷贝多层级的,不用担心嵌套问题。
- JSON.stringfy() 将对象序列化成json对象
- JSON.parse() 反序列化——将json对象反序列化成js对象
JSON.stingify(obj)将js中的对象转换成JSON字符串
let jack = {name: 'jack'}console.log(jack)console.log(JSON.stringify(jack))
它们在格式上有区别。下图中的第一个是对象,name没有双引号括起来。第二个是json字符串,其中,name用双引号括起来了
JSON.parse()将json字符串解析成对象
let obj = {name: '静茹秋叶'}console.log('obj: ', obj)console.log('json string: ', JSON.stringify(obj))let str = JSON.stringify(obj)console.log('--------------')console.log(str)console.log('str to obj: ', JSON.parse(str))
实际项目开发过程中,我使用的这种方式最多,除此之外,还可以使用
- _.cloneDeep()
- jQuery.extend()
- 手写循环递归
5、Vue中的浅拷贝与深拷贝
两个button-counter共用同一个jack对象,用同一块地址,当其中一个实例改变时,会影响另一个实例的值。(浅拷贝)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>vue的data选项</title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body><div id="app"><button-counter></button-counter><button-counter></button-counter></div><script>let jack = {counter: 0}// 子组件Vue.component('button-counter', {data() {// 函数类型return jack},template: `<button @click="counter++">click {{counter}} times</button>`})let vm = new Vue({el: '#app' // mount到DOM上})</script>
</body>
</html>
采用深拷贝,重新创建一块内存。这样,vue的button-counter组件中的counter值互不影响。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>vue的data选项</title><script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body><div id="app"><button-counter></button-counter><button-counter></button-counter></div><script>let jack = {counter: 0}// 子组件Vue.component('button-counter', {data() {// 函数类型return JSON.parse(JSON.stringify(jack))},template: `<button @click="counter++">click {{counter}} times</button>`})let vm = new Vue({el: '#app' // mount到DOM上})</script>
</body>
</html>
(例子来源于《Vue.js从入门到项目实战》书籍)
参考资料:
- https://vue3js.cn/interview/JavaScript/copy.html#
- https://github.com/wesbos/JavaScript30
- 《Vue.js从入门到项目实战》书籍
最后特别感谢评论区的小伙伴,感谢他们指出了我的问题。
详细讲解js中的深拷贝与浅拷贝相关推荐
- js中的深拷贝和浅拷贝区别
js中的深拷贝和浅拷贝与值传递和引用传递有着些许的关联,都是根据堆栈中数据的储存来传递数据. 下面主要说一下我对深拷贝和浅拷贝的理解: 简单举个例子来说明:当我们声明一个a变量并且赋值,并且让b等于a ...
- 重新梳理下js中的深拷贝和浅拷贝
参考链接: http://www.cnblogs.com/st-les... https://blog.csdn.net/hj7jay/... 浅拷贝: 1.最简单的浅拷贝就赋值. 由于js中的对象都 ...
- JS中的深拷贝与浅拷贝
刚看完JS中的深浅拷贝,来记录分享一番,一起来开心的掉发吧. 首先了解深浅拷贝之前来看看JS中的几种数据类型,分别有String.Number.Boolean.undefined.null.Objec ...
- 理清JS中的深拷贝与浅拷贝
作者:Manjula Dube译者:前端小智来源:Medium 为了保证的可读性,本文采用意译而非直译. 浅拷贝是对象的逐位复制.创建一个新对象,该对象具有原始对象中值的精确副本.如果对象的任何字段是 ...
- python怎么避免浅拷贝_详谈Python中的深拷贝和浅拷贝
在平时工作中,经常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题.为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用.为了生成这个副本,就产生了拷贝. ...
- 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架
前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...
- python中的深拷贝与浅拷贝
浅拷贝的时候,修改原来的对象,深拷贝的对象不会发生改变. 对象的赋值 对象的赋值实际上是对象之间的引用:当创建一个对象,然后将这个对象赋值给另外一个变量的时候,python并没有拷贝这个对象,而只是拷 ...
- 详细讲解Java中log4j的使用方法
详细讲解Java中log4j的使用方法 作者: 网络 来源: 日期: 2008-1-3 23:40:24 1.Log4j是什么? Log4j可以帮助调试(有时候debug是发挥不了作 用的)和分析,要 ...
- C语言中的深拷贝和浅拷贝
http://www.cnblogs.com/zhanggaofeng/p/5421804.html C语言中的深拷贝和浅拷贝 //C语言中的深拷贝和浅拷贝 #define _CRT_SECURE_N ...
最新文章
- GitHub上整理的一些工具【转载】
- 关于java和c的选择结构和循环结构
- 马斯克,特斯拉首席小白鼠
- MySQL中地理位置数据扩展geometry的使用心得
- C# 页面调用控制台应用程序
- Linux系统用户账号的管理技巧
- HVV之WIFI蜜罐反制红队
- 戴文的Linux内核专题:07内核配置(3)
- 写程序实现wireshark的抓包功能
- 二分排序法(折半排序)
- 深入理解死亡之Ping ---《openstack 网络安全》
- makefile编写规则
- unas 下 Transmission 下载使用体会
- html、css 实现网页弹出层
- export default (imported as router) was not found_一篇文章搞定as四大用法
- VMM物理内存、虚拟内存管理,偷页算法
- react 或者 vue,如何做 SEO 优化?
- 读研攻略(7)—从0到1,研究生第一篇SCI的诞生
- Remove WebCakeDesktop
- 晦涩难懂的c语言语句,【c/c 学习心得】晦涩难懂的const关键词,const v.s. 指标值...