作为前端了解栈与堆是非常必要的,如果不能充分理解那么js的深拷贝、浅拷贝就没办法正确使用。

当然如果你是大学计算机专业相信你因该了解很透彻了,如果文章有不足之处请多多指教

一、栈与堆概念

(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;(通俗讲:自动分配的内存空间,它由系统自动释放
(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。(通俗讲:动态分配的内存,大小也不一定会自动释放

说到这就不得不提js数据类型

二、js数据类型

基本数据类型: Number、String、Boolean、Null、 Undefined、Symbol(ES6)这些类型可以直接操作保存在变量中的实际值。

引用类型: Function,Array,Object,当我们需要访问这三种引用类型的值时,首先得从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

三、栈与堆区别

1、缓存方式区别
  1).栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
  2).堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

2、堆栈数据结构区别

1).栈(数据结构):一种先进后出的数据结构。

2).堆(数据结构):堆可以被看成是一棵树,如:堆排序;

了解完栈与堆,我们来说说浅拷贝与深拷贝;上面我们介绍的数据的基本类型

四、基本数据类型(存放在栈中)

基本数据类型是指存放在中的简单数据段,数据大小确定,内存空间大小可以分配,它们是直接按值存放的,所以可以直接按值访问。基本数据类型的值是没有办法添加属性和方法的

案例一
let a = 10;
let b = a;
b = 20;
console.log(a); // 10值
console.log(b); // 20值

解释:let a = 10;let b =a;是 a 先取出 10,copy 一份放到 b 里面,改变 a 的值,b 的值是不变的,再把 a=20;时 b 的值还是 10,不发生改变

案例二
let arr = [1,2];
let arr1 =arr;
arr.push(3);
console.log(arr) // [1,2,3]
console.log(arr1) // [1,2,3]

解释:引用值是在栈内存里面放堆的地址,拷贝的也是地址,所以改变 arr,arr1 也变了,在栈中arr,arr1同时指向一个堆,

let arr = [1,2]; let arr1 =arr; arr = [1,3]; document.write(arr1)

arr = [1,3];是新建了一个新的房间。arr1 是 1,2,现在是插入新引入值”房间”,

会在堆里面重新申请一间房,并指向新房间

五、深拷贝

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

1、数组的深拷贝:

 slice(slice()操作数组时,不会对原数组有影响,会产出一个新的数组。)

 let arr1 = [1, 42, 5, 6]let arr2 = arr1.slice()arr2[0] = 100console.log(arr1) // [1, 42, 5, 6]console.log(arr2) // [100, 42, 5, 6]

concat  (concat()方法,能够连接两个数组,同样不会改变原来的数组。用一个空数组连接另一个数组,即可实现深拷贝)

let arr3 = ['cat', 'dog', 'pig']
let arr4 = [].concat(arr3)
arr3[2] = 'big pig'
console.log(arr3) // ['cat', 'dog', 'big pig']
console.log(arr4) // ['cat', 'dog', 'pig']

ES6 ...拓展运算符

let arr5 = [0, 0, 1]
let arr6 = [...arr5]
arr5[0] = 10000
console.log(arr5) // [10000, 0, 1]
console.log(arr6) // [0, 0, 1]
我经常在数组的深拷贝中用到。

Array.from

Array.from()方法能从一个类似数组(伪数组)或可迭代对象中返回一个新的数组实例。通过Array.from()方法能获取到一个数组的深拷贝

let arr7 = [1, 2, 3]
let arr8 = Array.from(arr7)
arr7[1] = 1000
console.log(arr7) // [1, 1000, 3]
console.log(arr8) // [1, 2, 3]

2、Object的深拷贝

Object.assign()

ES6的Object.assign() Object.assign(target, …sources)用于对象的合并,将源对象中的所有可枚举属性,复制到目标对象中,并返回合并后的目标对象。后来的源对象的属性值,将会覆盖它之前的对象的属性。

 let person = {name: 'xia',age: 25,height: 160}let otherPerson = Object.assign({}, person)person.age = 30console.log(person)console.log(otherPerson)

 Object.create

Object.create 使用现有的对象来创建一个新的对象。同样的,也只能解决第一层的复制。

