文章首发于sau交流学习社区

一、赋值(Copy)

赋值是将某一数值或对象赋给某个变量的过程,分为:

1、基本数据类型:赋值,赋值之后两个变量互不影响

2、引用数据类型:赋**址**,两个变量具有相同的引用,指向同一个对象,相互之间有影响

对基本类型进行赋值操作,两个变量互不影响。

// saucxs
let a = "saucxs";
let b = a;
console.log(b); // saucxs a = "change";
console.log(a); // change console.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();
// 1545620645915 JSON.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()

浅析JavaScript解析赋值、浅拷贝和深拷贝的区别的更多相关文章

  1. Python FAQ2:赋值、浅拷贝、深拷贝的区别?

    在Python编程过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出现一些问题.所以,在这里按个人的理解谈谈它们之间的区别. 一.赋值(assignment) 在<P ...

  2. 对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解

    引用:https://www.jb51.net/article/142775.htm 列表赋值: 1 2 3 4 5 6 7 >>> a = [1, 2, 3] >>&g ...

  3. C++浅拷贝和深拷贝的区别

    C++浅拷贝和深拷贝的区别 2012-04-24 21:22 11454人阅读 评论(6) 收藏 举报 c++deleteclass编译器c c++默认的拷贝构造函数是浅拷贝 浅拷贝就是对象的数据成员 ...

  4. javascript中的浅拷贝和深拷贝(拷贝引用和拷贝实例)

    作者:千锋教育链接:https://www.zhihu.com/question/23031215/answer/326129003来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  5. Javascript中的浅拷贝和深拷贝

    很多开发语言中都有浅拷贝和深拷贝的说法,这里简单区分一下它们在Javascript中的区别,以及jQuery中深拷贝的实现. 在谈浅拷贝和深拷贝之前,先要屡清楚Javascript中的按值访问和按引用 ...

  6. 关于python中赋值、浅拷贝、深拷贝之间区别的深入分析

    当重新学习了计算机基础课程<数据结构和算法分析>后再来看这篇自己以前写的博文,发现错误百出.python内置数据类型之所以会有这些特性,归根结底是它采用的是传递内存地址的方式,而不是传递真 ...

  7. JavaScript中浅拷贝和深拷贝的区别和实现

    深拷贝和浅拷贝的区别   浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存:    深拷贝(deep copy):复制并创建一个一摸一样的对象,不共 ...

  8. Javascript/js 的浅拷贝与深拷贝(复制)学习随笔

    js变量的数据类型值分基本类型值和引用类型值. 在ES6(ECMAScript6)以前,基本数据类型包括String.Number.Boolean.Undefined.Null. 基本类型值的复制(拷 ...

  9. Python中赋值、浅拷贝和深拷贝的区别

    前言文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http: ...

随机推荐

  1. MDCC2013会议笔记

    技术性的Topic听的不多,也没记多少东西. 下面这些是产品设计论坛的笔记: 互联网为实体行业带来:数据驱动,用户参与,快速验证想法 体验整合:线上与线下,产品与服务,运营与营销,用户和利益相关方体验 ...

  2. jvm垃圾回收(三)

    一.分代思想(年轻代.老年代.永久代): 1.一个新人(new对象)会优先在伊甸园(Eden区)出生,当伊甸园(Eden区)人口达到最大容量时,JVM会派MinorGC去看看哪些人还有价值 2.伊甸园 ...

  3. ios WKWebView 与 JS 交互实战技巧

    一.WKWebView 由于Xcode8发布之后,编译器开始不支持iOS 7了,这样我们的app也改为最低支持iOS 8.0,既然需要与web交互,那自然也就选择使用了 iOS 8.0之后 才推出的新 ...

  4. Java Collections类

    集合类是什么? List列表.Set集合.Map映射.工具类(Iterator迭代器.Enumeration枚举类.Arrays和Collections). Set 接口继承 Collection,但 ...

  5. 浅谈面试中的OOD面向对象设计问题

    转载自:http://baozitraining.org/blog/Object-oriented-design-question/ OO设计问题是电面或者onsite中常考的问题,尤其对以Java为 ...

  6. mysql学习 第二章 数据库的基本操作

    3.1   创建数据库 MySQL安装好之后,首先需要创建数据库,这是使用MySQL各种功能的前提.本章将详细介绍数据的基本操作,主要内容包括:创建数据库.删除数据库.不同类型的数据存储引擎和存储引擎 ...

  7. 【转】tomcat logs 目录下各日志文件的含义

    tomcat每次启动时,自动在logs目录下生产以下日志文件,按照日期自动备份   localhost.2016-07-05.txt   //经常用到的文件之一 ,程序异常没有被捕获的时候抛出的地方 ...

  8. How nginx "location if" works

    Nginx's if directive does have some weirdness in practice. And people may misuse it when they do not ...

  9. jmeter 监听器聚合报告说明:

    Label:表示定义HTTP请求名称 Samples:表示这次测试中一共发出了多少个请求. Average:平均响应时长---默认情况下是单个Request的平均响应时长,当使用了Transactio ...

  10. python实现邮件的发送

    一.163邮箱设置 进入163邮箱,点击设置中的pop3/smtp/imap 开启smtp服务,如果没有开启,点击设置,手机号验证后勾选开启即可,开启后图如下: 主要用到的就是smtp服务器:smtp ...