JavaScript 结构化 Structure
结构化(一)
事件循环&宏任务&微任务
-
事件循环是什么?
事件循环是浏览器执行任务的机制,它会不断循环判断消息队列中是否有任务,队列中的任务都是指宏任务,而宏任务中包含微任务队列,在宏任务结束前后执行微任务队列,直到微任务队列中为空才结束这个宏任务。
-
宏任务是什么?
- 渲染事件(如解析 DOM、计算布局、绘制);
- 用户交互事件(如鼠标点击、滚动页面、放大缩小等);
- JavaScript 脚本执行事件;网络请求完成、文件读写完成事件。 为了协调这些任务有条不紊地在主线程上执行,页面进程引入了消息队列和事件循环机制,渲染进程内部会维护多个消息队列,比如延迟执行队列和普通的消息队列。然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。我们把这些消息队列中的任务称为宏任务。
-
微任务是什么?
微任务就是一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前。
- 微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列。
- 微任务的执行时长会影响到当前宏任务的时长。比如一个宏任务在执行过程中,产生了 100 个微任务,执行每个微任务的时间是 10 毫秒,那么执行这 100 个微任务的时间就是 1000 毫秒,也可以说这 100 个微任务让宏任务的执行时间延长了 1000 毫秒。所以你在写代码的时候一定要注意控制微任务的执行时长。
- 在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行。
其实所有的JS代码都是一个微任务,只是哪些微任务构成了一个宏任务;执行在JS引擎里的就是微任务,执行在JS引擎之外的就是宏任务,循环宏任务的工作就是事件循环。
事件循环不属于JavaScript引擎实现的东西,而是由浏览器或node js宿主环境实现的
script标签、UI交互、setTimeout、setInterval都会创建宏任务
一个宏任务只存在一个微任务队列,微任务根据入队时间顺序执
Promise的then方法以及async函数里的await会将一个微任务入队
宏任务&微任务执行顺序 实验代码
代码
async function afoo(){
console.log("1");
await new Promise(resolve => resolve());
console.log("2");
}
new Promise(resolve => (console.log("3"), resolve()))
.then(()=>(
console.log("4"),
new Promise(resolve => resolve())
.then(() => console.log("5")) ));
setTimeout(function(){
console.log("6");
new Promise(resolve => resolve()) .then(console.log("7"));
}, 0);
console.log("8");
console.log("9");
afoo();
// 3
// 8
// 9
// 1
// 4
// 2
// 5
// 6
// 7
解析:
- 解析:
- 第一个宏任务:
- 3
- 入队 4
- 8
- 9
- 1
- 入队 2
- 4
- 入队 5
- 2
- 5
- 3
- 第二个宏任务:
- 6
- 入队 7
- 7
- 6
- 第一个宏任务:
结构化(二)
从上到下
- JS Context => Realm
- Realm1 Realm2 ... 都会创建一套
- global
- Infinity NaN undefined Array Objecy Date ...
- global
- Realm1 Realm2 ... 都会创建一套
- Macro
- Micro
- 函数调用(Execution Context)
- Statement
- Expression
- 直接量/变量/this
Realm
对象分类
-
宿主对象 (Host Objects): JavaScript 宿主环境提供的对象
- window
- 一部分来自 js
- 一部分来自 浏览器
- window
-
内置对象 (Build-in Objects): JavaScript 语言提供
-
固有对象 (Intrinsic Objects): 标准规定,随着 JavaScript 运行时创建而自动创建的对象实例
- js 执行前就创建,类似基础库
-
原生对象 (Native Objects): 用户通过 Array RegExp 等内置构造器或特殊语法创建
- 可以 new 创建新的对象
- 无法通过 class/extend 或 纯 js 代码实现
- 这些构造器创建的对象使用了 私有字段,无法被原型继承
-
普通对象 (Ordinary Objects): 由 {} Object 构造器 或 class 创建,能够被原型继承
-
对象模拟函数和构造器
通过 私有字段 模拟
- 函数对象
- [[call]]
- 构造器对象
- [[construct]]
实现 [[call]] 和 [[construct]] 行为不一致
new Date() // object
Date() // string
new Image() // object
Image() // error
function 或 Function 构造器创建,[[call]] 和 [[construct]] 行为相似
function f() {
return 1
}
f() // [[call]]
new f() // [[constuct]]
[[construct]] 执行过程
- 以 Object.prototype 创建新对象
- 以 新对象 为 this,调用 [[call]]
- [[call]] 返回对象,或 返回 新对象
获取全部固有对象
标准中全部对象的定义,规定了以下全局对象的属性。查找这些对象的 属性 和 Getter/Setter,就可以获得所有 固有对象
- 三个值
- Infinity
- NaN
- undefined
- 九个函数
- eval
- isFinite
- isNaN
- parseFloat
- parseInt
- decodeURI
- decodeURIComponent
- encodeURI
- encodeURIComponent
- 一些构造器
- Array
- Date
- RegExp
- Promise
- Proxy
- Map
- WeakMap
- Set
- WeakSet
- Function
- Boolean
- String
- Number
- Symbol
- Object
- Error
- EvalError
- RangeError
- ReferenceError
- SyntaxError
- TypeError
- URIError
- ArrayBuffer
- SharedArrayBuffer
- DataView
- Typed Array
- Float32Array
- Float64Array
- Int8Array
- Int16Array
- Int32Array
- UInt8Array
- UInt16Array
- UInt32Array
- UInt8ClampedArray
- 四个用于当作命名空间的对象
- Atomics
- JSON
- Math
- Reflect
函数调用(Execution Context)
执行栈
execution context2 // running execution context
execution context1
execution context0
execution context stack
- execution context
- code evaluation state 代码执行位置
- async await generator
- Function
- context 为函数
- 全局 script 是 null
- Script/Module
- 同上
- Generator
- generator 产生的,否则是 null
- Realm
- LexicalEnvironment
- 词法环境,获取变量值时用到的
- VirableEnvironment
- code evaluation state 代码执行位置
LexicalEnvironment
- this
- 2019 版本,老版在 context
- new.target
- super
- 变量
VirableEnvironment
历史包袱,处理 var 声明
Environment Record
链表结构
- Environment Record
- Declarative Environment Record
- Function Environment Record
- Module Environment Record
- Global Environment Record
- Object Environment Record
- with eval
- Declarative Environment Record
Function Closure
Function: foo
Environment Record
y:2
Code
console.log(y)
var y = 2
function foo() {
console.log(y)
}
foo()
嵌套的 Environment
Function: foo3
Environment Record ---> Environment Record
x:3 y:2
Code
console.log(y,x)
var y = 2
function foo() {
var x = 3
return function foo3() {
console.log(y, x)
}
}
foo()
箭头函数,附加 this
Function: foo3
Environment Record ---> Environment Record
x:3 y:2
this: global
Code
console.log(y,x)
var y = 2
function foo() {
var x = 3
return () => {
console.log(y, x)
}
}
foo()
Realm
var o = new Object() // 不需要 realm,当前 realm 的 lex env 中可以获取到
var o = {} // 需要 realm,比如 别的 iframe 中的 {} 的不是当前的 Object 实例,所以需要确定在哪个 realm
Object.getPrototypeOf({}) === Object.prototype