浅析JavaScript解析赋值、浅拷贝和深拷贝的区别
文章首发于sau交流学习社区
一、赋值(Copy)
赋值是将某一数值或对象赋给某个变量的过程,分为:
1、基本数据类型:赋值,赋值之后两个变量互不影响
2、引用数据类型:赋**址**,两个变量具有相同的引用,指向同一个对象,相互之间有影响
对基本类型进行赋值操作,两个变量互不影响。
// saucxs
let a = "saucxs";
let b = a;
console.log(b); // saucxsa = "change";
console.log(a); // changeconsole.log(b); // saucxs
对引用类型进行赋**址**操作,两个变量指向同一个对象,改变变量 a 之后会影响变量 b,哪怕改变的只是对象 a 中的基本类型数据。
// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = a;
console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// }
通常在开发中并不希望改变变量 a 之后会影响到变量 b,这时就需要用到浅拷贝和深拷贝。
二、浅拷贝(Shallow Copy)
1、什么是浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
上图中,`SourceObject` 是原对象,其中包含基本类型属性 `field1` 和引用类型属性 `refObj`。浅拷贝之后基本类型数据 `field2` 和 `filed1` 是不同属性,互不影响。但引用类型 `refObj` 仍然是同一个,改变之后会对另一个对象产生影响。
简单来说可以理解为浅拷贝只解决了第一层的问题,拷贝第一层的**基本类型值**,以及第一层的**引用类型地址**。
2、浅拷贝使用场景
2.1 Object.assign()
`Object.assign()` 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
有些文章说`Object.assign()` 是深拷贝,其实这是不正确的。
// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = Object.assign({}, a);
console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "55"}
// }
上面代码改变对象 a 之后,对象 b 的基本属性保持不变。但是当改变对象 a 中的对象 `book` 时,对象 b 相应的位置也发生了变化。
2.2 展开语法 `Spread`
// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = {...a};
console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "55"}
// }
2.3 Array.prototype.slice方法
slice不会改变原数组,`slice()` 方法返回一个新的数组对象,这一对象是一个由 `begin`和 `end`(不包括`end`)决定的原数组的**浅拷贝**。
// saucxs
let a = [0, "1", [2, 3]];
let b = a.slice(1);
console.log(b);
// ["1", [2, 3]]a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]console.log(b);
// ["1", [4, 3]]
可以看出,改变 `a[1]` 之后 `b[0]` 的值并没有发生变化,但改变 `a[2][0]` 之后,相应的 `b[1][0]` 的值也发生变化。
说明 `slice()` 方法是浅拷贝,相应的还有`concat`等,在工作中面对复杂数组结构要额外注意。
三、深拷贝(Deep Copy)
3.1 什么是深拷贝?
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
3.2 使用深拷贝的场景
3.2.1 JSON.parse(JSON.stringify(object))
// saucxs
let a = {name: "saucxs",book: {title: "You Don't Know JS",price: "45"}
}
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "45"}
// } a.name = "change";
a.book.price = "55";
console.log(a);
// {
// name: "change",
// book: {title: "You Don't Know JS", price: "55"}
// } console.log(b);
// {
// name: "saucxs",
// book: {title: "You Don't Know JS", price: "45"}
// }
完全改变变量 a 之后对 b 没有任何影响,这就是深拷贝的魔力。
我们看下对数组深拷贝效果如何。
// saucxs
let a = [0, "1", [2, 3]];
let b = JSON.parse(JSON.stringify( a.slice(1) ));
console.log(b);
// ["1", [2, 3]]a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]console.log(b);
// ["1", [2, 3]]
对数组深拷贝之后,改变原数组不会影响到拷贝之后的数组。
但是该方法有以下几个问题:
(1)会忽略 `undefined`
(2)会忽略 `symbol`
(3)不能序列化函数
(4)不能解决循环引用的对象
(5)不能正确处理`new Date()`
(6)不能处理正则
其中(1)(2)(3) `undefined`、`symbol` 和函数这三种情况,会直接忽略。
// saucxs
let obj = {name: 'saucxs',a: undefined,b: Symbol('saucxs'),c: function() {}
}
console.log(obj);
// {
// name: "saucxs",
// a: undefined,
// b: Symbol(saucxs),
// c: ƒ ()
// }let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "saucxs"}
其中(4)循环引用会报错
// saucxs
let obj = {a: 1,b: {c: 2,d: 3}
}
obj.a = obj.b;
obj.b.c = obj.a;let b = JSON.parse(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSON
其中(5)* `new Date` 情况下,转换结果不正确。
// saucxs
new Date();
// Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)JSON.stringify(new Date());
// ""2018-12-24T02:59:25.776Z""JSON.parse(JSON.stringify(new Date()));
// "2018-12-24T02:59:41.523Z"
解决方法转成字符串或者时间戳就好了。
// saucxs
let date = (new Date()).valueOf();
// 1545620645915JSON.stringify(date);
// "1545620673267"JSON.parse(JSON.stringify(date));
// 1545620658688
其中(6)正则情况下
// saucxs
let obj = {name: "saucxs",a: /'123'/
}
console.log(obj);
// {name: "saucxs", a: /'123'/}let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "saucxs", a: {}}
PS:为什么会存在这些问题可以学习一下 JSON。
除了上面介绍的深拷贝方法,
常用的还有`jQuery.extend()` 和 `lodash.cloneDeep()`,后面文章会详细介绍源码实现。
四、总结
和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 | |
赋值 | 是 | 改变会使原数据一起改变 | 改变会使原数据一起改变 |
浅拷贝 | 否 | 改变不会使原数据一起改变 | 改变会使原数据一起改变 |
深拷贝 | 否 | 改变不会使原数据一起改变 | 改变不会使原数据一起改变 |
五、参考
1、深拷贝和浅拷贝
2、MDN之Object.assign()
3、MDN之展开语法
4、MDN之Array.stypetype.slice()
转载于:https://www.cnblogs.com/chengxs/p/10788442.html
浅析JavaScript解析赋值、浅拷贝和深拷贝的区别相关推荐
- python赋值浅拷贝和深拷贝的区别_python赋值、浅拷贝、深拷贝区别
在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出现一些问题.所以,在这里按个人的理解谈谈它们之间的区别. 一.赋值(assignment) 在<Py ...
- js 浅拷贝直接赋值_浅析JavaScript解析赋值、浅拷贝和深拷贝的区别
一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1.基本数据类型:赋值,赋值之后两个变量互不影响 2.引用数据类型:赋**址**,两个变量具有相同的引用,指向同一个对象,相互之 ...
- python赋值浅拷贝和深拷贝的区别_浅拷贝、深拷贝和普通赋值操作有什么区别?...
对于不可变对象,创建副本没有多大意义,因为它们不会改变.对于可变对象^{},^{}和^{}的行为不同.让我们用例子来讨论它们. 赋值操作只是将源的引用赋值给目标,例如:>>> i = ...
- 详细解析赋值、浅拷贝和深拷贝的区别
详细解析赋值.浅拷贝和深拷贝的区别 一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为下面 2 部分 基本数据类型:赋值,赋值之后两个变量互不影响 引用数据类型:赋址,两个变量具有相 ...
- 将对象拷贝一份备用,改变原对象的属性时,为啥备份数据也改变了?(赋值、浅拷贝和深拷贝的区别)
前言 前两天在写代码时碰到一个很奇怪的问题,我有一个对象person = {name: ''tianxin, age: 18};,我需要将person目前的数据拷贝一份备用let copyPerson ...
- Python 赋值、浅拷贝、深拷贝的区别?
http://songlee24.github.io/2014/08/15/python-FAQ-02/ 在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出 ...
- python数组赋值_对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解
对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解 列表赋值: >>> a = [1, 2, 3] >>> b = a >>> print ...
- 浅拷贝与深拷贝的区别(详解)
浅拷贝与深拷贝的区别 html 浅拷贝与深拷贝 一.数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型. 基 ...
- 彻底理解Python中浅拷贝和深拷贝的区别
目录 前言 1. 浅拷贝和深拷贝的概念 2. is和==的区别 3. 赋值操作 4. copy模块里面的copy()方法 5. copy模块里面的deepcopy()方法 6.字典自带的copy方法 ...
最新文章
- 拨号用户如何使用局域网上的LinuxSamba服务器
- 如何用ASP.NET加密Cookie数据过程分析
- 最详细的CISCO路由器配置命令及方法
- Asp.Net生命周期系列三
- 视觉SLAM笔记(25) 拼接点云
- NSURLSession访问HTTPS网站
- poj2828 Buy Tickets
- 力扣两数之和jAVA_力扣----1.两数之和(JavaScript, Java实现)
- 黑苹果 dmg,cdr和iso的区别
- 国内有哪些不错的CV(计算机视觉)团队
- Jenkins自动集成
- Android BLE操作成功或失败status code对应解释
- [转载]matlab中分号、冒号、逗号等常用标点符号的作用
- 英语论文写作技巧-2
- 软件测试面试屡屡失败,面试官总是说逻辑思维混乱,怎么办?
- 电梯门禁系统服务器一般在哪,电梯控制系统与门禁系统的区别
- 《人性的弱点》第一章 待人接物的三大原则:读后感
- [乐意黎原创] 2014年全国和云南省中级会计师成绩、分数段、过关率及年龄段统计分析
- Linux防火墙常用操作及端口开放
- css less 文件:global的写法
热门文章
- Gridview一次更新所有记录
- fst java_java快速序列化库FST
- npm升级依赖包_Taro跨端开发之依赖管理
- 用python做数据分析流程图_使用Pyecharts进行高级数据可视化
- win7 mysql php apache myadmin_windows下Apache+mysql+php+phpMyAdmin的安装及配置 | 学步园
- android通过adb shell播放音乐
- CPU上下文切换(系统调用、进程上下文、线程上下文、中断上下文)
- JavaScript | 使用提示从用户输入值
- 加载一张照片,可选择是否另存为
- Java——设计模式(简单工厂模式)