JS变量都存放在内存中,而内存给变量开辟了两块区域,分别为栈区域和堆区域
栈像个容器,容量小速度快
堆像个房间,容量较大


讲这些之前我们先说说基本数据类型引用数据类型

我们知道在js中的数据类型可以分为基本类型和引用类型。基本类型是存在栈内存中的,引用类型是存在堆内存中的,但是引用类型的引用还是存在栈内存中的。

let num1 = 1;
num1 = 2;
console.log(num1);  // 2

在这里插入图片描述

const p1 = {
    name: 'Tom',
    age: 20
}
const p2 = p1;
p2.name = 'oakley';
console.log(p1.name);  // "oakley"

在这里插入图片描述

基本类型 声明一个变量,多次赋值就会取取最后一个值
基本类型 可以直接复制,复制之后的内容和原内容没有什么联系,类似于开辟了一个新的空间
引用类型 直接赋值给另一个变量以后相互之间的修改会互相影响对方,进而引出浅拷贝与深拷贝的问题
基本类型 不能添加属性或者方法,而引用类型可以动态添加或删除属性/方法

学过数据结构的都知道 是一种 先进后出 的数据结构,栈内存是内存中用于存放临时变量的一片内存块。当声明一个基本变量时,它就会被存储到栈内存中。比如有这样一段代码,他们在栈内存中存储的形式如下表一样:

const a = 1;
const b = "1";
变量名变量值
b“1”
a1

而当其发生复制时,会把对应内存中的数据复制一份到新内存中,就像下面这样

const c = b
变量名变量值
c“1”
b“1”
a1

很显然,c,b两个变量占用了不同的存储空间,所以他们之间也并没有什么联系
栈内存的地址分配是连续的,所以在后续也不能对其进行进一步的扩充或者删除。

栈总结

栈内存的特点:存取速度快,但不灵活,同时由于结构简单,在变量使用完成后就可以将其释放,内存回收容易实现。

堆内存的存储不同与栈,虽然他们都是内存中的一片空间,但是堆内存存储变量时没有什么规律可言。

const p1 = {};
const p2 = {};
const p3 = {};

在这里插入图片描述

我们在访问引用类型时,需要在栈内存中查找 对应的地址,在去堆内存中取得真正的值,访问速度自然不及栈内存。

对引用类型进行复制

const p4 = p3;

在这里插入图片描述

可以看到,我们只是将地址复制了一遍,p4 和 p3 都是指向同一个地址,这就说明对 p4 进行修改时就会影响到 p3 的值。所以对引用类型进行复制时,应该把堆内存中的内容复制一遍,在将新地址赋值给新变量,这就涉及到深拷贝了, 那我就简单的实现下深拷贝:

深拷贝

// 第一种
const a = {};
const b = JSON.parse(JSON.stringfy(a));

// 第二种
function clone(obj, hash = new WeakMap()) {
  // 判断是否为 null 或者 typeof 返回类型是否为 object
  if (obj == null || typeof obj !== "object") return obj;
  else if (obj instanceof Date) return new Date(obj);
  else if (obj instanceof RegExp) return new RegExp(obj);
  
  // 判断集合是否有这个属性,有则直接 return obj
  if(hash.has(obj)) return hash.get(obj)
  const newObj = new obj.constructor();
    
  // 将属性和拷贝后的值作为一个map
  hash.set(obj, newObj);
    
  // 遍历 obj
  for (let key in Object.getOwnPropertyDescriptors(obj)) {
      // 过滤掉原型身上的属性
      if (obj.hasOwnProperty(key)) {
      	// 递归拷贝
        newObj[key] = clone(obj[key], hash);
      }
    
  }
  return newObj;
}

堆总结

堆内存的特点:使用灵活,可以动态增加或删除空间,但是存取比较慢

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