二十二、变量作用域和函数声明的提升

二十二、变量作用域和函数声明的提升

22.1 变量作用域分为全局作用域和局部作用域

  • 全局作用域:函数外的区域,函数外声明的变量也叫做全局变量。

    • 全局变量,可以在脚本的任何位置进行调用,它随页面加载生成,页面关闭后销毁。
  • 局部作用域:声明的函数会形成一个自己的作用域,那么在函数内声明的变量就叫做局部变量。

    • 局部变量,函数被调用时创建,调用完毕后销毁

22.1.1 局部变量

  • 局部作用域:声明的函数会形成一个自己的作用域,那么在函数内声明的变量就叫做局部变量。

    • 局部变量,函数被调用时创建,调用完毕后销毁
1
2
3
4
5
6
7
8
9
10
11
<script>
// 在函数内部声明的变量只能在函数内部使用
function fun(){
// a 在函数内部声明的,只能在fun函数内部使用
var a = 10;
console.log(a);
}

// a 在函数外部不能使用
console.log(a); // 报错 Uncaught ReferenceError: a is not defined
</script>

UC8xhT.png

对于JS,只有函数能够关住变量的作用域

22.1.1.1 形式参数是局部变量

形参也是局部变量,只能在函数内部使用,在外部任何地方都不能访问。

1
2
3
4
5
6
7
8
9
10
<script>
// 形参是局部变量
function fun(a, b, c){
console.log(a);
console.log(b);
console.log(c);
}
// a 是形参不能在函数外部使用
console.log(a);
</script>

UCGkH1.png

1
2
3
4
5
6
7
8
9
10
11
12
<script>
// 形参是局部变量
function fun(a, b, c){
console.log(a);
console.log(b);
console.log(c);
}
// 调用
fun(10, 20, 30, 40, 50);
// a 是形参不能在函数外部使用
console.log(a);
</script>

UCGVN6.png

22.1.2 全局变量

  • 全局作用域:函数外的区域,函数外声明的变量也叫做全局变量。

    • 全局变量,可以在脚本的任何位置进行调用,它随页面加载生成,页面关闭后销毁。

22.1.2.1 传递作用

传递作用:在不同函数间使用全局变量可以作为信号量,这些函数都可以改变信号量,使用新值参与计算。

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
<script>
// a 是全局变量,在任何地方都可以使用
// a 称为信号量,不管任何函数改变 a 的值,都可以记住并且再次使用时都是使用 a 改变之后的新值参与计算
var a = 10;
// 函数使用 a
function fun(){
a++;
console.log(a);
}

// 函数fun2 也可以使用a
function fun2(){
a--;
console.log(a);
}

// 调用
fun2();
fun2();
fun2();
console.log("a的当前值为:" + a);
fun();
fun();
fun();
fun();
console.log("a的当前值为:" + a);

</script>

UCGRv4.png

22.1.2.2 通信作用

通信作用:在同一个函数使用全局变量,不会每次都清空,也是使用当前的新值参与计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// 使用全局变量
var a = 1;
// 声明函数
function fun(){
console.log(++a);
}

fun();
fun();
fun();
fun();
fun();
console.log("a 的当前值为:" + a);
</script>

UCJmaq.png

22.2 作用域链

指的是我们变量查找的一个规律:

  • 我们可以在不同的作用域内使用相同的标识符去命名变量。我们在使用一个变量的时候,需要找到匹配的标识符,我们有重复的,用哪一个?

    • 如果在当前作用域有这个变量,就直接使用;

    • 如果当前作用域没有这个变量定义,会一层一层的从本层往外依次查找。

    • 遇到第一个就直接使用,类似于就近原则。

    • 当遇见一个变量时,JS引擎会从其所在的作用域依次向外层查找。查找会在找到第一个匹配的标识符的时候停止。在多层嵌套的作用域中可以定义同名的标识符,发生“遮蔽效应”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
// 使用全局变量 a
var a = 1;
// 声明函数
function fun1(){
var a =20
// 本层作用域有变量,直接使用
console.log(a);
function fun2(){
// 本层作用域没有 a 的定义,从本层出发,向外进行查找到 20
console.log(a);
function fun3(){
var a = 30;
// 本层有 a 定义,直接使用
console.log(a);
}
fun3();
}
fun2();
}
// 调用
fun1();
console.log(a); //向外查找,不要向内
</script>

UCYhA1.png

  • 在函数内部没有使用var声明的变量,在JS运行阶段,调用函数执行到这个变量的时候,这个变量会被当作全局变量使用

  • 注意:没有使用var声明的变量,不会进行变量的提升

  • 建议在使用时,尽量使用var关键字声明变量,避免全局变量的污染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
// 使用全局变量 a
var a = 10;
// fun1里的 a = 20;等价于在此处写 var a = 20;
// 声明函数
function fun1(){
// 函数内部的var省略,强制转为全局变量
a =20
console.log(a);
function fun2(){
console.log(a);
}
fun2();
}
// 调用
fun1();
console.log(a); //向外查找,不要向内
</script>

