产生因素

函数在执行时会先创建当前的执行上下文环境。其中产生声明提升的因素有以下两点:

  • 变量定义
  • 函数声明

编译阶段

当 js 引擎开始解析脚本时,这时代码还没有运行,会先给代码中的变量、函数声明设置内存,并暂时将变量赋值为 undefined,函数先声明为可使用;为后面即将执行的代码做准备,这个阶段就是编译阶段。

比如 var a = 1; JS 引擎会将其看作两个声明:var a; 和 a = 1;。第一个定义声明在编译阶段进行,第二个赋值声明会被留在原地等待执行阶段。


变量声明提升

只有 var 声明的变量存在变量提升;let、const 会形成块级作用域,造成暂时性死区。

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

console.log(fun); // undefined
var fun = function () {};

上面例子都会会打印出 ‘undefined’,变量声明提升会将变量声明提升到作用域顶部,但只会提升声明部分,不会提升赋值部分,实际提升后结果如下:

1
2
3
4
5
6
7
var a;
console.log(a);
a = 1;

var fun;
console.log(fun);
fun = function () {};

函数声明提升

1
2
func();
function func() {}

上例不会报错,因为 ‘函数声明提升’,即将函数声明提升(整体)到作用域顶部(注意是函数声明,不包括函数表达式),实际提升后结果同下:

1
2
function func() {}
func();

函数声明和变量声明提升的优先顺序

1
2
3
4
5
6
7
8
9
var func = 1
console.log(typeof(func)) // number
function func () {}

-----

console.log(typeof(func)) // function
function func () {}
var func = 1

上两例将分别输出 ‘number’、’function’,原因是 函数声明提升优先于变量提升,函数声明会被变量赋值影响,但不会被变量声明影响,实际提升后结果如下:

1
2
3
4
5
6
7
8
9
10
function func () {}
var func = 10
console.log(typeof(func))

-----

function func () {}
var func
console.log(typeof(func))
func = 10

局部与全局变量

1
2
3
4
5
6
7
8
a = 1;
function func() {
window.a = 2;
console.log(a); // undefined
var a;
console.log(window.a); // 2
}
func();

上例将会分别打印出 ‘undefined’、’5’,第一处为局部变量,第二处为全局变量,实际提升后结果如下:

1
2
3
4
5
6
7
8
9
function func() {
var a;
window.a = 2;
console.log(a); // undefined
console.log(window.a); // 2
}
var a;
a = 1;
func();