一、什么是值类型?

二、什么是引用类型?

三、使用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. WebRTC互联网实时通信

    muaz-khan/WebRTC-Experiment WebRTC, WebRTC and WebRTC. Everything here is all about WebRTC!! Updated ...

  2. Pause Web Sessions

    To pause specific sessions, add rules using FiddlerScript to the OnBeforeRequest function (except wh ...

  3. Android 内存泄漏总结(转)

    Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题.内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却 ...

  4. Oracle单引号双重角色——字符串引用与转义

    单引号双重角色——字符串引用与转义      在ORACLE中,单引号有两个作用,一是字符串是由单引号引用,二是转义,开始学习时动态构成SQL时经常不解.单引号的使用是就近配对,即就近原则.而在单引号 ...

  5. 〖Linux〗使用命令行切换触摸板的状态on/off/toggle

    最近发现在Ubuntu13.10中使用Fn+F9对触摸板的控制操作不灵了: 并且在黑夜.外置键盘时,按下这个组合键也很不方便,由此便想到使用命令行来切换触摸板状态: 脚本:~/bin/touchpad ...

  6. 虚拟机在NAT模式下主机与宿主机的ip配置

    现有如下环境: 宿主机:win7 (IP为100.101.201.20) 虚拟机: SUSE LinuxEterprise 11 64bit (IP为100.101.201.23) 连接模式如下图: ...

  7. django之创建第8-3个项目-数据库数据提取之高级操作

    1.配置test2.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  8. Dlib三维点云示例

    Dlib三维点云示例 源代码来自Dlib的示例代码http://dlib.net/3d_point_cloud_ex.cpp.html 在windows下需要链接winmm``comctl32``gd ...

  9. bootstrap table + spring + springmvc + mybatis 实现从前端到后端的表格分页

    1.使用准备 前台需要的资源文件,主要有Bootstrap3相关css.js以及bootstrap Table相关css.js: <-- 样式 --> <link rel=" ...

  10. Iphone开发基本UI组件

    在IOS中的基础UI组件,IPHONE的组件大多以UI开头,这种独树一帜的命名方法极有可能是为了与其他系统的组件进行区分,避免混淆引起冲突:  下面描述一下IOS的基础UI控件以及和Android中的 ...