JavaScript中的变量作用域、作用域链

基础的变量作用域认知

首先,JavaScript中,函数内部可以访问全局定义的变量:

1
2
3
4
5
6
7
8
9
10
var var1 = 1;
function test(arg){
console.log(var1);
}
test(var1);
/*结果是:
1
undefined
//证明在函数内部可以访问到全局的var1
*/

其次,JavaScript中局部变量会覆盖全局变量:

1
2
3
4
5
6
7
8
9
10
11
var var1 = 1;
function test(arg){
var var1 = 2;
console.log(var1);
}
test(var1);
/*结果是:
2
undefined
//证明函数内部的var1覆盖了全局的varl
*/

形参与同名局部变量:

1
2
3
4
5
6
7
8
9
10
function test(i){
var i;
alert(i);
}
test(10);

//实际上这里只有一个变量i
//程序在运行时,首先声明形参i,值为10
//然后处理局部变量i声明,在这里i已经声明过,不作处理
//在javascript中变量被多次声明是不会报错的,程序直接跳过不做处理

最后,JavaScript中,函数内部可以声明全局变量,只是在声明变量时,不加var声明就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
var var1 = 1;
function test(arg){
var2 = 2;
console.log(var1);
}
test(var1);
console.log(var2);
/*结果是:
1
2
undefined
//证明函数外部可以访问到varl
*/

但是,考虑以下情况:

1
2
3
4
5
6
7
8
9
10
var var1 = 1;
function test(arg){
var2 = 2;
console.log(var1);
}
console.log(var2);
test(var1);
console.log(var2);
/*结果是:
Uncaught ReferenceError: var2 is not defined */

以及以下情况:

1
2
3
4
5
6
7
8
9
function test(arg){
arg = 5;
console.log(arg);
}
console.log(arg);
test();
console.log(arg);
/*结果是:
Uncaught ReferenceError: arg is not defined */

同样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function mygl1(){
console.log(testgloable);
testgloable = 10;
}
mygl1()/*结果是:
Uncaught ReferenceError: testgloable is not defined */

function mygl2(){
testgloable = 10;
console.log(testgloable);
}
mygl2()/*结果是:
10
undefined */
证明不加var虽然可以得到全局变量,但是这样得到的变量不会提前。

删掉一句:

1
2
3
4
5
6
7
8
9
function test(arg){
arg = 5;
console.log(arg);
}
test();
console.log(arg);
/*结果是:
5
Uncaught ReferenceError: arg is not defined */

证明形参定义的是局部变量,是 var varx = xxx;形式的语句。

块级作用域、函数作用域

块级作用域:

任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// c语言
#include <stdio.h>
void main()
{
int i=2;
i--;
if(i)
{
int j=3;
}
printf("%d/n",j);
}
//运行这段代码,会出现“use an undefined variable:j”的错误。
//可以看到,C语言拥有块级作用域,因为j是在if的语句块中定义的,因此,它在块外是无法访问的。

函数作用域:
1
2
3
4
5
6
7
function test(){ 
for(var i=0;i<3;i++){
}
alert(i);
}
test();
//结果是:3

注:在ECMAScript6(以下简称ES6)之前,ECMAScript的作用域只有两种:
  1、 全局作用域
  2、 函数作用域
  但,ES6的到来,为我们提供了”块级作用域”,且”块级作用域”并不影响 var 声明的变量。

作用域链

作用域链是针对函数来说的,每个函数对应自己的作用域链。
当我们把一段JavaScript函数代码加载加入引擎的过程可以这样看待:
6310069-edef14cec1bf9461.png
一个JavaScript函数,在被加载到JavaScript引擎后,生成一个对象,这个对象包含两个属性,一个是一段可执行的代码块,另一个是一个引用,指向一个作用域链,比如,图中的函数在加载加入JavaScript引擎时(也可以理解为函数定义时,因为,此时JavaScript引擎会将函数处理,然后方便后面使用),生成的对象的引用指向的作用域链是全局对象。当我们要调用此函数时,生成一个对象,将函数内部的局部变量(此处为var1)添加到生成的对象,然后将其放到作用域链上,形成新的作用域链,这是调用时的作用域链,然后调用是,执行代码块,此时的变量查找范围为新生成的作用域链。函数调用完成后,删除添加到作用域链上的对象,函数的在引擎中的表示对象回到调用前的状态。



参考:
[1]JavaScript的作用域和块级作用域概念理解
[2]JS在函数中形参和局部变量同名的问题
[3]ES6之块级作用域

[4]JavaScript深入之作用域链
[5]javascript的词法作用域
[6]JavaScript深入之词法作用域和动态作用域
[7]动态作用域和词法域的区别是什么?
[8]JS中的作用域链是在什么时候建立的?

[9]词法作用域与动态作用域的区别
[10]Javascript作用域和变量提升
[11]块级作用域与函数作用域
[12]javascript里面对象和函数的区别?
[13]闭包 (计算机科学))