Js的执行机制

参考链接:
(转载)js引擎的执行过程(一)

js引擎执行过程分为3个过程:

1、语法分析
2、预编译阶段
3、执行阶段
注:浏览器首先按顺序加载由script标签分割的js代码块,加载js代码块完毕后,立刻进入以上三个阶段,然后再按顺序查找下一个代码块,再继续执行以上三个阶段,无论是外部脚本文件(不异步加载)还是内部脚本代码块,都是一样的原理,并且都在同一个全局作用域中。

1、语法分析:

js语法代码块加载完毕后,会首先进入语法分析阶段,主要作用是:

分析该js脚本代码块的语法是否正确,如果出现不正确,则向外抛出一个语法错误(SyntaxError),停止该js代码块的执行,然后继续查找并加载下一个代码块;如果语法正确,则进入预编译阶段

2、预编译阶段:

js代码块通过语法分析阶段后,语法正确则进入预编译阶段。在分析预编译阶段之前,我们先了解一下js的运行环境,运行环境主要有三种:

  • 全局环境(JS代码加载完毕后,进入代码预编译即进入全局环境)
  • 函数环境(函数调用执行时,进入该函数环境,不同的函数则函数环境不同)
  • eval(不建议使用,会有安全,性能等问题)

每进入一个不同的运行环境都会创建一个相应的执行上下文,那么在一段js程序中一般都会创建多个执行上下文,js引擎会以栈的方式对这些执行上下文进行处理,形成函数调用栈(call stack),栈底永远是全局执行上下文,栈顶永远是当前执行上下文

创建执行上下文:

可以理解为当前的执行环境,创建执行上下文的过程中,主要做了以下三件事:

  1. 创建变量对象(Variable Object)
  2. 建立作用域链(Scope Chain)
  3. 确定this的指向
    图片说明
  • 创建变量对象
    图片说明

    //首先创建fun执行上下文
    funEC = {
     //变量对象
     VO: {
         //arguments对象
         arguments: {
             a: undefined,
             b: undefined,
             length: 2
         },
    
         //test函数
         test: <test reference>, 
    
         //num变量
         num: undefined
     },
    
     //作用域链
     scopeChain:[],
    
     //this指向
     this: window
    }

    预编译阶段的变量对象都是在预编译阶段,没有进入执行阶段,变量对象都是不可以访问的,因为变量对象中的变量属性没有赋值,为undefined,只有到执行阶段,变量对象中的变量属性进行赋值,变量对象才能转为活动对象,才能进行访问,这个过程为VO->AO

  • 建立作用域链
    作用域链由当前执行环境的变量对象(未进入执行阶段前)与上层环境的一系列活动对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

var num = 30;

function test() {
    var a = 10;

    function innerTest() {
        var b = 20;

        return a + b
    }

    innerTest()
}
innerTestEC = {

    //变量对象
    VO: {b: undefined}, 

    //作用域链
    scopeChain: [VO(innerTest), AO(test), AO(global)],  

    //this指向
    this: window
}
test()

当调用到innerTest时,全局和test是执行阶段,innerTest是预编译,所以活动对象分别为AO,A0,VO,而innerTest的作用域链由当前执行环境的变量对象(没进入执行阶段前)与上层环境的一系列活动对象组成。

  1. 作用域链的第一项永远是当前作用域(当前上下文的变量对象或活动对象)
  2. 最后一项永远是全局作用域
  3. 作用域链保证了变量和函数的有序访问,查找方式是沿着作用域链从左至右查找变量或函数,找到则会停止查找,找不到则一直查找到全局作用域,再找不到则会抛出引用错误。

3、确定this指向

在全局中国this指向window,函数环境的this指向却较为灵活,需要根据执行环境和执行方法确定。

3、执行阶段

