概念

所有变量的声明语句都会被提升到代码头部,这就是变量提升。

eg1

  1. console.log(a);
  2. var a =1;

以上语句并不会报错,只是提示undefined。实际在js引擎中的运行过程是:

  1. var a;
  2. console.log(a);
  3. a =1;

实际运行表示变量a已声明,但还未赋值。

但是变量提升只对var命令声明的变量有效,如果一个变量不是用var命令声明的,就不会发生变量提升。

  1. console.log(aa);
  2. aa =1;

以上代码将会报错:ReferenceError: aa is not defined

补充:js里的function也可看做变量,也存在变量提升情况

  1. a();
  2. function a(){
  3. console.log(1);
  4. };

表面上,上面代码好像在声明之前就调用了函数a。但是实际在js引擎中,由于“变量提升”,函数a定义部分被提升到了代码头部,也就是在调用之前已经声明了。

但是!如果采用赋值语句定义函数,JavaScript就会报错:

  1. a();
  2. var a = function(){
  3. console.log(1);
  4. };
  5. // TypeError: a is not a function

因为js引擎把变量声明提升,此时,a就是一个变量,而并不是一个function,以下是js引擎实际运行代码:

  1. var a;
  2. a();
  3. a = function(){
  4. console.log(1);
  5. };

PS函数声明方式区别

  1. // 函数表达式
  2. var f = function() {
  3. console.log(1);
  4. }
  5. // 直接声明
  6. function f (){
  7. console.log(2);
  8. }

第一种方式,函数只能在声明之后调用。因为这种方式声明的函数,是在函数运行的阶段才赋值给变量 f 的;

第二种方式,函数可以在声明函数的作用域内任一地方调用。因为这种方式,是在函数解析阶段赋值给标识符 f .

值得注意的是,当同时使用这两种方式声明同一个函数名,最终执行的是函数表达式声明的函数。

  1. // 函数表达式
  2. var f = function() {
  3. console.log(1);
  4. }
  5. // 直接声明
  6. function f (){
  7. console.log(2);
  8. }
  9. f();
  10. // 1

例子

  1. b() // call b
  2. console.log(a) // undefined
  3. var a = 'Hello world'
  4. function b() {
  5. console.log('call b')
  6. }

在生成执行上下文时,会有两个阶段。第一个阶段是创建的阶段(具体步骤是创建 VO),JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined💛,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用。

在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升

  1. b() // call b second
  2. function b() {
  3. console.log('call b fist')
  4. }
  5. function b() {
  6. console.log('call b second')
  7. }
  8. var b = 'Hello world'

var 会产生很多错误,所以在 ES6中引入了 letlet 不能在声明前使用,但是这并不是常说的 let 不会提升,let 提升了声明但没有赋值,因为临时死区导致了并不能在声明前使用。