浏览器工作原理——变量提升

2021年10月23日

总结

JavaScript 引擎在执行之前会先编译,编译过程会将函数的声明和赋值以及由 var 关键字声明的变量的声明添加到内存里的执行上下文中,表现为在代码执行阶段,虽然还未执行到某个变量或函数的声明语句,却可以访问该变量而不会报错的一种行为。就像是将这些声明提升到了所有代码的开头。

要点

程序中的语句通常按照顺序执行,JavaScript 引擎会先编译一遍要执行的 JavaScript 代码,再执行代码。

变量提升(hoisting)

从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

var a = 1

以上语句可以分为声明和赋值两句

var a // 声明一个变量
a = 1 // 为声明的变量赋值

var 关键字声明的变量的声明会提升到作用域开头,但是赋值不会提升。

JavaScript 的执行过程,先编译,再执行,预编译的作用是什么?是准备执行上下文吗。

编译阶段

编译阶段会将为要执行的 JavaScript 代码在内存中生成两部分内容:执行上下文和可执行的代码。

执行上下文是 JavaScript 执行一段代码时的运行环境。执行上下文中存在一个变量环境对象(VariableEnvironment),它保存了当前代码执行环境中的变量。将名称和值绑定起来。在代码执行过程中,JavaScript 引擎在遇到变量名时会在变量环境对象中查找。根据代码顺序,后声明的变量会覆盖之前声明的变量。

关于作用域中声明的同名变量和函数

  • 如果是同名的函数,JavaScript编译阶段会选择最后声明的那个。
  • 如果变量和函数同名,那么在编译阶段,变量的声明会被忽略
  • 函数提升要比变量提升的优先级要高一些,且不会被变量声明覆盖,但是会被变量赋值之后覆盖。

思考题

分析输出结果

showName()
var showName = function() {
    console.log(2)
}
function showName() {
    console.log(1)
}

解答:

这段代码交给 JavaScript 引擎之后,JavaScript 引擎先对这段代码进行编译,顺序是从上到下。

遇到 var 关键词声明了一个 showName 变量,此时将 showName 添加到函数的执行上下文的变量环境对象中,它的值为默认的 undefined。

随后又遇到了一个函数声明,其声明了名为 showName 的函数,因为在声明同名变量的情况下,函数声明的优先级高于var 声明的变量,所以 showName 变量的值变成函数的地址,

到了代码的末尾,编译过程结束。

开始执行代码,是对 showName 这个名字的函数调用,此时 JavaScript 引擎会在执行上下文中查找 showName 这个名字,在执行上下文的变量环境变量中找到了其对应的值,是一个函数,于是对该函数进行调用,结果输出 1。

然后再执行第二行代码中的赋值部分,将输出 2 的函数赋值给变量 showName。

执行阶段结束。


© 2022, 分享知识和生活,记录成长与感动。