js 保存文件_「实践」浅析JS堆、栈、执行栈和EventLoop

47d78bbda8476473631a50b7fb570975.png

作者:河畔一角

转发链接:https://mp.weixin.qq.com/s/Er4ZoRqSy9upZQbI0k14BA

前言

现在前端面试,大多都会问到关于事件循环、执行栈等问题,本文通过案列、图片等形式给大家讲解这些概念,如果认真看完,我相信90%的同学可以彻底理解。

JS内存机制

JavaScript具有自动垃圾回收机制,周期性会检查没有使用的变量,进行回收释放。所以在闭包中,如果引用了外部的变量,则无法进行释放和回收,一般会传参进去。

垃圾回收:找出那些不再继续使用的变量,然后释放其占用的内存,垃圾收集器会按照固定的时间间隔周期性地执行这一操作。

在JS中,每一个数据都需要一个内存空间,内存空间又分为栈内存(stack)与堆内存(heap)。

栈内存一般储存基础数据类型

Number String Null Undefined Boolean Symbol

看一个例子:

var num = 1 

我们定义一个变量num,系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问。

数据在栈内存中的存储与使用方式类似于数据结构中的堆栈数据结构,遵循 后进先出的原则。

堆内存一般储存引用数据类型

var user = { name:'jack' }var arr = [1,3,5]

JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。

通过下面这张图,我们就能直观理解。

3a301b347f18eb51c347ce7dd3d8e19c.png
var num = 1;   // 栈 var name = '前端未来'; // 栈// 变量user存在于栈中,{name:'河畔'}存在于堆内存中var user = { name: '河畔' }; // 变量arr存在于栈中,[1, 2, 3] 作为对象存在于堆内存中var arr = [1, 3, 5]; 

因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从栈中获取了该对象的指针,然后再从堆内存中取得我们需要的数据。

所以,我们经常说:基本类型赋值相互不影响,引用类型赋值,会影响原对象。

一个例子就能看明白:

var a = 20;var b = a;b = 30;// a为20,b为30,值类型不影响console.log(a) var user = { name: '河畔' }; var info = user;info.name = 'Jack'// 打印为jack,指向同一个内存地址console.log(user.name)

总结:

  • JavaScript具备自动垃圾回收机制
  • JS内存分为堆内存和栈内存
  • 引用类型在栈中保存指针,在堆中保存对象值
  • 栈内存数据遵循先进后出

EventLoop

现在前端面试,大家都喜欢问EventLoop,但说实话,很多人看了无数篇文章,还是稀里糊涂,今天依然通过代码+图片的方式给大家演示效果。

为了更好的理解事件机制,我们需要先介绍执行栈。所有JS代码运行都是被放入执行中执行的,遵循进栈和出栈,直到栈被清空。

执行栈

JS 代码在运行前都会创建执行上下文,也可以理解为执行环境,JS 中有三种执行上下文:

  • 全局执行上下文,默认的,在浏览器中是 window 对象
  • 函数执行上下文,JS 的函数每当被调用时会创建一个上下文。
  • Eval 执行上下文,eval 函数会产生自己的上下文。

通常,我们的代码中都不止一个上下文,那这些上下文的执行顺序应该是怎样的?从上往下依次执行?

栈,是一种数据结构,遵循先进后出的原则。JS 中的执行栈就具有这样的结构,当引擎第一次遇到 JS 代码时,会产生一个全局执行上下文并压入执行栈,每遇到一个函数调用,就会往栈中压入一个新的上下文。引擎执行栈顶的函数,执行完毕,弹出当前执行上下文。

接下来,我们看一个例子:

function foo() {  console.log('1');  bar();  console.log('3');}function bar() {  console.log('2');}foo();

这个毫无疑问,大家都知道答案,执行栈是怎么调用的?

a7d24dd8adaa6ee1fcbf29acb26dc86c.png

首先执行这个JS文件,创建一个全局上下文,并压入执行栈中,当 foo() 函数被调用时,将 foo 函数的执行上下文压入执行栈,接着执行输出 ‘1’;当 bar() 函数被调用,将 bar 函数的执行上下文压入执行栈,接着执行输出 ‘2’;bar() 执行完毕,被弹出执行栈,foo() 函数接着执行,输出 ‘3’;foo() 函数执行完毕,被弹出执行栈,最后清空整个执行栈。这就是先进后出,Foo先被压入执行栈,最后才被弹出执行栈

EC就是Execute Context执行上下文

总结:

  • 所有JS代码运行,都需要放入执行栈中.
  • 执行上下文包含了三种(全局、函数、eval)
  • 栈是一种数据结构,遵循先进后出

接下来,看一道经典面试题

console.log(1)new Promise(function(resolve){    console.log(3)    resolve(100)}).then(function(data){    console.log(data)})setTimeout(function(){    console.log(4);})console.log(2)

上面的面试题打印结果:1 3 2 100 4

你能说出具体执行步骤吗?

我们都知道JS本身是单线程的,一次只能干一件事儿,那么像定时器、Promise这些它是怎么处理的呢?实际上就要介绍quene队列了。

主线程执行同步代码块,遇到定时器、Promise等异步任务时,会创建事件队列,把他们丢到队列里面去,等主线程执行完成后,再回去执行队列中的task.

所以,我们的JS执行主要包括同步任务和异步任务,整个同步任务会进入到主线程中,最后放入执行栈中执行,就是我们上面给大家讲解的执行栈,接下来关注异步任务。

浏览器的JS中,异步任务又分为宏任务和微任务,宏任务和微任务都是属于队列,而不是放在栈中。微任务会创建一个队列,宏任务会创建一个队列,而主线程执行完以后,会优先执行微任务,把微任务全部放到执行栈中执行,最后再从宏任务中取出一个放入执行栈进行执行,执行完后,再取一个,直到执行完所有的宏任务。

