Web引擎

JavaScriptCore 是 JavaScript 引擎,通常会被叫做虚拟机,专门设计来解释和执行 JavaScript 代码。最开始的 JavaScriptCore 是从 KJS(KDE 的 JavaScript 引擎)以及 PCRE 正则表达式的基础上开发的,是基于抽象语法树的解释器。2008 年重写了,叫做 SquirrelFish,后来是 SquirrelFish Extreme,又叫 Nitro。目前 JavaScript 引擎还有 Google 的 V8 ,Mozilla 的 SpiderMonkey。

核心引擎

JavaScript源代码会由词法分析器(Lexer)生成token流,然后通过语法分析器(Parser)生成AST(抽象语法树),生成AST后使用字节码生成器(ByteCode Generator)生成字节码(ByteCode),然后启动解释器(LLInt)进行解释执行,如果同一行代码运行了几次,则标记为warm,运行了很多次,则标记为hot。如果标记为warm,那么JIT就会把它送到基线编译器,并把编译结果存储起来,比如,监视器监视到了,某行、某个变量执行同样的代码、使用了同样的变量类型,那么就会把编译后的版本,替换这一行代码的执行,并且存储。如果一个代码段变得hot,分析器会把它发送到优化编译器中。生成一个更快速和高效的代码版本出来,并且存储。例如:循环加一个对象属性时,假设它是 INT 类型,优先做 INT 类型的判断

类型推断

JavaScript是一门解释型弱类型语言,在生成AST后,一边解释一边执行,当一段代码被多次执行后,这就会有被优化的空间。编译型语言如Java,可以在执行前进行优化编译,但是要花费大量时间,不适合web交互。

实现

  • 分析值 d=3+c
  • 类型预测 d(int32)=3+c(int32)
  • 类型检查 d=3+2=5
  • 类型验证 typeof d == number
  • 更新到编译结果

对象访问优化

  • 字典模式(slow mode)
    • 也成为哈希表模式,V8使用哈希表来存储对象的属性
  • Stable模式(fast mode)
    • 使用类似数组结构来存储对象的属性并使用offset访问
  • 新创建的小对象默认是fast mode
  • 退化成字典模式
    • 动态添加太多属性
    • 删除属性
  • 进化成快速模式
    • RuntimeCall
    • 对象被设置为一个函数的原型(Object.create)

事件循环

执行栈

  • 执行上下文
    • 外部作用域
    • 私有作用域
    • this
  • 同步代码依次加入执行栈

任务队列

  • 异步回调函数
  • 异步回调上下文
  • 宏任务(定时器、IO回调、整体script)
  • 微任务(Promise、MutationObserver、process.nextTick、setImmediate)

执行顺序

  • 执行执行栈的同步代码->堆积微任务->UI线程->从任务队列获取宏任务并执行->微任务执行
  • 执行完一个宏任务就一次性执行玩所有的微任务

JavaScript虚拟机

垃圾回收

  • 引用计数,潜在循环引用问题
  • 标记清除
    • 从根节点开始查找
    • 标记所有可以访问对象
    • 标记所有不可访问对象

内存泄漏

  • 全局变量
  • 绑定事件没有解绑
  • 定时器没有清除
  • 闭包引用
  • DOM元素引用,常用WeakSet、WeakMap处理

数据结构

原始类型

  • Boolean
  • Number
    • Infinity
    • NaN
  • String
  • undefined
  • null
  • Symbol
  • Object

内建对象

  • Date
  • Math
  • 数组
  • 集合
    • Map
    • WeakMap
    • Set
    • WeakSet
  • JSON
  • RegExp

内存模型

  • 栈 按值传递
  • 堆 按引用传递

类型检测

  • typeof
    • null 用===判断
    • NaN用!==判断
  • instanceof
  • Object.prototype.toString.call

克隆

  • 深克隆
    • JSON.parse
      • RegExp、FUNC无法克隆
      • 对象原型无法克隆
  • 浅克隆
    • for in
    • hasOwnProperty
    • Object.assign

数据监听

  • Object.observe(废弃)
  • Object.defineProperty
    • get 依赖收集
    • set 派发更新
    • 发布订阅模式
  • Proxy
    • Reflect
    • 劫持整个对象
    • 可监听数组对象