js的异步执行机制是由事件循环event loop解决的。
js是单线程的,但是js执行过程会有4个线程,但是永远只有js引擎线程在执行js脚本程序,其他的三个线程只协助,不参与代码解析和执行。

  • JS引擎线程: 也称为JS内核,负责解析执行Javascript脚本程序的主线程(例如V8引擎)
  • 事件触发线程: 归属于浏览器内核进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数推进事件队列,等待JS引擎线程执行
  • 定时器触发线程:主要控制计时器setInterval和延时器setTimeout,用于定时器的计时,计时完毕,满足定时器的触发条件,则将定时器的处理函数推进事件队列中,等待JS引擎线程执行。
    注:W3C在HTML标准中规定setTimeout低于4ms的时间间隔算为4ms。
  • HTTP异步请求线程:通过XMLHttpRequest连接后,通过浏览器新开的一个线程,监控readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进事件队列中,等待JS引擎线程执行。
  • 注:浏览器对通一域名请求的并发连接数是有限制的,Chrome和Firefox限制数为6个,ie8则为10个。

总结:永远只有JS引擎线程在执行JS脚本程序,其他三个线程只负责将满足触发条件的处理函数推进事件队列,等待JS引擎线程执行。

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

图片说明

图片说明

宏任务
宏任务就是JS 内部(任务队列里)的任务,严格按照时间顺序压栈和执行的任务。
宏任务(macro-task)可分为同步任务和异步任务:

  • 同步任务指的是在JS引擎主线程上按顺序执行的任务,只有前一个任务执行完毕后,才能执行后一个任务,形成一个执行栈(函数调用栈)。

  • 异步任务指的是不直接进入JS引擎主线程,而是满足触发条件时,相关的线程将该异步任务推进任务队列(task queue),等待JS引擎主线程上的任务执行完毕,空闲时读取执行的任务,例如异步Ajax,DOM事件,setTimeout等。

理解宏任务中同步任务和异步任务的执行顺序,那么就相当于理解了JS异步执行机制–事件循环(Event Loop)。

微任务通常来说就是需要在当前 task 执行结束后立即执行的任务,

问题:

1、js为什么是单线程的?

js最先设计被用在浏览器上,如果js是多线程的

场景描述:
那么现在有2个线程,process1 process2,由于是多线程的JS,所以他们对同一个dom,同时进行操作
process1 删除了该dom,而process2 编辑了该dom,同时下达2个矛盾的命令,浏览器究竟该如何执行呢?

2、js为什么需要异步?

如果JS中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。
对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验

3、js单线程是如果实现异步的?

通过的事件循环(event loop),理解了event loop机制,就理解了JS的执行机制

js中的event loop

事件循环可以理解成3部分组成,分别是:

  • 主线程执行栈
  • 异步任务等待触发
  • 任务队列
    任务队列就是以队列的数据结构对时间任务进行管理,特点是先进先出,后进后出

图片说明

在JS引擎主线程执行过程中:

  1. 首先执行宏任务的同步任务,在主线程上形成一个执行栈,可理解为函数调用栈;
  2. 当执行栈中的函数调用到一些异步执行的API(例如异步Ajax,DOM事件,setTimeout等API),则会开启对应的线程(Http异步请求线程,事件触发线程和定时器触发线程)进行监控和控制
  3. 当异步任务的事件满足触发条件时,对应的线程则会把该事件的处理函数推进任务队列(task queue)中,等待主线程读取执行
  4. 当JS引擎主线程上的任务执行完毕,则会读取任务队列中的事件,将任务队列中的事件任务推进主线程中,按任务队列顺序执行
  5. 当JS引擎主线程上的任务执行完毕后,则会再次读取任务队列中的事件任务,如此循环,这就是事件循环(Event Loop)的过程

微任务

微任务是在es6和node环境中出现的一个任务类型,如果不考虑es6和node环境的话,我们只需要理解宏任务事件循环的执行过程就已经足够了,但是到了es6和node环境,我们就需要理解微任务的执行顺序了。
微任务(micro-task)的API主要有:Promise, process.nextTick
图片说明

  • 执行宏任务中同步任务,执行结束;

  • 检查是否存在可执行的微任务,有的话执行所有微任务,然后读取任务队列的任务事件,推进主线程形成新的宏任务;没有的话则读取任务队列的任务事件,推进主线程形成新的宏任务

  • 执行新宏任务的事件任务,再检查是否存在可执行的微任务,如此不断的重复循环

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