接下来看张图:

36880dd68d09d31b9f559d52ca77ed1b.png

左侧JS图包含了堆和栈,所有的代码都会被放入栈中执行,我们叫执行栈,执行栈是一条主线程,先执行同步任务,中间遇到ajax、setTimeout等异步任务后,会push到queue中,最后再把队列中事件取出来放入执行中执行,依次循环这个过程。

那我们再来看上面的例子:

console.log(1)new Promise(function(resolve){    console.log(3)    resolve(100)}).then(function(data){    console.log(data)})setTimeout(function(){    console.log(4);})console.log(2)
  1. 创建全局上下文,并压入执行栈中
  2. 把同步代码console.log压入执行栈中执行,打印1,并出栈
  3. 把同步代码new Promise压入执行栈中执行,打印3,并出栈

注意:new Promise 这个过程实际上是同步的,只有resolve和reject后才是异步

  1. then属于异步任务,push到微任务队列中,并创建事件
  2. setTimeout属于异步任务,push到宏任务队列中,并创建事件

注意:宏任务和微任务是两个队列

  1. 把同步代码console.log压入执行栈中执行,打印2,并出栈

到此整个执行栈只剩下全局上下文,没有可以执行的代码了

  1. 微任务先执行,所以把微任务队列中的事件全部拿出来,放入执行栈进行执行。打印100,并出栈
  2. 从宏任务队列中,只取出一个事件放入执行栈中执行,打印4

那我们把上面例子改造一下:

console.log(1)new Promise(function(resolve){    console.log(3)    resolve(100)}).then(function(data){    console.log(data)}).then(function(){    console.log(200)})setTimeout(function(){    console.log(4);})setTimeout(function(){    console.log(5);})console.log(2)

有2个then,2个setTimeout,此时学完后,您觉得应该打印多少?答案是:1 3 2 100 200 4 5

整个文章到此结束,希望大家能够看懂!

总结:

  • JavaScript具备自动垃圾回收机制
  • JS内存分为堆内存和栈内存
  • 引用类型在栈中保存指针,在堆中保存对象值
  • 所有JS代码运行,都需要放入执行栈中.
  • 执行代码前,会先创建执行上下文
  • 执行上下文包含了三种(全局、函数、eval)
  • 同步任务先执行,异步任务放队列
  • 微任务先执行,宏任务后执行
  • 微任务全部拉入执行栈,宏任务一次拉一个
  • 栈是先进后出,队列是先进先出

有多少人能理解加粗的文字,上面实际上通过图片和代码给大家演示过了。

以上是为大家整理的堆、栈、事件机制等概念,希望大家面试的时候能够说出个张三、李四来,不要再被对方diss了。

推荐JavaScript经典实例学习资料文章

《Node + H5 实现大文件分片上传、断点续传》

《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》

《【实践总结】关于小程序挣脱枷锁实现批量上传》

《手把手教你前端的各种文件上传攻略和大文件断点续传》

《字节跳动面试官:请你实现一个大文件上传和断点续传》

《谈谈前端关于文件上传下载那些事【实践】》

《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》

《最全的 JavaScript 模块化方案和工具》

《「前端进阶」JS中的内存管理》

《JavaScript正则深入以及10个非常有意思的正则实战》

《前端面试者经常忽视的一道JavaScript 面试题》

《一行JS代码实现一个简单的模板字符串替换「实践」》

《JS代码是如何被压缩的「前端高级进阶」》

《前端开发规范:命名规范、html规范、css规范、js规范》

《【规范篇】前端团队代码规范最佳实践》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《几个非常有意思的javascript知识点总结【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《JavaScript实现图片合成下载》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《开源了一个 JavaScript 版敏感词过滤库》

《送你 43 道 JavaScript 面试题》

《3个很棒的小众JavaScript库,你值得拥有》

《手把手教你深入巩固JavaScript知识体系【思维导图】》

《推荐7个很棒的JavaScript产品步骤引导库》

《Echa哥教你彻底弄懂 JavaScript 执行机制》

《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》

《深入解析高频项目中运用到的知识点汇总【JS篇】》

《JavaScript 工具函数大全【新】》

《从JavaScript中看设计模式(总结)》

《身份证号码的正则表达式及验证详解(JavaScript,Regex)》

《浏览器中实现JavaScript计时器的4种创新方式》

《Three.js 动效方案》

《手把手教你常用的59个JS类方法》

《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》

《深入浅出讲解 js 深拷贝 vs 浅拷贝》

《手把手教你JS开发H5游戏【消灭星星】》

《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》

《手把手教你全方位解读JS中this真正含义【实践】》

《书到用时方恨少,一大波JS开发工具函数来了》

《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》

《手把手教你JS 异步编程六种方案【实践】》

《让你减少加班的15条高效JS技巧知识点汇总【实践】》

《手把手教你JS开发H5游戏【黄金矿工】》

《手把手教你JS实现监控浏览器上下左右滚动》

《JS 经典实例知识点整理汇总【实践】》

《2.6万字JS干货分享,带你领略前端魅力【基础篇】》

《2.6万字JS干货分享,带你领略前端魅力【实践篇】》

《简单几步让你的 JS 写得更漂亮》

《恭喜你获得治疗JS this的详细药方》

《谈谈前端关于文件上传下载那些事【实践】》

《面试中教你绕过关于 JavaScript 作用域的 5 个坑》

《Jquery插件(常用的插件库)》

《【JS】如何防止重复发送ajax请求》

《JavaScript+Canvas实现自定义画板》

《Continuation 在 JS 中的应用「前端篇」》

作者:河畔一角

转发链接:https://mp.weixin.qq.com/s/Er4ZoRqSy9upZQbI0k14BA

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值