Skip to content
本页目录

执行上下文

浏览器的 JS 引擎在解析 JS 代码时分为2个阶段,编译阶段与执行阶段,编译阶段会将 varfunction 进行变量声明提升。

以上说法实际不太准确,现代浏览器如 V8 引擎,会先进行词法分析,变量声明提升,再转为 AST(词法树),有的代码会被编译为机器码,使用时直接执行机器码;大部分还是会在调用时进入 JIT,即边编译边执行。

什么是执行上下文?

  • 全局执行上下文:
  • 函数执行上下文:每个函数会在执行的时候创建自己的执行上下文。
  • Eval 函数执行上下文:使用 eval() 函数也会创建一个新的执行上下文。
  • 导入 module 模块的代码

创建执行上下文

  1. 确实 this 绑定
  2. 创建 词法环境(LexicalEnvironment)组件
  3. 创建 变量环境组件(VariableEnvironment) 组件。

this 指向

  • 在全局执行上下文中,this 总是指向全局对象。如浏览器中执行 window 对象
  • 在函数执行上下文中, this 的值取决于函数的调用方式:
    • 如果被对象调用,则执行这个对象;
    • 其它情况均指向全局对象 window(严格模式下执行 undefined);

函数执行上下文的文本环境会指向其声明时创建的函数对象 作用域/当前执行上下文/当前上下文的词法环境 可以理解为相同的意思

代码执行步骤

  1. 创建全局执行上下文,并加入栈顶
    1. varfunction 声明创建在全局对象(window)中,而 let const class 声明放在全局作用域(scope) 中
    2. 先在全局 scope 中找变量,找不到再到全局对象中查找
  2. 分析
    1. 找到所有的 非函数 中的 var 声明;
    2. 找到所有的顶级函数声明(不是函数表达式等)
    3. 找到顶级 let const class 声明
  3. 命名重复处理
    1. let const class 声明变量名不能重复
    2. let const class 的变量名不能和 var function 名称重复
    3. varfunction 名称重复时,function 名称优先
  4. 创建绑定
    1. 登记并初始化 varundefined
    2. 顶级函数声明:登记 function 名称,并初始化为新创建函数对象(函数对象内部会保存函数创建时的执行上下文的文本环境)
    3. 块级中函数声明:登记名称,初始化为 undefined
    4. 登记 let const class但未初始化
  5. 执行语句

ES6 环境中,块级作用域内声明的函数,行为类似于 var 声明的变量 --阮一峰

代码题

JS
let foo
if(true) {
   // 外层有 let 定义的同名变量,则不会提升
   function foo() {
      console.log(1)
   }
}
foo() // 报foo不是一个方法
let foo
if(true) {
   // 外层有 let 定义的同名变量,则不会提升
   function foo() {
      console.log(1)
   }
}
foo() // 报foo不是一个方法
JS
var foo
if(true) {
   // 块级作用域内的函数,如果外层没有同名变量或者不是 let、const、class 定义的变量,则会提升
   function foo() {
      console.log(1)
   }
}
foo() // 1
var foo
if(true) {
   // 块级作用域内的函数,如果外层没有同名变量或者不是 let、const、class 定义的变量,则会提升
   function foo() {
      console.log(1)
   }
}
foo() // 1
JS
{
   function foo() {
      console.log(1)
   }
}
foo() // 1 我理解的是浏览器为了兼容性,无法区分这是ES6写法,而ES5没有块级作用域
{
   function foo() {
      console.log(1)
   }
}
foo() // 1 我理解的是浏览器为了兼容性,无法区分这是ES6写法,而ES5没有块级作用域