let person = {name: "宁次",age: 20,sister: {name: "雏田",age: 18,},
}let person2 = Object.create(person)person2.name = "鸣人"alert(person.name) // 宁次, 第一层 okperson2.sister.name = "鸣妹"
alert(person.sister.name) // 鸣妹,第二层仍是浅拷贝

注意:Object.create 对于数组的深拷贝不理想。

 JSON.parse 和 JSON.stringify

先用 JSON.stringify 将对象序列化成字符串,再用 JSON.parse 解析回来,可以达到深拷贝的效果,并且对于多层对象同样有效

let person = {name: "宁次",age: 20,sister: {name: "雏田",age: 18,},
}let person2 = JSON.parse(JSON.stringify(person))person2.name = "鸣人"
alert(person.name) // 宁次, 第一层 okperson2.sister.name = "鸣妹"
alert(person.sister.name) // 雏田,多层对象依旧 OK

注意:JSON.parse 和 JSON.stringify 依旧不是完美的,像一些方法或用户自定义的类型则不能有效的拷贝。

let person = {name: "宁次",age: 20,sayHello() {alert("你好~")},
}let person2 = JSON.parse(JSON.stringify(person))person2.sayHello() // error:person2.sayHello is not a function

structuredClone

structuredClone 是内置的一个方法,对于多层对象也能很好地执行拷贝,但同样对方法或自定义对象无力。

let person = {name: "宁次",age: 20,sister: {name: "雏田",age: 18,},
}let person2 = structuredClone(person)person2.name = "鸣人"alert(person.name) // 宁次, 第一层 okperson2.sister.name = "鸣妹"
alert(person.sister.name) // 雏田,多层对象依旧 OK

对于方法或自定义对象无力无能为力。

let person = {name: "宁次",age: 20,sayHello() {alert("你好~")},
}// Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': sayHello() {alert("你好~")} could not be cloned
let person2 = structuredClone(person)

自己实现

自己实现的灵活度就很高,根据代码实现不同达到的效果与运行效率也会有所不同。

以下是三眼鸭通过递归实现的一个深拷贝函数,经测试能正确拷贝常见的多层对象、数组、方法。

// 深拷贝
function deepclone(obj) {function copyList(arr) {let result = []for (let item of arr) {result.push(this.deepclone(item))}return result}if (typeof obj === "object") {if (Array.isArray(obj)) {return copyList(obj)} else {let result = {}for (let key in obj) {result[key] = deepclone(obj[key])}return result}} else {return obj}
}let person = {name: "宁次",age: 20,sayHello() {alert("你好")},sister: {name: "雏田",age: 18,},arr: [1, 2, 3],
}let person2 = deepclone(person)person2.name = "鸣人"alert(person.name) // 宁次, 第一层 okperson2.sister.name = "鸣妹"
alert(person.sister.name) // 雏田,多层对象依旧 OKperson2.arr[0] = "x"
alert(person.arr[0]) // 1, 数组也 okperson2.sayHello() // 你好,方法也 ok

 lodash

let person = {name: "宁次",age: 20,sayHello() {alert("你好")},sister: {name: "雏田",age: 18,},arr: [1, 2, 3],
}let person2 = _.cloneDeep(person)person2.name = "鸣人"alert(person.name) // 宁次, 第一层 okperson2.sister.name = "鸣妹"
alert(person.sister.name) // 雏田,多层对象依旧 OKperson2.arr[0] = "x"
alert(person.arr[0]) // 1, 数组也 okperson2.sayHello() // 你好,方法也 ok

六、栈和堆的溢出六: 堆和栈的溢出

如果想要堆溢出,比较简单,可以循环创建对象或大的对象; 如果想要栈溢出,可以递归调用方法,这样随着栈深度的增加,JVM
(虚拟机)维持着一条长长的方法调用轨迹,直到内存不够分配,产生栈溢出。

总结:使用浅拷贝和深拷贝要取决于当时的场景

使用浅拷贝的情况下要留意对数据的修改,要想清楚修改后影响到所有引用的对象这个结果是否是你想要的,不是的话请使用深拷贝。
对于函数内部的代码则要尽量避免修改传入进来的对象,如果必须要修改,则要在函数说明中醒目提示。
也不要盲目频繁地使用深拷贝,以免造成大量的内存浪费。
对于深拷贝也不必选择大而全地完全拷贝的方法,如果只是单层对象或数组,简单地使用解构赋值就是最好的方法

