一、什么是值类型?

二、什么是引用类型?

三、使用ES Next新特性带来的 Object.assign 方法 和 扩展运算符;

四、Object.assign 方法 和 扩展运算符的 “深入浅出” 问题 —— 浅拷贝;

五、解决深拷贝问题常见的三种方法。


在 JavaScript 中,数据类型包括:

1、基本数据(值)类型,如 undefined、null、number、boolean、string等

2、引用类型:如Object、Array、Function等。

一、什么是值类型?

var a = ;
var b = a; b = b + 1
console.log(a, b) // 1, 2

值类型赋值时,会在栈内存空间中分配全新的地址,并得到相应的值。代码中 b 是从 a 创建来的,当 b 变化时,a 不会受到影响。这是符合常识的。

二、什么是引用类型?

var data =  {
id: 0,
book: 'learn redux',
avaliable: false
}
var newData = data newData.id = 1;
console.log(data.id, newData.id) // 1 1

再来看看引用类型(以Object对象为例),对象实际是存在于堆内存空间中。当我们要访问一个对象的时候,实际上是从栈内存中获取引用地址,然后根据这个引用地址再从堆内存中获取所需要的值。

而引用类型赋值时,实际上是获取其引用地址,而不是直接获取值。所以,值发生改变时,源对象也会随着改变。

三、使用ES Next新特性带来的 Object.assign 方法 和 扩展运算符

使用 Object.assign({}, ...) 来解决 "单层"对象 引用问题

let data =  {
id: 0,
book: 'learn redux',
avaliable: false
}
let newData = Object.assign({}, data)
newData.id = 1;
console.log(data.id, newData.id) // 0 1

或者使用对象的扩展运算符

var data =  {
id: 0,
book: 'learn redux',
avaliable: false
}
var newData = {title: 'fuck', ...data};
newData.id = 1;
console.log(data.id, newData.id) // 0 1

(PS:JavaScript的数组也是引用类型,同理可以使用 Array.concat 方法 和 数组的扩展运算符来解决引用问题,这里就省略了)

四、Object.assign 方法 和 扩展运算符的 “深入浅出” 问题 —— 浅拷贝

需要注意的是,使用 Object.assign 及 扩展运算符都属于浅操作。如果往对象的外面再套一层的话,那问题就会浮现出来了。

var data =  {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
var newData = Object.assign({}, data)
newData.item.id = 1;
console.log(data.item.id, newData.item.id) // 1 1

上述的结果表明,原始数据还是被更改了。

五、解决深拷贝问题常见的三种方法

1、jQuery的深拷贝工具: $.extend(true, ...)

let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
// 首参必须设置为 true 才算是深拷贝
let newData = $.extend(true, {}, data);
newData.item.id = 1
console.log(data.item.id, newData.item.id) // 0 1

为什么$.extend默认不使用深拷贝,还需要手动传参true才开启呢?

这是因为深拷贝在实战中是比较消耗性能的,如无必要请使用浅拷贝即可。稍后我们会实现一个自己的深拷贝工具。你可以从中了解详情。

2、巧用序列化:JSON.parse(JSON.stringify(...))

let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
let newData = JSON.parse(JSON.stringify(data))
newData.item.id = 1
console.log(data.item.id, newData.item.id) // 0 1

原理很简单,就是先将对象转换为字符串,再重新序列化为对象,这样理所当然不会有引用问题。值得一提的是,这些操作对数组也是有效的。

3、递归拷贝(深拷贝)

在我们封装一个自己的深拷贝工具之前,我们需要先跳过一些可能的拷贝错误认知 —— “不要单纯的认为,只要是拷贝嵌套对象就会产生深拷贝问题”。

(1)就算是拷贝嵌套对象,如果操作的只是基本数据类型,不会有影响的。

let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
let newData = Object.assign({}, data);
newData.title = 'fuck'
console.log(data.title, newData.title) // 123 fuck

(2)就算是嵌套对象,如果只拷贝其中一层对象,是不会产生问题的。

let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
let newData = Object.assign({}, data.item);
newData.id = 1;
console.log(data.item.id, newData.id) // 0 1

有了以上两个认知,实际上我们可以实现手动深拷贝了。

let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
} // 创建一个空对象
let newData = {} // 基本数据类型,可以直接拿
newData.title = data.title // data.item对象,其实可以使用 Object.assign 来拿
// newData.item = Object.assign({}, data.item) // 但为了体现手动深拷贝细节,还是一步一步来吧。
// 通过对data.item拆解,item对象被转换为一个个的基本数据类型直接赋值给newData.item的每一项
// 如果 item 还有对象的话,那就只能依样画葫芦继续拆解。直到变成基本数据类型为止。
newData.item = {}
newData.item.id = data.item.id
newData.item.book = data.item.book
newData.item.avaliable = data.item.avaliable newData.item.id = 1
console.log(data.item.id, newData.item.id) // 0 1

通过对data.item拆解,item对象就转换为一个个的基本数据类型了,所以可以直接赋值给newData.item的每一项了。

如果 item 还有对象的话,那就只能依样画葫芦继续拆解。直到变成基本数据类型为止。

知道了如何手动深拷贝,现在我们把他转换为自动化深拷贝,常见的的实现方式是递归拷贝了:

