变量提升与函数提升

直接使用则定为全局变量

1
2
3
4
5
6
function fn(){
var a = (b = 10);
}
fn();
console.log(b);//10
console.log(a);//a is not defined

上例中声明了b为全局变量并赋值为10,a为局部变量在函数结束后销毁,故而输出如上

变量提升

使用var来声明变量时,声明提升到它所在作用域的顶端去执行,到代码所在的位置来赋值

为什么会发生提升?

涉及到编译原理的知识

JS的源代码在运行的时候,会经过编译和执行两个阶段

编译

  • 词法解析,把字符串解析成一个个有意义的代码块(词法单元)
    • var a = 1;=>var、a、=、1;空格需要看意义而定
  • 语法解析,词法单元解析成抽象语法树
  • 代码生成,将语法解析阶段生成的AST转译成可执行的代码
  • 变量提升
  • LHS查询,在编译的过程中,先将标识符和函数声明给提升到其对应的作用域的顶端。标识符解析的时候,会进行LHS查询,在LHS查询的时候,如果标识符一直找不到声明的位置,那么最终就会在全局环境生成一个全局变量。 LHS : 指的是赋值操作的左端。
  • RHS查询,如果找不到对应的标识符,抛出异常:ReferenceError

下面来看几个例子

1
2
3
4
5
6
7
8
9
10
function test(){
console.log(a); //undefined
var a = 123;
}
//实际执行顺序
function test(){
var a;
console.log(a);
a = 123;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
console.log(v1);  //undefined
var v1 = 100;
function foo() {
console.log(v1);
var v1 = 200;//变量提升在函数中依然存在
console.log(v1);
}
//所以相当于以下代码
/*function foo(){
var v1;
console.log(v1);
v1 = 200;
console.log(v1);
}*/
foo();
//undefined
//200
console.log(v1);//100

函数提升

具名函数的声明有两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.函数声明式
function bar(){}
2.函数字面量式/函数表达式
var foo = function(){}
函数字面量的声明与变量提升的结果一样,函数只是一个具体的值
但是函数声明略有不同
console.log(bar);
function bar(){
console.log(1);
}
//结果
/*
f bar(){
console.log(1)
}
*/
执行顺序相当于
/*
function bar(){
console.log(1);
}
console.log(bar);
*/

几个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function hoistFunction() {
foo(); // 2

var foo = function() {
console.log(1);
};

foo(); // 1

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

foo(); // 1
}

hoistFunction();
//等价于
/*
function hoistFunction() {
function foo() {
console.log(2);
}
var foo;

foo(); // 2

foo = function() {
console.log(1);
};//覆盖

foo(); // 1

foo(); // 1
}

hoistFunction();
*/
此例也可以看出函数提升与变量提升相比函数优先
1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(typeof a);
a();
var a = 3;
function a() {
console.log(typeof a);
}
console.log(typeof a);
a = 6;
a();
//输出结果为
//function
//function
//number
//a is not a function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function f() {
console.log(typeof f); //function
// var f = 3;
f = 3;
console.log(typeof f); //number
};

f();

var s = function s() {
console.log(typeof s); //function
// var s = 3;
s = 3;
console.log(typeof s); //function
};
s();
//声明中可以函数内部改变函数名,而函数表达式,如果有函数名,则函数名只能作用在其自身作用域中,且不可改变函数名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
function fn(a) {
// a = '变量';
var a = 'lisi';
// var a;
function a() {
return 'a方法';
}
return a;
}
console.log(fn('参数'))

//变量>函数>参数>提升
1.变量>函数
function fn(a) {
a = '变量';

function a() {
return 'a方法';
}
return a;
}
console.log(fn('参数'))
//变量
2.函数>参数
function fn(a) {
function a() {
return 'a方法';
}
return a;
}
console.log(fn('参数'))
/*ƒ a() {
return 'a方法';
}
*/
3.参数>提升
function fn(a) {
var a;
return a;
}
console.log(fn('参数'))
//参数

变量提升与函数提升
https://blog-theta-ten.vercel.app/2021/07/01/变量提升与函数提升/
作者
Chen
发布于
2021年7月1日
许可协议