js stack栈与heap堆的区别与含义相关推荐

  1. [Java]Stack栈和Heap堆的区别(终结篇)[转]

    首先分清楚Stack,Heap的中文翻译:Stack-栈,Heap-堆. 在中文里,Stack可以翻译为"堆栈",所以我直接查找了计算机术语里面堆和栈开头的词语: 堆存储: hea ...

  2. JAVA Stack栈和Heap堆的区别(转)

          首先分清楚Stack,Heap的中文翻译:Stack-栈,Heap-堆. 在中文里,Stack可以翻译为"堆栈",所以我直接查找了计算机术语里面堆和栈开头的词语:  堆 ...

  3. 如何给女朋友讲明白:Java中Stack(栈)与Heap(堆)

    背景 Java中Stack(栈)与Heap(堆)是面试中被经常问到的一个话题. 有没有对Java中Stack(栈)与Heap(堆)烂熟于心的童鞋,请举手!!!(怎么没人举手-) 这个时候蜗牛哥的对象弱 ...

  4. java内存stack heap_java内存解析-------stack(栈)和heap(堆)的理解

    学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. ...

  5. 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类

    目录 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类): #和 ...

  6. C++中栈内存和堆内存区别

    C++中栈内存和堆内存区别 栈内存:当对象** 所在的函数体**执行完毕时,栈内存里的数据就会被清理.如:Student std;(是栈内存) 堆内存:会一直存在,执行delete是会清楚.如果是指针 ...

  7. JavaScript栈内存和堆内存区别

    和栈这两个字我们已经接触多很多次,那么具体是什么存在栈中什么存在堆中呢?就拿JavaScript中的变量来说: 首先JavaScript中的变量分为基本类型和引用类型. 基本类型就是保存在栈内存中的简 ...

  8. JS中栈和堆的区别?

    目录 一.js的数据类型 二.什么是堆什么是栈 三.堆和栈的区别 四.总结 五.堆和栈的溢出 堆和栈是数据存储的一种结构 一.js的数据类型 为了更好容易的理解堆和栈,首先来复习一下js中的数据类型. ...

  9. 【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基本工作原理

    栈基本工作原理 导航 深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈 深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第 ...

最新文章

  1. 【Git】ubuntu上git commit提交后如何保存和退出类似vim的界面,回到命令行
  2. shingling算法——提取特征,m个hash函数做指纹计算,针对特征hash后变成m维向量,最后利用union-find算法计算相似性...
  3. Fast CGI 工作原理
  4. ffplay android 编译,ffmpeg android 平台编译
  5. 如何用Java编写最快的表达式评估器之一
  6. postgresql 高可用 etcd + patroni 之六 callback bind vip
  7. C# 对文本文件的几种读写方法
  8. 斐波那契数列(复习)
  9. iOS 使用CocoaPods
  10. CSS使图像等比例缩放兼容IE6、IE7、FF
  11. 安卓下最强的3款pdf阅读器测评
  12. 批处理跑Axapta多个主计划
  13. 业务系统监控解决方案
  14. 用Prolog解决数独
  15. 超过2T硬盘用不了,怎么办?
  16. 初识JavaScript,体验JS的美好
  17. 手机连接投影机的步骤_投影仪怎么连接手机 投影仪连接手机方法介绍【详细步骤】...
  18. word在图片上添加文字且不改变原图教程
  19. WebSSH在线编程 所需工具介绍
  20. PC3000可恢复的SSD固态硬盘支持列表

热门文章

  1. (三)python的文件操作
  2. 小白or初学者快速上手C++ And 职工管理系统
  3. Unity编辑器UnityEditor基础(二)
  4. 航班信息查询接口_调用示例
  5. 第79篇 C++实现未知假币重量的假币问题(一)三分法
  6. 拒绝拖延——《拖延心理学》读书笔记
  7. 任务接单小程序app软件开发需求知识
  8. edge和chrome
  9. js中获取某个月份有多少天
  10. 关于宏定义的一些使用方法和注意点总结