在我们的印象中,当提到JavaScript中的变量提升,我们想到的是“变量和函数的声明在物理层面移动到代码的最前面“。当然这么说不大准确,变量和函数声明在代码里的位置是不会变的,而是在编译阶段被放入内存中。
可是如果我问你:let 到底有没有提升?如果有,var / let / function三者的变量提升一致吗?此时你的答案是什么?
接下来让我们来探讨这两个问题,如有错误敬请指正。

1. let存在提升

对于let是否存在提升这个问题,让我们先来看以下这段代码:

  1. a = "global";
  2. (function() {
  3. console.log(a); // undefined, 而不是打印出global
  4. var a;
  5. }());
  6. {
  7. console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
  8. let a = 1;
  9. }

对于在函数作用域下打印出undefined,我们应该不奇怪。但引起我注意的是在块作用域下,抛出引用错误的原因是在初始化之前找不到a。那有没有可能a创建过程被提升?而在我发现ES文档中存在[var/let hoisting],这让我有理由猜测let存在提升,只是由于暂时性死区的原因,我们不能在let a之前使用 a
为了证明我的猜想,我想先从let声明的创建、初始化和赋值过程入手。

  1. {
  2. let a = 0;
  3. a = 1;
  4. }

上述的代码块中发生的过程如下:

  1. 1. 找到所有用`let`声明的变量,在环境创建变量;
  2. 2. **执行代码;**
  3. 3. 执行`a=0`,将`a`初始化为0
  4. 4. 执行`a=1`,对`a`进行赋值。

由该过程可知,如果我们在创建完变量后和初始化之前执行log()方法,控制台将会报错:

  1. let a = 'global'
  2. {
  3. console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
  4. let a = 1
  5. }

由此一来我们就可以知道了let在创建的过程被提升,而在初始化过程没有被提升。

2. var/function的变量提升

接下来我们来看看function/val的创建、初始化和赋值过程,由此看看能否探究出它们的差别。

2.1 var的变量提升

  1. function foo() {
  2. console.log(a); // undefined
  3. var a = 0;
  4. var b = 1;
  5. }
  6. foo();

当我们执行foo()时,发生以下过程(较为省略):

  1. 1. `foo`创建环境;
  2. 2. 找到`foo`中所有被`var`声明的变量,在这个环境中创建变量;
  3. 3. 将变量初始化为 undefined;
  4. 4. 执行代码;
  5. 5. `a`赋值为0`b`赋值为1

由此我们可以知道var声明在代码执行前创建变量并初始化,所以当我们在var a = 0之前执行log(a)方法会得到 undefined 的结果。

2.2 function的变量提升

  1. foo('btqf');
  2. function foo(name) {
  3. console.log("my name is" + name) // my name is btqf
  4. }

当我们执行foo函数时,发生以下过程:

  1. 1. 找到所有`function`声明的变量,在环境中创建变量;
  2. 2. 将这些变量初始化并赋值;
  3. 3. 执行代码。

由此可见,function声明在代码执行前就创建、初始化并赋值。

3. 总结

  1. let的「创建」过程被提升了,但是初始化没有提升。
  2. var的「创建」和「初始化」都被提升了。
  3. function 的「创建」「初始化」和「赋值」都被提升了。
  4. 函数和变量相比,会被优先提升,即函数会被提升到更靠前的位置。

值得一提的是constlet基本类似,唯一的区别在于const只有创建和初始化,没有赋值过程。
好啦,本文就讲述到这里,谢谢大家的观看!❤❤

参考推荐

一篇好文:我知道你懂hoisting,可是你了解到多深?