|
|
|
>JavaScript 引擎会常驻于内存中,等待着宿主将 JavaScript 代码或者函数传递给它执行。
|
|
|
|
|
|
|
|
在 ES3 和更早的版本中,JavaScript 本身还没有异步执行代码的能力,在 ES5 之后,JavaScript 引入了 Promise,这样不需要浏览器的安排,引擎本身也可以发起任务了。
|
|
|
|
|
|
|
|
## 宏观和微观任务
|
|
|
|
|
|
|
|
采纳 JSC 引擎的术语,把宿主发起的任务称为**宏观任务**,将引擎发起的任务称作**微观任务**
|
|
|
|
|
|
|
|
用伪代码表示事件循环大概如下:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
while(true) {
|
|
|
|
r = wait();
|
|
|
|
execute(r);
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
其中每次的循环过程,其实就是一个宏观任务,大致可以理解为宏观任务的队列就相当于事件循环。在宏观任务中,JavaScript 的 Promise 还会产生异步代码,JavaScript 必须保证这些异步代码在一个宏观任务中完成,因此,每个宏观任务中又包含了一个微观任务队列。
|
|
|
|
|
|
|
|
有了宏观任务和微观任务机制,我们就可以实现 JavaScript 引擎级和宿主级的任务了,例如:Promise 永远在队列尾部添加微观任务。setTimeout 等宿主 API,则会添加宏观任务。
|
|
|
|
|
|
|
|
## Promise
|
|
|
|
|
|
|
|
Promise 是 JavaScript 语言提供的一种标准化的异步管理方式,它的总体思想是,需要进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过 Promise 的 then 方法的回调)。
|
|
|
|
|
|
|
|
JavaScript 引擎会在下一次执行宿主发起的宏观任务之前先执行由引擎自身发起的微观任务,这也就是为什么微观任务始终会在宏观任务之前执行。
|
|
|
|
|
|
|
|
## this 关键字
|
|
|
|
|
|
|
|
普通函数的 this 的最终指向的是那个调用它的对象,举一个例子说明
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
function showThis(){
|
|
|
|
console.log(this);
|
|
|
|
}
|
|
|
|
var o = {
|
|
|
|
showThis: showThis
|
|
|
|
}
|
|
|
|
showThis(); // global
|
|
|
|
o.showThis(); // o
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
在以上代码中 `o.showThis()` 表达式返回的并非是函数本身,而是一个 Reference 类型,该类型由两部分组成:一个对象和一个属性值,在调用时时被解引用,获取到真正的信息,在上面的代码中 Reference 类型中的对象被当作 this 值,传入了执行函数时的上下文当中,这也就是为什么 this 会指向调用它的那个对象。
|
|
|
|
|
|
|
|
如果将例子中的 showThis 函数改为箭头函数,那么结果会发生变化,这是因为箭头函数并不会产生新的执行上下文,其 this 始终与外层函数保持一致。
|
|
|
|
|
|
|
|
如果将例子中的 showThis 函数改写为类中的方法,那么直接调用 showThis 方法的 this 的结果也会不同,这是因为 JavaScript 的 Class 被设计成了默认在严格模式下(use strict)执行,而严格模式下 this 指向会发生一些改变。
|
|
|
|
|
|
|
|
函数能够引用定义时的变量,函数也能记住定义时的 this,因此,函数内部必然有一个机制来保存这些信息,这个用来保存定义时上下文的机制就是私有属性 \[\[Environment\]\]。
|
|
|
|
|
|
|
|
在函数执行时,会创建一条执行环境记录,也就是函数定义时的上下文设置为函数的 \[\[Environment\]\],这个动作就是切换上下文,着一层一层形成的上下文就是函数的 **“作用域链”**。
|
|
|
|
|
|
|
|
## 上下文栈
|
|
|
|
|
|
|
|
>JavaScript 引擎并非一行一行分析执行代码,而是一段一段的分析执行,当执行一段代码的时候会进行一些准备工作
|