# 提升

作用域同期中声明变量出现的位置有某种微妙的关系

# 先有鸡还是先有蛋

a = 2
var a
console.log(a) // 2
1
2
3
console.log(a) // undefined
var a = 2
1
2

到底是声明(蛋)在前,还是赋值(鸡)在前??

包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理

当看到 var a = 2 时,可能会认为是一个声明。但 JavaScript 实际上会将其看成两个声明; var aa = 2第一个声明是在编译阶段进行的。第二个声明是会被留在原地等待执行阶段

即:

var a
a = 2
console.log(a) // 2
1
2
3
var a 
console.log(a) // undefined
a = 2
1
2
3

这个过程就好像变量和函数声明从他们在的代码中出现的位置被“移动”到了最上面。这个过程被叫做提升。

函数声明会被提升,函数表达式不会被提升

foo()

function foo () {
  console.log('我是foo')
}
1
2
3
4
5
foo() // TypeError: foo is not a function
bar() // ReferenceError: bar is not defined
var foo = function bar () {
  // ...
}
1
2
3
4
5

上述代码片段经过提升后,会被理解为以下形式

var foo
foo()
bar()

foo = function () {
  var bar = ...self...
}

1
2
3
4
5
6
7
8

# 函数优先

函数声明和变量声明都会被提升,但是函数首先被提升,然后才是变量

foo() // 1

var foo 
// 尽管出现在 funciton foo ...的声明之前,
// 但他是重复声明(因此被忽略了)函数声明会被提升到普通变量之前

function foo () {
  console.log(1)
}

foo = function () {
  console.log(2)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

以上代码会被引擎理解成:

function foo () {
  console.log(1)
}

foo() // 1

foo = function () {
  console.log(2)
}

1
2
3
4
5
6
7
8
9
10

一个普通块内部的函数声明通常会被提升到所在作用域顶部

foo() // b

var a = true

if (a) {
  function foo () {
    console.log('a')
  }
} else {
  function foo () {
    console.log('b')
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 小结

我们习惯将 var a = 2 看做是一个声明,而实际上 JavaScript 引擎并不这么认为。它将 var aa = 2 当做是两个单独的声明,第一个是编译阶段的任务,第二个是执行阶段的任务

无论作用域中的声明出现在什么地方,都将代码本身被执行前首先进行处理。可以将这个过程形象的想象成所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程被称为提升。