接触es2015时let有一个比较重要的特点是没有变量提升的,当初看的是阮一峰老师的ECMAScript入门,这句话被明确的写出来的。不过最近发现真实情况貌似更复杂一点。

变量提升是js比较特殊的一个特性,然而在es2015中用let(const, class也一样)声明变量的话似乎变量似乎无法提升,在声明前使用会得到一个referenceError。

本来我是以为就是简单的没有hoisting,然而前一阵子看到一个问题,事情可能没有这么简单。于是google了一把。

正文

一个变量从声明到可以使用的生命周期大致有三个阶段:

When the engine works with variables, their lifecycle consists of the following phases:

  1. Declaration phase is registering a variable in the scope.
  2. Initialization phase is allocating memory and creating a binding for the variable in the scope. At this step the variable is automatically initialized with undefined.
  3. Assignment phase is assigning a value to the initialized variable.

而let只是hoist了第一步,也就是declaration阶段,此时变量在一个“临时死区(temporal dead zone)中”,此时仍是不可访问的,所以会抛出referenceError。

In ECMAScript 2015, let will hoist the variable declaration to the top of the block, but not the initialization. Referencing the variable in the block before the initialization results in a ReferenceError (contrary to a variable declared with var, which will just have the undefined value). The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.

与之相比,var的hoisting会完成前两步,所以可以访问到这个变量为undefined(初始化默认值)。

BTW,函数声明提升会完成全部三个步骤,所以可以在函数声明前使用这个函数。

js中所有的变量声明都会有变量提升,只不过hoisting完成的步骤不一样。class和const行为和let相似。

参考文章