JavaScript Hoisting

引子

关于 JavaScript 提升(Hoisting),一般在实际使用的过程中,只要遵循了“先声明,后使用”的约定,很少会碰到问题。但浏览器引擎中肯定考虑各种情况,经历了这些问题,吃一堑长一智,还是总结一下。

提升

先看下面打印的是什么。

console.info(a);
a = 1;
var a;
console.info(a);

学过 JavaScript 基础就会知道 var 声明会先提升,第一个会打印 undefined ,按顺序执行后, a 被赋值,第二个打印是 1 。基于这样的解释,个人产生的疑惑会有:

  • 为什么会提升?
  • 这个“提升”是指声明代码都提升到最上面了吗?

带着这些疑问,去查找资料,从中了解到下面这些内容。

JavaScript 代码在执行前都要进行编译,大部分情况下编译发生在代码执行前的几微秒(甚至更短)的时间内。编译通常会经历三个步骤:

  • 分词/词法分析(Tokenizing/Lexing) :这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)。
  • 解析/语法分析(Parsing) :这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为抽象语法树(Abstract Syntax Tree,AST)。
  • 代码生成 : 将 AST 转换为可执行代码的过程称被称为代码生成。这个过程与语言、目标平台等息息相关。

JavaScript 引擎相对上面步骤要复杂得多,例如,在语法分析代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。

编译的词法分析阶段基本能够知道全部(注意 eval 和 with)标识符在哪里以及是如何声明的,从而能够预测在执行过程中如何对它们进行查找。也就是说,变量和函数在内的所有声明会在代码执行前先被处理。因此实际上声明的代码的位置是不会变动的,而是在编译阶段被放入内存中。

提升优先级

通过上面的了解,我们知道变量和函数声明都会提升,变量名和函数名是一样的时候会如何?

// 先变量声明,后函数声明
console.info(b);
var b = 1;
function b() {
  console.info(2);
}

// 先函数声明,后变量声明
console.info(c);
function c() {
  console.info(3);
}
var c = 4;

通过上面的例子可以发现:函数声明比变量声明优先提升

函数声明和函数表达式

函数声明语法如下:

function name([param[, param[, ... param]]])

函数声明的 name 必须要有。

函数表达式和函数声明非常相似,它们有相同的语法。

var myFunction = function name([param[, param[, ... param]]])

函数表达式的 name 非必需,写上 name 可以在调用堆栈时使用,当省去 name 时,就成了匿名函数。

函数表达式不会提升,所以不能在定义之前调用。

参考资料

posted @ 2020-06-17 14:09  XXHolic  阅读(147)  评论(0编辑  收藏  举报