// 递归深拷贝
var deepExtend = function(out) {
out = out || {}; for (var i = 1; i < arguments.length; i++) {
var obj = arguments[i]; if (!obj)
continue; for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object')
out[key] = deepExtend(out[key], obj[key]);
else
out[key] = obj[key];
}
}
} return out;
};

// demo
let data = {
title: '123',
item: {
id: 0,
book: 'learn redux',
avaliable: false
}
}
var newData = deepExtend({}, data);
newData.item.id = 1
console.log(data.item.id, newData.item.id) // 0 1

js 什么是深拷贝问题?的更多相关文章

  1. JS对象复制(深拷贝、浅拷贝)

    如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...

  2. JS 对象的深拷贝和浅拷贝

    转载于原文:https://www.cnblogs.com/dabingqi/p/8502932.html 这篇文章是转载于上面的链接地址,觉得写的非常好,所以收藏了,感谢原创作者的分享. 浅拷贝和深 ...

  3. 关于js浅拷贝与深拷贝的理解

    前端开发中,一般情况下,很少会去在意深拷贝与浅拷贝的关系. 大家知道,js变量有2种数据类型:基本类型和引用类型.基本类型的拷贝是将整个值完全拷贝一份的,也就是深拷贝.就是开辟了新的堆内存.所以基本类 ...

  4. 关于JS浅拷贝和深拷贝

    在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...

  5. JS - 浅拷贝与深拷贝的理解以及简单实现方法

    前几天撸项目代码时, 由一个技术点间接牵扯出了这东西. 所以就来总结一下. 深拷贝 拷贝对象每个层级的属性. 作用的对象是 js中引用类型的对象,基本类型没有涉及. 本质上将引用类型的对象在堆上重新开 ...

  6. js 中的 深拷贝与浅拷贝

    js在平时的项目中,赋值操作是最多的:比如说: var person1 = { name:"张三", age:18, sex:"male", height:18 ...

  7. js变量浅拷贝 深拷贝

    js的变量分为简单数据类型和复杂数据类型(即引用类型). 简单数据类型在内存中占据着固定大小的空间,被保存在栈内存中,在简单数据类型中,当一个变量指向另一个变量时,只是创建了值的副本,两个变量只是占用 ...

  8. 一篇文章理解JS数据类型、深拷贝和浅拷贝

    前言 笔者最近整理了一些前端技术文章,如果有兴趣可以参考这里:muwoo blogs.接下来我们进入正片: js 数据类型 六种 基本数据类型: Boolean. 布尔值,true 和 false. ...

  9. js 中的深拷贝与浅拷贝

    在面试中经常会问到js的深拷贝和浅拷贝,也常常让我们手写,下面我们彻底搞懂js的深拷贝与浅拷贝. 在js中 Array 和 Object  这种引用类型的值,当把一个变量赋值给另一个变量时,这个值得副 ...

  10. js中的深拷贝和浅拷贝2

    所谓 深浅拷贝: 对于仅仅是复制了引用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个东西,彼此之间的操作会互相影响,为 浅拷贝. 而如果是在堆中重新分配内存,拥有不同的地址,但是值是一 ...

随机推荐

  1. Go语言中异常处理painc()和recover()的用法

    Go语言中异常处理painc()和recover()的用法 1.Painc用法是:用于抛出错误.Recover()用法是:将Recover()写在defer中,并且在可能发生panic的地方之前,先调 ...

  2. 解决Maven报Plugin execution not covered by lifecycle configuration

    来自:http://blog.csdn.net/xxd851116/article/details/25197373 环境     eclipse 4.3.0     maven 3.0.4     ...

  3. Influxdb数据存储

    环境: CentOS6.5_x64 InfluxDB版本:1.1.0 InfluxDB存储引擎看起来很像一个LSM Tree,它包含预写日志和类似存储在LSM Tree中的SSTables只读数据. ...

  4. go语言生成uuid

    操作系统: CentOS 6.9_x64 go语言版本: 1.8.3 问题描述 golang没有提供生成uuid的接口,但开发中确实需要uuid. 这里把看到的代码记录下,也方便我以后查阅. 解决方案 ...

  5. H5移动端JS操作LocalStorage方法

    LocalStorage和SessionStorage LocalStorage 是对Cookie的优化 没有时间限制的数据存储 在隐私模式下不可读取 大小限制在500万字符左右,各个浏览器不一致 在 ...

  6. SpringBoot集成redisson分布式锁

    官方文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 20180226更新:增加tryLock方法,建议后面去掉Distr ...

  7. Spark的 运行模式详解

    Spark的运行模式是多种多样的,那么在这篇博客中谈一下Spark的运行模式 一:Spark On Local 此种模式下,我们只需要在安装Spark时不进行hadoop和Yarn的环境配置,只要将S ...

  8. Ceontos6.X配置XDMCP远程调用图形化

    Linux一般不需要图形化,但是偶尔也是需要的,基于X11协议的图形化XDMCP很受推广,VNC也是很不错的... 前提: 关闭防火墙,不关闭需要配置177端口放行 关闭selinux,不关闭自己配置 ...

  9. ios中创建自己的框架

    如果你是IOS的新手,并有.net开发的背景(就像我一样),你可能到处去找,哪里有dll呢? 其实,IOS平台是使用框架而不是dll,框架里面包含公共头文件和二进制文件.很不幸,XCode并不支持你自 ...

  10. servlet 让浏览器输出中文,并成功打印出来.2种方法

    import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; impor ...