参考:

  1. 【进阶4-1期】详细解析赋值、浅拷贝和深拷贝的区别
  2. How to differentiate between deep and shallow copies in JavaScript

在编程语言中,数据或者值是存放在变量中的。拷贝的意思就是使用相同的值创建新的变量。

当我们改变拷贝的东西时,我们不希望原来的东西也发生改变。

深拷贝的意思是这个新变量里的值都是从原来的变量中复制而来,并且和原来的变量没有关联。

浅拷贝的意思是,新变量中存在一些仍然与原来的变量有关联的值。

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 原生的数据类型(非自定义数据类型)。

而且存在问题

  1. 会忽略 undefined
  2. 会忽略 symbol
  3. 不能序列化函数
  4. 不能解决循环引用的对象
// 木易杨
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)的更多相关文章

  1. 深拷贝(deep clone)与浅拷贝(shallow clone)

    深拷贝(deep clone)与浅拷贝(shallow clone) 浅复制(浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复 ...

  2. copy&mutableCopy 浅拷贝(shallow copy)深拷贝 (deep copy)

    写在前面 其实看了这么多,总结一个结论: 拷贝的初衷的目的就是为了:修改原来的对象不能影响到拷贝出来得对象 && 修改拷贝出来的对象也不能影响到原来的对象 所以,如果原来对象就是imm ...

  3. 由Python的浅拷贝(shallow copy)和深拷贝(deep copy)引发的思考

    首先查看拷贝模块(copy)发现: >>> help(copy)Help on module copy:NAME    copy - Generic (shallow and dee ...

  4. 浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)

    首先,深拷贝和浅拷贝针对的是对象类型(对象,数组,函数) 浅拷贝指的是只是拷贝了对象的引用地址,彼此之间高耦合,一个改变,另一个可能也随之改变: 深拷贝是指只是完整的将变量的值拷贝过来,是一个新的对象 ...

  5. Shallow copy and Deep copy

    Shallow copy and Deep copy 第一部分: 一.来自wikipidia的解释: Shallow copy One method of copying an object is t ...

  6. 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 ...

  7. python中的shallow copy 与 deep copy

    今天在写代码的时候遇到一个奇葩的问题,问题描述如下: 代码中声明了一个list,将list作为参数传入了function1()中,在function1()中对list进行了del()即删除了一个元素. ...

  8. javaScript 深拷贝、浅拷贝

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

  9. [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. 这道题问 ...

  10. python deep copy and shallow copy

    Python中对于对象的赋值都是引用,而不是拷贝对象(Assignment statements in Python do not copy objects, they create bindings ...

随机推荐

  1. A previous installation of Qt5 Visual Studio Add-in was detected. Please uninstall it before running this installer解决办法

    前段时间在安装Qt Visual Studio插件的时候,安装到一半不小心中止了,结果后来怎么安装都不行,提示已经安装了,要先卸载, 可是到哪里都找不到有卸载的,因为压根就没有安装完成.这可害苦我了. ...

  2. Selective Kernel Network

    senet: https://arxiv.org/abs/1709.01507 sknet: http://arxiv.org/abs/1903.06586 TL, DR Selective Kern ...

  3. 前端笔记知识点整合之JavaScript(三)关于条件判断语句、循环语句那点事

      一.条件分支语句 条件分支语句,也叫作条件判断语句,就是根据某种条件执行某些语句,不执行某些语句. JS中有三种语法是可以表示条件分支的 1.1 if……else…… 条件分支的主力语法,这个主力 ...

  4. [HTTP]POST报文中Content-Type对正文解析的影响

    概述 在POST请求中,理论上请求端程序可以发送任意格式报文正文,但是最好在报文头Content-Type字段标明正文的格式,方便接收端根据Content-Type正确处理正文. 传统HTML-for ...

  5. Jace 上新建 Station 配置 笔记

    1.Station站点的结构图 2.niagara 结构框架图 Niagara 系统的架构是围绕着“以组件(Component)为导向的编程”为核心设计的.组件(Component)是使用Java 编 ...

  6. 树链剖分——线段树区间合并bzoj染色

    线段树区间合并就挺麻烦了,再套个树链就更加鬼畜,不过除了代码量大就没什么其他的了.. 一些细节:线段树每个结点用结构体保存,pushup等合并函数改成返回一个结构体,这样好写一些 struct Seg ...

  7. 4.2 Oracle Dataguard failover 操作步骤

    在11g里面,Oracle认为最理想的情况是,虽然Oracle数据库不能打开,但是可以启动到 mount状态.Mount状态之所以重要,就在于如果可以到这个阶段,控制文件control_file就可以 ...

  8. WPF自定义轮播控件

     闲得蛋疼做了一个WPF制作轮播动画(随机动画),勉强可以看,写个随笔留个脚印.  效果图:

  9. windows jdk安装

    先去官网下载安装包 x86 32位 x64 64位 下载地址 安装jdk 安装目录默认c盘 配置系统环境 JAVA_HOME环境变量.作用:它指向jdk的安装目录,Eclipse/NetBeans/T ...

  10. Python3.2.3官方文档(中文版)

    所属网站分类: 资源下载 > python电子书 作者:熊猫烧香 链接:http://www.pythonheidong.com/blog/article/66/ 来源:python黑洞网,专注 ...