资讯专栏INFORMATION COLUMN

【进阶4-1期】详细解析赋值、浅拷贝和深拷贝的区别

silvertheo / 2594人阅读

摘要:展开语法木易杨通过代码可以看出实际效果和是一样的。木易杨可以看出,改变之后的值并没有发生变化,但改变之后,相应的的值也发生变化。深拷贝使用场景木易杨完全改变变量之后对没有任何影响,这就是深拷贝的魔力。木易杨情况下,转换结果不正确。

一、赋值(Copy)

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

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

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

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

// 木易杨
let a = "muyiy";
let b = a;
console.log(b);
// muyiy

a = "change";
console.log(a);
// change
console.log(b);
// muyiy

对引用类型进行赋操作,两个变量指向同一个对象,改变变量 a 之后会影响变量 b,哪怕改变的只是对象 a 中的基本类型数据。

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let b = a;
console.log(b);
// {
//     name: "muyiy",
//     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。浅拷贝之后基本类型数据 field2filed1 是不同属性,互不影响。但引用类型 refObj 仍然是同一个,改变之后会对另一个对象产生影响。

简单来说可以理解为浅拷贝只解决了第一层的问题,拷贝第一层的基本类型值,以及第一层的引用类型地址

2、浅拷贝使用场景

Object.assign()

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

有些文章说Object.assign() 是深拷贝,其实这是不正确的。

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let b = Object.assign({}, a);
console.log(b);
// {
//     name: "muyiy",
//     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: "muyiy",
//     book: {title: "You Don"t Know JS", price: "55"}
// } 

上面代码改变对象 a 之后,对象 b 的基本属性保持不变。但是当改变对象 a 中的对象 book 时,对象 b 相应的位置也发生了变化。

展开语法 Spread

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let b = {...a};
console.log(b);
// {
//     name: "muyiy",
//     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: "muyiy",
//     book: {title: "You Don"t Know JS", price: "55"}
// } 

通过代码可以看出实际效果和 Object.assign() 是一样的。

Array.prototype.slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 beginend(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

// 木易杨
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) 1、什么是深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

2、深拷贝使用场景

JSON.parse(JSON.stringify(object))

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don"t Know JS",
        price: "45"
    }
}
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
//     name: "muyiy",
//     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: "muyiy",
//     book: {title: "You Don"t Know JS", price: "45"}
// } 

完全改变变量 a 之后对 b 没有任何影响,这就是深拷贝的魔力。

我们看下对数组深拷贝效果如何。

// 木易杨
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、不能处理正则

undefinedsymbol 和函数这三种情况,会直接忽略。

// 木易杨
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

new Date 情况下,转换结果不正确。

// 木易杨
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"

解决方法转成字符串或者时间戳就好了。

// 木易杨
let date = (new Date()).valueOf();
// 1545620645915

JSON.stringify(date);
// "1545620673267"

JSON.parse(JSON.stringify(date));
// 1545620658688

正则情况下,

// 木易杨
let obj = {
    name: "muyiy",
    a: /"123"/
}
console.log(obj);
// {name: "muyiy", a: /"123"/}

let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "muyiy", a: {}}

PS:为什么会存在这些问题可以学习一下 JSON。

除了上面介绍的深拷贝方法,常用的还有jQuery.extend()lodash.cloneDeep(),后面文章会详细介绍源码实现,敬请期待!

四、总结
-- 和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变会使原数据一同改变 改变会使原数据一同改变
参考
js 深拷贝 vs 浅拷贝

Java 深拷贝和浅拷贝

MDN 之 Object.assign()

MDN 之展开语法

MDN 之 Array.prototype.slice()

进阶系列目录

【进阶1期】 调用堆栈

【进阶2期】 作用域闭包

【进阶3期】 this全面解析

【进阶4期】 深浅拷贝原理

【进阶5期】 原型Prototype

【进阶6期】 高阶函数

【进阶7期】 事件机制

【进阶8期】 Event Loop原理

【进阶9期】 Promise原理

【进阶10期】Async/Await原理

【进阶11期】防抖/节流原理

【进阶12期】模块化详解

【进阶13期】ES6重难点

【进阶14期】计算机网络概述

【进阶15期】浏览器渲染原理

【进阶16期】webpack配置

【进阶17期】webpack原理

【进阶18期】前端监控

【进阶19期】跨域和安全

【进阶20期】性能优化

【进阶21期】VirtualDom原理

【进阶22期】Diff算法

【进阶23期】MVVM双向绑定

【进阶24期】Vuex原理

【进阶25期】Redux原理

【进阶26期】路由原理

【进阶27期】VueRouter源码解析

【进阶28期】ReactRouter源码解析

交流

进阶系列文章汇总如下,内有优质前端资料,觉得不错点个star。

https://github.com/yygmind/blog

我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100733.html

相关文章

  • 进阶4-2】Object.assign 原理及其实现

    摘要:木易杨注意原始类型被包装为对象木易杨原始类型会被包装,和会被忽略。木易杨原因在于时,其属性描述符为不可写,即。木易杨解决方法也很简单,使用我们在进阶期中介绍的就可以了,使用如下。 引言 上篇文章介绍了赋值、浅拷贝和深拷贝,其中介绍了很多赋值和浅拷贝的相关知识以及两者区别,限于篇幅只介绍了一种常用深拷贝方案。 本篇文章会先介绍浅拷贝 Object.assign 的实现原理,然后带你手动实...

    layman 评论0 收藏0
  • JavaScript系列--析JavaScript解析赋值拷贝和深拷贝区别

    摘要:它将返回目标对象。有些文章说是深拷贝,其实这是不正确的。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。使用深拷贝的场景完全改变变量之后对没有任何影响,这就是深拷贝的魔力。 一、赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1、基本数据类型:赋值,赋值之后两个变量互不影响 2、引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有...

    laznrbfe 评论0 收藏0
  • 进阶4-3】面试题之如何实现一个深拷贝

    摘要:今天这篇文章我们来看看一道必会面试题,即如何实现一个深拷贝。木易杨注意这里使用上面测试用例测试一下一个简单的深拷贝就完成了,但是这个实现还存在很多问题。 引言 上篇文章详细介绍了浅拷贝 Object.assign,并对其进行了模拟实现,在实现的过程中,介绍了很多基础知识。今天这篇文章我们来看看一道必会面试题,即如何实现一个深拷贝。本文会详细介绍对象、数组、循环引用、引用丢失、Symbo...

    longmon 评论0 收藏0
  • js拷贝和深拷贝和应用场景

    摘要:而大多数实际项目中,我们想要的结果是两个变量初始值相同互不影响。所以就要使用到拷贝分为深浅两种深浅拷贝的区别浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。 为什么会用到浅拷贝和深拷贝 首先来看一下如下代码 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...

    MartinDai 评论0 收藏0
  • js拷贝和深拷贝和应用场景

    摘要:而大多数实际项目中,我们想要的结果是两个变量初始值相同互不影响。所以就要使用到拷贝分为深浅两种深浅拷贝的区别浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。 为什么会用到浅拷贝和深拷贝 首先来看一下如下代码 let a = b = 2 a = 3 console.log(a) console.log(b) let c = d = [1,2,3] let e = f = {a:...

    nemo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<