执行上下文
浏览器的 JS
引擎在解析 JS
代码时分为2个阶段,编译阶段与执行阶段,编译阶段会将 var
与 function
进行变量声明提升。
以上说法实际不太准确,现代浏览器如 V8 引擎,会先进行词法分析,变量声明提升,再转为 AST(词法树),有的代码会被编译为机器码,使用时直接执行机器码;大部分还是会在调用时进入 JIT,即边编译边执行。
什么是执行上下文?
- 全局执行上下文:
- 函数执行上下文:每个函数会在执行的时候创建自己的执行上下文。
Eval
函数执行上下文:使用eval()
函数也会创建一个新的执行上下文。- 导入
module
模块的代码
创建执行上下文
- 确实
this
绑定 - 创建 词法环境(LexicalEnvironment)组件
- 创建 变量环境组件(VariableEnvironment) 组件。
this 指向
- 在全局执行上下文中,
this
总是指向全局对象。如浏览器中执行window
对象 - 在函数执行上下文中,
this
的值取决于函数的调用方式:- 如果被对象调用,则执行这个对象;
- 其它情况均指向全局对象
window
(严格模式下执行undefined
);
函数执行上下文的文本环境会指向其声明时创建的函数对象 作用域/当前执行上下文/当前上下文的词法环境 可以理解为相同的意思
代码执行步骤
- 创建全局执行上下文,并加入栈顶
var
与function
声明创建在全局对象(window
)中,而let
const
class
声明放在全局作用域(scope
) 中- 先在全局
scope
中找变量,找不到再到全局对象中查找
- 分析
- 找到所有的 非函数 中的
var
声明; - 找到所有的顶级函数声明(不是函数表达式等)
- 找到顶级
let
const
class
声明
- 找到所有的 非函数 中的
- 命名重复处理
let
const
class
声明变量名不能重复let
const
class
的变量名不能和var
function
名称重复var
和function
名称重复时,function
名称优先
- 创建绑定
- 登记并初始化
var
为undefined
- 顶级函数声明:登记
function
名称,并初始化为新创建函数对象(函数对象内部会保存函数创建时的执行上下文的文本环境) - 块级中函数声明:登记名称,初始化为
undefined
- 登记
let const class
,但未初始化
- 登记并初始化
- 执行语句
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没有块级作用域