我们需要了解的js引擎整理

几个问题

1. 前端代码的运行环境?
    
2. js是单线程的?
    
3. js中为啥要有异步,异步来源?

4. js怎么实现异步的?

为什么它会有这么多问题,参考
Javascript诞生记-参考阮一峰老师

运行环境

1. 客户端浏览器
2. [桌面端](https://electronjs.org/)
3. 运行在服务器端(node)
ps:还有移动端(rn,weex,混合开发),不讨论这么多,主要说浏览器端

js为啥是单线程?

我们都知道,浏览器引擎负责用户界面和呈现引擎之间传送指令,假如现在有两个线程,
一个线程的命令是,用户点击某个按钮,按钮变色,
另一个线程是用户点击这个按钮,这个按钮隐藏
假如,js是多线程的, 当用户点击这个按钮,它是应该变色啊,还是应该隐藏啊!
它也傻傻分不清楚了,这就导致js的设计乃至以后都会是单线程的,这一点以后都不会改变

首先,我们应该知道的浏览器组成

1. 用户界面 
2. 浏览器引擎 
    在用户界面和呈现引擎之间传送指令。
3. 呈现引擎(render引擎) 
    负责显示请求的内容
4. 用户界面后端 
5. js引擎
    用于解析和执行 JavaScript 代码
6. 数据存储
    cookie session localStorage等
7. 网络请求
    http  https ajax  axios
    

参考浏览器的工作原理:新式网络浏览器幕后揭秘

浏览器是如何处理前端代码的?

对于浏览器来说,我们先认识两个引擎: render(渲染)引擎 和 js 引擎

1. render引擎通过编译源码 => 
    解析dom =>  
    处理css(同解析dom一块执行) => 通过算法 => 
    虚拟dom => 进行layout布局(绘制和排版) => 
    生成真实dom树 => 在页面上展示
2. js引擎 
    负责加载你的源代码 => 
    把它分解成字符串(又叫做分词)=>
    把这些字符串转换成编译器可以理解的字节码 => 
    执行这些编译后的字节码

什么是JS Engine(JS引擎)

处理并执行js代码的运行环境, 也就是专门处理Js脚本的虚拟机

常用的引擎

  JavaScriptCore(简称 JSC),苹果的JavaScript引擎,用于Safari和React Native;
  Chakra,微软的JavaScript引擎,用于Edge和Node-ChakraCore;
  SpiderMonkey是Mozilla的JavaScript引擎,在Firefox和SpiderNode中使用;
  V8引擎 在 Chrome 和 Node.js中使用

PS:正是由于js引擎是浏览器厂商自家实现的,所以这就能明白为啥有些js的方法在个别浏览器上需要做
单独处理, 比如说IE浏览器, 同理css 样式不兼容,也是这个道理,各家浏览器实现了不同的渲染引擎

js引擎中栈和堆

同其它编程语言(java)一样,JS引擎中也有堆(Heap)和栈(Stack)的概念

堆和栈都是内存中划分出来用来存储的区域,是一种数据结构,它基本记录了程序中的位置

栈(stack)为自动分配的内存空间,它由系统自动释放,用来存储方法和基本数据类型;
堆(heap)则是动态分配的内存,大小不定也不会自动释放,用来存储引用数据类型;

栈堆

基本数据类型存储

基本数据类型存放在栈中
在栈内存中的简单数据,数据大小确定,内存空间大小可以分配,是直接按值存放的

引用数据类型

引用类型存放在堆中,变量实际上是一个存放在栈内存的指针,
这个指针指向堆内存中的地址。每个空间大小不一样,根据情况进行特定的分配

图解基本数据和引用数据类型存放位置

基本数据类型和引用数据类型
参考深浅拷贝的解析

参考-js中的调用栈和堆

什么是WebApi

我们都使用过的API(例如“setTimeout”,获取dom等),但是,js引擎不提供这些API。
我们有一些叫做Web API的东西,它们是由浏览器提供的,比如DOM,AJAX,setTimeout
当初学js时,我们看到js中分为了Dom Bom的章节,而这些章节的Api是属于浏览器提供的

js中的异步(事件循环和回调队列)

1. 为什么会有异步呢?
   js是自上而下执行,浏览器自上而下运行代码,当碰到script标签时,会立马停掉所有的工作,直接去解析
   js,因为js是单线程的,同一时间只能干一件事,当js代码执行时间很长时,就会出现阻塞问题。也就是
   我们 常说的页面卡死状态,这样就导致了很差的用户体验,所以这些问题就需要在js中要存在异步执行
2. 如何实现的异步?
   通过事件循环(EventLoop)和回调队列(CallBack Queue)

图解 WebApi EventLoop CallBack 栈堆

在这里插入图片描述

EventLoop(事件循环)

直接参考阮一峰老师的-再谈eventloop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件
(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数

执行栈(主线程)中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行

PS: Event Loop只做一件事情,负责监听Call Stack和Callback Queue。当Call Stack里面的调用栈运行完变成空了, Event Loop就把Callback Queue里面的第一条事件(其实就是回调函数)放到调用栈中并执行它,后续不断循环执行这个操作

CallBack Queue(任务回调队列)

任务回调队列特点是: 先进先出

任务回调队列,其实就是个callBack 回调函数,说白了,就是js引擎执行到异步的代码时,会把它放到任务回调队列里,
等主线程上(调用栈)的所有同步代码执行完毕之后,才会去执行任务队列里放入的异步代码的回调函数。
PS: JS里将任务分为: 同步任务 和 异步任务
但实际并不是这么简单!(这里边还会涉及到微任务和宏仁务)参考几个例子

异步举例(setTimeout)

console.log(1)
setTimeout(() =>{
  console.log(2)
},100)
console.log(3);

执行结果 : 1 3 2
首先判断JS是同步还是异步,同步就进入主进程,异步就进入event table,
异步任务在event table中注册函数,当满足触发条件后,被推入event queue,
同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看
是否有可执行的异步任务,如果有就推入主进程中
console.log(1) 是同步任务,放入主线程里 setTimeout() 是异步任务,被放入event table, 100秒之后被推入任务队列里 console.log(3) 是同步任务,放到主线程里

settimeout() 和 promise()

  setTimeout(function () {
    console.log('定时器')
  },0);
 new Promise(function (resolve) {
    console.log('执行for循环');
    resolve()
  }).then(function () {
    console.log('then函数')
  });
  console.log('执行结束');

按上边的js执行顺序推断:
【执行for循环, 执行结束,定时器,then函数】

首先代码
js判断代码是同步还是异步,setTimeout() 异步进入event table,0秒后被推入任务队列中
new Promise()判定为同步 (因为promise是一个构造函数,被实例化之后属于一个同步任务,而then()里边的代码才是异步执行的),进入主线程执行,接着解析到then(),判定为异步任务,推入event table,接着 console.log(‘执行结束’);,判定为同步,所以推断出上边的执行结果

然而真正执行结果:
【执行for循环,执行结束,then函数,定时器】
在这里插入图片描述
额,为啥不上预想的结果!这时候肯定想,难道异步任务还有先后顺序!嗯哼,是滴,就是那个微任务和宏仁务
宏仁务主要是: script(全局任务),setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering
微任务主要是: process.nextTick, Promise.then, Object.observer, MutationObserver.
在这里插入图片描述
js执行机制
js执行机制
安排一下js

执行代码,遇到宏仁务放入宏仁务队列,遇到微任务放入微任务队列
执行其他函数的时候放入执行上下文
执行上下文中全部执行完毕后,执行微任务队列
微任务队列执行完毕后,再到宏仁务队列中取出第一项放入执行上下文中执行
接着就不停循环1-3的步骤

一: 概念

  1. 宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。
  2. 微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。
  3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

二. 运行机制

  1. 在执行栈中执行一个宏任务。
  2. 执行过程中遇到微任务,将微任务添加到微任务队列中。
  3. 当前宏任务执行完毕,立即执行微任务队列中的任务。
  4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。
  5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)

概念参考

发布了63 篇原创文章 · 获赞 100 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/qq_36407748/article/details/89329451
今日推荐