//如果then之前有输出的话是当作同步处理的
Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

代码块通过语法分析和预编译后,进入执行阶段,当JS引擎主线程执行到console.log('script start');,JS引擎主线程认为该任务是同步任务,所以立刻执行输出script start,然后继续向下执行;

  1. JS引擎主线程执行到setTimeout(function() { console.log('setTimeout'); }, 0);,JS引擎主线程认为setTimeout是异步任务API,则向浏览器内核进程申请开启定时器线程进行计时和控制该setTimeout任务。由于W3C在HTML标准中规定setTimeout低于4ms的时间间隔算为4ms,那么当计时到4ms时,定时器线程就把该回调处理函数推进任务队列中等待主线程执行,然后JS引擎主线程继续向下执行
  2. JS引擎主线程执行到Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); });,JS引擎主线程认为Promise是一个微任务,这把该任务划分为微任务,等待执行
  3. JS引擎主线程执行到console.log('script end');,JS引擎主线程认为该任务是同步任务,所以立刻执行输出script end
  4. 主线程上的宏任务执行完毕,则开始检测是否存在可执行的微任务,检测到一个Promise微任务,那么立刻执行,输出promise1和promise2
  5. 微任务执行完毕,主线程开始读取任务队列中的事件任务setTimeout,推入主线程形成新宏任务,然后在主线程中执行,输出setTimeout
    图片说明
全部评论

相关推荐

整体时间线:2月末力扣从零开始。3月初刷题成瘾,中旬陆续开面开杀,被机试折磨,下旬纠结日常offer选择。4月入职淘天,从硬landing到上手业务快乐融入5月平静美好,顺利到我觉得直接转正是最佳选择,月底转暑期流程被hr直接挂,主管诱骗能转正,万幸蚂蚁暑期流程没拒掉,压哨发意向,手里也还有个腾讯offer兜底,毁约腾讯暑期到此结束。==============================一些感悟:永远保留后手,先拿了阿里国际日常,拿到网易伏羲offer之后才拒绝意向,中间难免要催hr尽量开在同一时间,后续等淘天oc的时候立马拒了网易意向。不会让手里超过2个offer,但是也不会在未确定的时候就拒掉到手的。在淘天的时候师兄主管都保证能转正别担心,甚至主管拉我进内部群一起团建,但是始终把腾讯offer抓在手里,也给了我撕破脸之后和主管谈判的底气。蚂蚁一面二面间隔一个半月,时不时反向保温一下面试官又没拒掉流程,真是我最明智的选择。==============================实习体验:研一在鹅厂AI&nbsp;Lab实习打杂纯快乐的,自己包装一下也是有产出的。遇到的所有人都很温和有礼貌,整体不卷年纪偏大,公司关怀好,不考虑城市的话应该会是第一选择。淘天业务组非常业务,技术不容易提升但是容易有产出,整体强度能承受分到的活也不多还挺核心的,师兄还是很nice的,往年转正待遇也挺好,小组整体年龄结构有中有小没老人,晋升空间不错。拒掉的offer里面,同花顺是做大模型部署加速的,给钱少太卷拒了;阿里国际是研究型实习生随便面的感觉面试官技术没有太懂;网易伏羲是llm+智能npc其实很有搞头,还是贪图大厂title拒了;腾讯这个最可惜,agent+游戏ai,而且在大部门实习过可以丝滑landing,腾讯招聘经常能看到校招社招广告,应该是团队扩张期,考虑到城市因素忍痛拒绝,释放一个hc给大家。==============================彩蛋:想看看牛u会做什么选择,感觉人生到了这个时间点,每个决策都会影响很大,已知和女友都是浙江人,她稳定杭州工作,计划后续杭州定居结婚。 #暑期实习# #腾讯# #阿里# #蚂蚁# #大模型# #淘天#
投递蚂蚁集团等公司10个岗位
点赞 评论 收藏
转发
1 收藏 评论
分享
牛客网
牛客企业服务