JavaScript中的深拷贝与浅拷贝

最近在面试和学习当中都遇到了关于深拷贝和浅拷贝的问题,由于之前做项目没有遇到过此类问题,这里做一个简单的记录,学习一下。

基本知识

前置知识:js的数据类型。

在JavaScript里面,数据类型可以分为两大类,基本类型和引用类型。

基本类型也就是简单的数据类型,比如Number, null, Boolean, String, undefined, Symbol,而引用类型通常是复合类型,比如Object, Array, Function, Date, RegExp等。

基本类型存储在栈中,以键值对的形式,引用类型的值保存在堆中,对象的引用保存在栈中,啥叫对象的引用呢,可以简单理解为这是个指针。访问的时候,我们不能访问堆,需要通过栈间接访问堆。

那么深拷贝和浅拷贝到底是什么东西呢?首先明确拷贝是什么。拷贝就是复制,创建一个新变量时使用原有变量的“内容”创建,复制一份新的。

在复制的过程中,问题就出现在引用类型里,我要复制的是对象的引用,还是对象的本身?这听起来似乎有点绕,可以换个方式理解:假如我要复制一个对象,我是要让复制后的对象也指向原来的内容,还是重新创建一份完全一样的新对象?

“当我的全身都被更换,就连我的大脑一起都被换掉时,那时候的我,还会是‘我’吗?”

这里面就牵扯到深拷贝和浅拷贝的概念了。所谓浅拷贝,就是在复制的时候只复制所有的引用,复制之后的新对象里,只是创建了一个新的访问原来位置的引用,而原来这个位置本身的内容,没有被重新创建。那么深拷贝就是与其相对应的,不仅引用被复制了,连引用指向的原位置的内容也被重新复制了一份,创建了一个新的。

那么既然这样,深拷贝就一定比浅拷贝好吗?在实际应用中是否应当全都使用深拷贝?其实不一定。深拷贝可能需要经过多层递归调用,效率通常会低一些,而有时候浅拷贝就能满足很多实际场景的要求。

浅拷贝实际应用

以下是几个例子。

Object.assign(),把任意多个源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

展开运算符...,与Object.assign()功能相同。

赋值运算符=,赋值只会赋给引用,算浅拷贝。

Array.prototype.concat(),返回一个新数组,但如果数组中有嵌套对象则为浅拷贝

Array.prototype.slice(),返回一个新数组,但如果数组中有嵌套对象则为浅拷贝

上面这些操作只能算是在一维或者在一层上进行了深拷贝,如果有二层以上则无法完全拷贝,所以算是浅拷贝了。

浅拷贝手动实现

1
2
3
4
5
6
7
8
9
10
function shallowCopy(obj) {
if (typeof obj !== 'object') return;
let newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key];
}
}
return newObj;
}

深拷贝实际应用

JSON.parse(JSON.stringify(obj)),这种JSON化的方式可以实现多维或多层对象的深拷贝,但会忽略undefined、任意的函数、symbol,所以这种方式不能完全解决深拷贝问题。

Object.create(obj)可以做到深拷贝。

深拷贝手动实现

1
2
3
4
5
6
7
8
9
10
function deepCopy(obj) {
if (typeof obj !== 'object') return;
let newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2018-2021 Shawn Zhou
  • Hexo 框架强力驱动 | 主题 - Ayer
  • 访问人数: | 浏览次数:

感谢打赏~

支付宝
微信