UCtcPP.png

22.3 函数的作用域

在函数内部声明的函数,只能在函数内部声明,在函数外部任何地方都不能访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
// 声明函数
function outer(){
// 函数内部声明一个变量
var a = 10;
// 函数内部声明一个函数
function inner(){
console.log(a);
}
// 调用函数,在当前函数的外部调用
inner();
}
// 调用函数,在当前函数的外部调用
outer();
//inner 在函数外部调用不能使用
inner();
</script>

UCtbGV.png

22.4 函数声明的提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// 使用function关键字
// 先使用
fun();
// 再声明
function fun(){
console.log(1);
}
/*
提升过程:计算机预解析将所有的声明语句提升到最前面
相当于:var fun;
fun是函数可以通过指针找到定义,相当于整体都进行提升
其他语句原位置执行
*/
</script>

UCNFxO.png

1
2
3
4
5
6
7
8
9
10
11
<script>
console.log(fun); // undefined
var fun = 10 ;
/*
var fun;
// 声明变量没有赋值,结果是undefined
console.log(fun);
fun = 10;
*/
console.log(fun); // 10
</script>

UCN8sg.png

使用函数表达式进行提升,只提升变量名,函数的定义不能进行提升,先调用函数会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
// 先调用
fun();
// 再定义
var fun = function(){
console.log(2); // 控制台报错 Uncaught TypeError: fun is not a function
}
/*
提升变量声明,没有赋值
var fun;
其他语句原位置执行
fun();
fun = function(){
console.log(2);
}
*/
</script>

UCNdJ0.png

总结:

  • 在声明函数时,一般我们都使用function关键字这种,不会出错误。

  • 并且,一般我们习惯先书写函数的调用,将声明书写在所有语句之后,便于代码读取。

变量名和函数名(function 关键字())相同,优先提升函数名,也就是将名字优先给函数使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
// 先调用
console.log(fun);
// 后声明
var fun = 10;
function fun(){
console.log(20);
}
/*
预解析:
函数优先提升
function fun(){
console.log(20);
}; fun名字已经存在,不会重复声明
var fun;(var fun提升,但不会书写出来)
其他语句原位置执行
console.log(fun);
变量声明一次可以多次赋值
fun = 10;
*/
console.log(fun); // 10
</script>

UCN4SK.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
// 先调用
fun();
// 后声明
var fun = function(){
console.log(1);
}
function fun(){
console.log(2);
}
/*
预解析:
function fun(){ console.log(2);}; (fun名字已经声明)
var fun; 提升 但不会再书写出来
fun();
fun = function fun(){ console.log(1);}
*/
fun(); // 1
console.log(fun); // function fun(){ console.log(2);}
</script>

UCNbTA.png

  • 变量的提升,预解析阶段会将var声明的变量提升到当前作用域的最顶层

  • 局部变量函数调用时创建,调用完毕后销毁

  • 在函数内部没有使用var声明的变量,在JS运行阶段,调用函数执行到这个变量的时候,这个变量会被当作全局变量使用

  • 注意:没有使用var声明的变量,不会进行变量的提升

  • 建议在使用时,尽量使用var关键字声明变量,避免全局变量的污染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
// 声明一个全局变量number,并赋值10
// 全局变量在脚本的任何位置都可以被调用到
var number = 10;
// 声明一个函数fn,形成一个局部作用域
function fn(){
// 变量的提升,预解析阶段会将var声明的变量提升到当前作用域的最顶层
// var number;
console.log(number); // undefined
// 局部变量函数调用时创建,调用完毕后销毁
var number = 200;
console.log(number); // 200
}
// 调用函数 执行函数里的两个console.log(number)
fn();
// 调用全局变量number
console.log(number); // 10
</script>

UCUkYq.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
// 声明一个全局变量number,并赋值10
// 全局变量在脚本的任何位置都可以被调用到
var number = 10;
// 声明一个函数fn,形成一个局部作用域
function fn(){
// 变量的提升,预解析阶段会将var声明的变量提升到当前作用域的最顶层
// 注意:没有使用var声明的变量,不会进行变量的提升
// 建议在使用时,尽量使用var关键字声明变量,避免全局变量的污染
// var number;
console.log(number); // 10
// 局部变量函数调用时创建,调用完毕后销毁
// 在函数内部没有使用var声明的变量,在JS运行阶段,调用函数执行到这个变量的时候,这个变量会被当作全局变量使用
number = 200;
console.log(number); // 200
}
// 调用函数 执行函数里的两个console.log(number)
fn();
// 调用全局变量number
console.log(number); // 200
</script>

UCwjEQ.png

点击查看

本文标题:二十二、变量作用域和函数声明的提升

文章作者:Mango

发布时间:2020年07月08日 - 21:57:19

最后更新:2020年07月08日 - 22:35:39

原始链接:https://mango185.github.io/post/e2dbc1ea.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------