Skip to content

执行上下文与作用域

执行上下文是 JS 中的核心概念,执行上下文决定了当前环境下能够访问哪些变量与函数,JS 中执行上下文分为如下三种:

  • 全局执行上下文:只有一个,浏览器中的全局对象就是 window 对象,this 指向这个全局对象。
  • 函数执行上下文:存在无数个,只有在函数被调用的时候才会被创建,每次调用函数都会创建一个新的执行上下文。每当调用函数时 JS 引擎都会为该函数创建一个新的函数执行上下文,并将该函数的执行上下文推到一个执行下文栈中,函数执行完毕后,执行上下文栈会弹出该函数上下文,将控制权返回给之前的执行上下文。
  • eval 函数执行上下文:指的是运行在 eval 函数中的代码。

执行上下文的创建分为创建阶段和执行阶段,执行上下文创建阶段流程如下:

  • 确定 this 的值,也被称为 This Binding。不同执行上下文 this 的指向也不同,在全局执行上下文件中,浏览器环境 this 指向 window 对象,Node 环境指向该文件的 module 对象。在函数执行上下中,this 的指向取决于函数的调用方式,函数的调用方式分为:默认绑定、隐式绑定、显式绑定(硬绑定)、new 绑定、箭头函数。
  • 创建 LexicalEnvironment(词法环境)。
  • 创建 VariableEnvironment(变量环境)。

作用域提升

作用域提升(Hoisting)是指在 JavaScript 解释器中,在代码执行前将变量和函数声明提升到其所在作用域的顶部的过程,只有声明才会被提升,而赋值操作并不会被提升。在 ES6 之前,JavaScript 并没有块级作用域,因此在块内部声明的变量和函数都会被提升到所在的函数或全局作用域中。在 ES6 中引入了 let 和 const 关键字,它们声明的变量有块级作用域,不会被提升。作用域提升的存在可以让代码更易于理解,但也可能会导致一些错误。因此,编写 JavaScript 代码时应该注意声明变量和函数的顺序,避免出现意料之外的行为。作用域提升分为变量提升和函数提升,其中函数提升的优先级高于变量提升

变量提升

js
// 变量提升,在变量声明之前使用变量会产生undefined值
console.log(x) // undefined
var x = 10

/** 上面代码等同于如下代码  */
var x
console.log(x) // undefined
x = 10

函数提升

注意:函数表达式并不会被提升,只有函数声明才会被提升。

js
foo() // 1
function foo() {
  console.log(1)
}

/** 上面代码等同于如下代码 */
function foo() {
  console.log(1)
}
foo() // 1

在 JavaScript 中,函数声明的优先级高于变量声明。也就是说,如果在作用域中同时存在同名的函数声明和变量声明,那么函数声明会被提升到作用域顶部,而变量声明会被提升,但不覆盖已经存在的同名函数声明。

js
// 函数声明的优先级高于变量声明,因此 foo 最终被赋值为函数
foo() // 输出"Hello"
var foo = 'world'
function foo() {
  console.log('Hello')
}

Released under the MIT License.