js知识版图-引用类型赋值、浅拷贝、深拷贝

630 阅读3分钟

前言

在实际项目开发中会遇到浅拷贝、深拷贝的问题,这也是面试经常问到的知识点,所以做个笔记汇总整理相关知识点。 

赋值、浅拷贝、深拷贝

数据类型

我们先来复习下数据类型相关知识: 此处引申知识点:基本数据类型

基本数据类型(原始类型):StringNumberBooleanUndefinedNullSymbol
基本数据类型的特点:直接存储在栈(stack)中的数据
引用数据类型:object、function、array
引用数据类型的特点:存储的是该对象在栈(stack)中的引用(指针),真实的数据存放在堆内存里,当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

赋值

这里所说的赋值是对象的引用赋值,当我们把一个对象赋值给一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

// 对象赋值
let obj1 = {
    'name':'wukongkong',
    'age':'18',
    'language':['1',['2','3']]
}
let obj2 = obj1
obj2.name = 'dasheng'
obj2.language[1][0] = 'no'
console.log('obj1',obj1) 
console.log('obj2',obj2)
此时obj1,obj2修改是联动的

image.png

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的

浅拷贝

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

// 浅拷贝
let obj1 = {
    'name':'wokongkong',
    'age':'18',
    'language':[1,[2,3]]
}
let obj2 = Object.assign({},obj1)
obj2.name = 'dasheng'
obj2.language[1][0] = '666'
console.log('obj1',obj1) 
console.log('obj2',obj2) 

image.png 由此可见浅拷贝只解决了第一层的问题,如果接下去的值中还有对象,两者享有相同的地址。

浅拷贝的实现方式

Object.assign()需要注意的是目标对象只有一层的时候,是深拷贝
Object.assign()只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址
展开运算符...

深拷贝

深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

深拷贝的实现方法

  • JSON.parse(JSON.stringify(object)) 局限性: 会忽略undefined 会忽略symbol 不能序列化函数 不能解决循环引用的对象
  • 热门的函数库lodash,也有提供_.cloneDeep用来做深拷贝
  • jquery提供一个$.extend可以用来做深拷贝
  • JSON.parse(JSON.stringify())
  • 手写递归方法 递归实现深拷贝的原理:要拷贝一个数据,我们肯定要去遍历它的属性,如果这个对象的属性仍是对象,继续使用这个方法,如此往复。