How JavaScript Work

一、chrome浏览器组成

JavaScript的运行时模型

  • JavaScript引擎——V8引擎
  • WebAPIs——由宿主环境提供的额外API不属于引擎的原生部分
  • EventLoop & CallbackQueue 事件循环和回调队列——属于宿主环境提供的机制,用于辅助引擎工作

img

二、JavaScript 引擎

img

如图V8引擎主要由两部分构成

  • 内存堆(Memory Heap)—— 用于分配内存的位置
  • 调用栈(Call Stack)—— 用于执行代码的位置

(一)Call Stack 调用栈

调用栈是解释器(如浏览器中 JavaScript 解释器)追踪函数执行流的一种机制

也称执行栈,拥有后进先出(LIFO)的数据结构,被用来存储代码运行时创建的所有执行上下文

JvaScript 是一种单线程编程语言,这意味着它只有一个 Call Stack 。因此,它一次仅能做一件事

当V8引擎遇到你编写的代码时,会创建全局的执行上下文并压入当前调用栈中,每当引擎遇到一个函数调用,它会为该函数创建一个新的函数执行上下文并压入栈的顶部

引擎会执行位于栈顶的函数,正在调用栈中执行的函数如果调用了其他函数,新函数也将添加到调用栈顶,立即执行

当前函数执行完毕后,解释器将该函数执行上下文从栈中弹出,继续执行当前执行环境下的剩余的代码

当分配的调用栈空间被占满时,会引发“堆栈溢出”错误

正常运行
function first() {
  console.log('Inside first function');
  second();
}

function second() {
  console.log('Inside second function');
}

first();

img

追踪异常

Call Stack 的每个入口被称为 Stack Frame(栈帧)

这正是在抛出异常时如何构建 stack trace 的方法——基本上是在异常发生时的 Call Stack 的状态

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

img

堆栈溢出

在某些情况下,调用堆栈中函数调用的数量超出了调用堆栈的实际大小,浏览器将抛出 错误终止

img

递归可能导致该错误

function foo() {
    foo();
}
	foo();

img

三、WebAPIs

由于 JavaScript 只有一个调用堆栈,理论上当某段代码运行变慢(比如网络请求、下载图片)时就会发生阻塞,导致浏览器不能执行后面的简单操作

但是,实际上可以看到即使进行网络请求等操作后续代码依然执行。

怎么处理的,最简单的方式提供——异步回调

(一)Async Callbacks & Call Stack

console.log('hi')
setTimeout( function cb1() {
    console.log('cb1')
}, 5000)
console.log('bye')

神奇的,setTimeout的异步回调没有在执行定时器时立即执行,而是执行下一个函数,后执行异步回调函数

为什么呢,setTimeout并不是JavaScript引擎所拥有的API,而是浏览器提供的WebAPI

考虑到定时器为web API的部分,我们对上面的代码进行分析

  1. 调用console.log('HI') 进入到调用栈中,控制台打印Hi

    img
  2. 执行定时器,加入到调用栈中

    img
  3. 在WebAPIs中创建一个Timer,并将定时器的内容移过去

    img
  4. 定时器部分执行完毕,弹出调用栈,此时定时器内的内容被保存在WebAPIs环境当中

    img
  5. 调用console.log('Bye') 进入到调用栈中,控制台打印Bye

    img
  6. console.log('Bye')弹出调用栈

    img
  7. 等待WebAPIs中的timer执行,将cb1加入到回调队列中

    img
  8. 通过事件循环将回调队列中的cb1重新压入到调用栈中

    img
  9. cb1内调用了console.log('cb1')所以也要压入到调用栈中,控制台打印cb1

    img
  10. 弹出console.log('cb1')

    img
  11. 弹出cb1

img

四、EventLoop & CallbackQueue

我们由上面知道,任务分为同步任务和异步任务。比如我们打开网站时,网页的渲染过程就是一大堆同步任务,页面骨架和页面元素的渲染等;而加载图片音乐之类占用资源大耗时久的任务,就是异步任务

为什么javascript是一门单线程语言,可以进行异步任务。原来javascript的多线程都是用单线程模拟出来的,单线程这一核心仍未改变

img

分析导图可以看出

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue
  • 主线程内的任务执行完毕为空(存在monitoring process进程),会去Event Queue读取对应的函数,进入主线程执行
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)
let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');
  • 首先ajax发送异步的网络请求,进入Event Table,注册回调函数success
  • 执行console.log(),主线程任务为空
  • 网络请求完成,回调函数success进入Event Queue
  • 主线程从Event Queue读取回调函数success并执行

(一)macro-task & micro-task

除了广义的同步任务和异步任务,我们对任务有更精细的定义

  • macro-task(宏任务):整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

不同类型的任务会进入对应的Event Queue

img

事件循环的顺序

  1. 先执行所有同步任务
  2. 遇到的异步任务分发到对应Event Queue
  3. 主线程任务执行完毕
  4. 先执行微任务Event Queue :
  5. 再执行宏任务Event Queue
setTimeout(function() {
    console.log('setTimeout');
})

new Promise(function(resolve) {
    console.log('promise');
}).then(function() {
    console.log('then');
})

console.log('console');
// promise console then setTimeout
console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})
//1 7 6 8 2 4 3 5 9 11 10 12
posted @ 2020-06-15 22:25  鱼一十三  阅读(230)  评论(0编辑  收藏  举报