参考:

  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. [转] GloVe公式推导

    from: https://pengfoo.com/post/machine-learning/2017-04-11 GloVe(Global Vectors for Word Representat ...

  2. linux 如何截取一段时间内log日志

    截取一段时间内的log日志可以使用sed命令对log文件进行抽取操作: 1,sed查看某时间段到现在的系统日志:sed -n '/May 20 17/,$p' /var/log/messages | ...

  3. linux系统下完全卸载Jenkins

    1.关闭tomcat:./shutdown.sh 2.删除/webapps/jenkins下所有文件:rm -rf jenkins 3.删除配置文件:rm -rf /root/.jenkins/

  4. Win 10中使用图片查看器

    在Win10中,照片应用提供了时间线.专辑等更丰富的图片管理功能,但是对于基于文件夹打开浏览图片的方式显得笨拙, 放大缩小操作略繁琐,有时还会出现当前文件夹图片加载迟缓导致无法快速浏览的问题. 此时你 ...

  5. python安装过程中的一些问题

    因为看到大神的教程是基于python V2.7,下载该版本且安装成功. 安装目录: https://www.python.org/download/releases/2.7/ 根据系统进行安装包下载 ...

  6. Mysql常用命令大全 sql

    1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...

  7. form表单中button按钮返回上一页解决办法

    解决Form中button按钮不好用的问题解决办法. 方法一: 1.在Form表单标签中国增加一个属性,如下图,红框处 2.返回按钮样式 3.onclick方法需要跳转的页面,遮挡处为需要返回的页面 ...

  8. spring-mvc访问本地html文件

    项目中要用到在线预览word文档,刚开始考虑是要将word转成pdf文件,然后再直接在浏览器打开pdf文档即可, 但是项目部署在Linux下,在网上搜了一下没有找到合适的方法, 后来项目组讨论用POI ...

  9. vue组件路由守卫钩子函数(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave)

    用法:与mounted,created等同级并列. export default { data() { return { } }, methods: { go() { this.$router.pus ...

  10. selenium+python自动化测试,上传文件怎样实现

    其实上传图片操作与输入框的输入是一样的,一种是在输入框中输入字符信息,一种是在输入文件格式的信息,下面是用代码实现上传文件, from selenium import webdriverfrom ti ...