JavaScript 深拷贝(deep copy)和浅拷贝(shallow copy)
参考:
在编程语言中,数据或者值是存放在变量中的。拷贝的意思就是使用相同的值创建新的变量。
当我们改变拷贝的东西时,我们不希望原来的东西也发生改变。
深拷贝的意思是这个新变量里的值都是从原来的变量中复制而来,并且和原来的变量没有关联。
浅拷贝的意思是,新变量中存在一些仍然与原来的变量有关联的值。
JavaScript 数据类型
原始数据类型 (有的资料叫做基本数据类型):数字、字符串、布尔值、undefined、null
这些值被赋值后就和对应的变量绑定在一起。如果你拷贝这些变量,就是实实在在的拷贝。
b = a 就是一次拷贝,重新给 b 赋值,a 的值不会改变:
const a = 5
let b = a // this is the copy b = 6 console.log(b) //
console.log(a) //
复合数据类型(有的资料叫做引用数据类型)——对象 和 数组
技术上讲,数组也是对象。
这种类型的值,只在初始化的时候存储一次。赋值给变量也仅仅是创建了一个指向这个值的引用。
拷贝 b = a,改变 b 中的属性 pt 的值,a 中包含的 pt 的值也改变了,因为 a 和 b 实际上指向的是同一个对象:
const a = {
en: 'Hello',
de: 'Hallo',
es: 'Hola',
pt: 'Olà'
}
let b = a
b.pt = 'Oi'
console.log(b.pt) // Oi
console.log(a.pt) // Oi
上面这个例子就是一个浅拷贝。
新的对象有着原对象属性的一份精确拷贝。如果属性值是原始类型,拷贝的就是原始类型值,如果属性是引用类型,拷贝的就是内存地址,如果其中一个对象改变了这个地址或者改变了内存中的值,另一个对象的属性也会变化。
也就是说浅拷贝只拷贝了第一层的原始类型值,和第一层的引用类型地址。
浅拷贝的场景:
展开操作符 Spread operator
使用这个操作符可以将所有的属性值复制到新对象中。
const a = {
en: 'Bye',
de: 'Tschüss'
}
let b = {...a}
b.de = 'Ciao'
console.log(b.de) // Ciao
console.log(a.de) // Tschüss
还可以合并两个对象,比如 const c = { ...a, ...b}.
Object.assign()
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,然后返回目标对象。
第一个参数是被修改和最终返回的值,第二个参数是你要拷贝的对象。通常,只需要给第一个参数传入一个空对象,这样可以避免修改已有的数据。
const a = {
en: 'Bye',
de: 'Tschüss'
}
let b = Object.assign({}, a)
b.de = 'Ciao'
console.log(b.de) // Ciao
console.log(a.de) // Tschüss
拷贝数组
const a = [1,2,3]
let b = [...a]
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2
数组方法——map, filter, reduce
这些方法都可以返回新的数组:
const a = [1,2,3]
let b = a.map(el => el)
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2
在拷贝的过程中修改特定的值:
const a = [1,2,3]
const b = a.map((el, index) => index === 1 ? 4 : el)
console.log(b[1]) // 4
console.log(a[1]) // 2
Array.slice
使用array.slice() 或者 array.slice(0) 你可以得到原数组的拷贝。
const a = [1,2,3]
let b = a.slice(0)
b[1] = 4
console.log(b[1]) // 4
console.log(a[1]) // 2
嵌套对象或数组
就算使用了上面的方法,如果对象内部包含对象,那么内部嵌套的对象也不会被拷贝,因为它们只是引用。因此改变嵌套对象,所有的实例中的嵌套对象的属性都会被改变。所以说上面的场景全部都只实现了浅拷贝。
const a = {
foods: {
dinner: 'Pasta'
}
}
let b = {...a}
b.foods.dinner = 'Soup' // changes for both objects
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Soup
深拷贝
拷贝所有属性,并拷贝属性指向的动态分配的内存。深拷贝时对象和所引用的对象一起拷贝,相比浅拷贝速度较慢且花销大。拷贝对象和原对象互不影响。
对嵌套的对象进行深拷贝,一种方法是手动拷贝所有嵌套的对象。
const a = {
foods: {
dinner: 'Pasta'
}
}
let b = {foods: {...a.foods}}
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta
如果对象除了 foods 之外还有很多属性,仍然可以利用展开操作符,比如
const b = {...a, foods: {...a.foods}}.
如果你不知道这个嵌套结构的深度,那么手动遍历这个对象然后拷贝每个嵌套的对象就很麻烦了。
一个很简单的方法就是使用 JSON.stringify 和 JSON.parse
const a = {
foods: {
dinner: 'Pasta'
}
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta
但是这里要注意的是,你只能使用这种方法拷贝 JavaScript 原生的数据类型(非自定义数据类型)。
而且存在问题:
- 会忽略 undefined
- 会忽略 symbol
- 不能序列化函数
- 不能解决循环引用的对象
// 木易杨
let obj = {
name: 'muyiy',
a: undefined,
b: Symbol('muyiy'),
c: function() {}
}
console.log(obj);
// {
// name: "muyiy",
// a: undefined,
// b: Symbol(muyiy),
// c: ƒ ()
// } let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "muyiy"}
// 木易杨
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
拷贝自定义类型的实例
你不能使用 JSON.stringify 和 JSON.parse 来拷贝自定义类型的数据,下面的例子使用一个自定义的 copy() 方法:
class Counter {
constructor() {
this.count = 5
}
copy() {
const copy = new Counter()
copy.count = this.count
return copy
}
}
const originalCounter = new Counter()
const copiedCounter = originalCounter.copy()
console.log(originalCounter.count) //
console.log(copiedCounter.count) //
copiedCounter.count = 7
console.log(originalCounter.count) //
console.log(copiedCounter.count) //
如果实例中有其它对象的引用,就要在copy方法中使用 JSON.stringify 和 JSON.parse 。
除此之外,深拷贝方法还有 jQuery.extend() 和 lodash.cloneDeep()
总结:

JavaScript 深拷贝(deep copy)和浅拷贝(shallow copy)的更多相关文章
- 深拷贝(deep clone)与浅拷贝(shallow clone)
深拷贝(deep clone)与浅拷贝(shallow clone) 浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复 ...
- copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)
写在前面 其实看了这么多,总结一个结论: 拷贝的初衷的目的就是为了:修改原来的对象不能影响到拷贝出来得对象 && 修改拷贝出来的对象也不能影响到原来的对象 所以,如果原来对象就是imm ...
- 由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考
首先查看拷贝模块(copy)发现: >>> help(copy)Help on module copy:NAME copy - Generic (shallow and dee ...
- 浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)
首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数) 浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变: 深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象 ...
- Shallow copy and Deep copy
Shallow copy and Deep copy 第一部分: 一.来自wikipidia的解释: Shallow copy One method of copying an object is t ...
- Summary: Deep Copy vs. Shallow Copy vs. Lazy Copy
Object copy An object copy is an action in computing where a data object has its attributes copied t ...
- python中的shallow copy 与 deep copy
今天在写代码的时候遇到一个奇葩的问题,问题描述如下: 代码中声明了一个list,将list作为参数传入了function1()中,在function1()中对list进行了del()即删除了一个元素. ...
- javaScript 深拷贝、浅拷贝
在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...
- [CareerCup] 13.4 Depp Copy and Shallow Copy 深拷贝和浅拷贝
13.4 What is the difference between deep copy and shallow copy? Explain how you would use each. 这道题问 ...
- python deep copy and shallow copy
Python中对于对象的赋值都是引用,而不是拷贝对象(Assignment statements in Python do not copy objects, they create bindings ...
随机推荐
- SQL server 数据库中插入中文变???格式乱码的问题另一种容易忽略的情况(C#操作dapper)
1.先查查 VS2015 中的XXX.cs页面中编码格式,记事本打开另存一下,编码格式可能是ANSI改为unioncode. (中文前面加N或者改排序规则解决不了的情况有可能是以上原因.)
- I2C通信基本原理及其实现
I2C是一种总线式结构,它只需要SCL时钟信号线与SDA数据线,两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为常用的通信方式. 由于I2C采用的是主从式通信方式, ...
- CodeForces 587 E.Duff as a Queen 线段树动态维护区间线性基
https://codeforces.com/contest/587/problem/E 一个序列, 1区间异或操作 2查询区间子集异或种类数 题解 解题思路大同小异,都是利用异或的性质进行转化,st ...
- 自动备份远程mongodb数据库并拉取到本地
自动备份远程mongodb数据库并拉取到本地 目标: 远程服务器 .1中的mongodb数据拉回公司测试服务器中 .远程服务器中编写自动备份mongodb脚本 ①编写脚本 # vim /opt/bac ...
- SQLAlchemy+Flask-RESTful使用(一)
前言 开新坑啦.最近打算自己开一个资源聚合网站.就用Flask. 当然也使用了 Flask-RESTful和SQLAlchemy啦 写的过程中遇到过很多坑/觉得比较有意义的就写在这里. 变更记录 # ...
- Oracle建立全文索引详解
Oracle建立全文索引详解1.全文检索和普通检索的区别 不使用Oracle text功能,当然也有很多方法可以在Oracle数据库中搜索文本,比如INSTR函数和LIKE操作: SELECT *FR ...
- Topologies on product spaces of $\mathbb{R}$ and their relationships
In this post, I will summarise several topologies established on the product spaces of \(\mathbb{R}\ ...
- Quartz+JAVA+Servlet实现任务调度系统(简洁)
前言 该系统使用场景: 在12306上买了一张火车票,30分钟内需要支付(需要添加一个倒计时),30分钟还没有支付就请求取消订单的接口(自动根据url请求),如果支付了收到了支付的回调通知后,就删除计 ...
- 了解javascript里面的 封装
// 封装 //生成实例对象的原始模式 //假如我们把一个动物看成一个对象 var cat = { //那么它有名字和颜色两个属性 name:'', color:'' }; //接下来我们根据原形对象 ...
- Angular6 用户自定义标签开发
参考地址:https://www.jianshu.com/p/55e503fd8307