js作用域

先说结论,在 JavaScript 中,作用域只有两种。全局作用域(window)局部作用域(函数)
全局作用域中的所有对象均为 window 对象的属性。局部作用域(函数)中的所有对象在整个函数范围内均是可见的,也就是说 JavaScript 没有类似其他语言中的块级作用域。

作用域链

当发生变量的调用时,调用方先在当前局部作用域(作用域链的第一个对象)寻找变量,没有的话沿着作用域链向着外层局部作用域(作用域链的第二个对象)寻找变量,还没有的话就继续沿着链寻找全局变量(作用域链的最后一个对象)。

下述代码中涉及到的作用域对象分别是inner->outter->window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var hello = 1;
function outter(){
var world = 2;
function inner(){
var innerVar = 4;
console.log(hello);
}
inner(); //调用inner函数
}
outter(); //调用outter函数
outter.na = "as"

/*outpout
1
*/

函数体内部局部变量的优先级高于全局变量

这一点和其他语言类似,如以下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
var hello = 1;    //定义全局变量 hello
function check(){

var hello = 100; //定义局部变量hello
console.log( hello ); //打印 100
}
check();
console.log( hello ); //打印 1
/*output:
1
1
*/

但需要注意的是以下代码所示的这种情况:

1
2
3
4
5
6
7
var x = 1;
function func(){
console.log( x ); //'undefined',不是1
var x = 'skyfly';
console.log( x ); //'skyfly'
}
func();

由于 JavaScript 的解释执行机制分为两步,分别是预编译(预处理)和解释执行,在预处理的时候已经发现了局部变量 x,由于同名局部变量的优先级高于全局变量。所以在执行的时候,func 中局部变量 x 的优先级最高。预编译时期也只进行变量和函数的声明,后续的定义在执行时期才能发生。于是这里的第一个 x 是 ‘undefined’。

JavaScript 不存在块级作用域

如下所示:

1
2
3
4
5
6
7
8
9
function func() {
var i = 0;
if (true) {
var j = 2;
for (var k = 0; j < k; j++) {
console.log(k);
}
}
}

可见,在函数之内的所有非嵌套函数内声明的变量均是可见的,说明 JavaScript 没有传统意义上的块级作用域。这也说明,JavaScript 只有函数window 作用域

不以 var 声明的变量是全局变量

1
2
3
4
5
6
7
8
<script type="text/javascript">
var a = 100 //在node中这个是局部变量,作用域在模块内。在浏览器中,这个是全局变量,属于window
function rain(){
b = 100; //声明了全局变量x并进行赋值
}
rain();
alert( b ); //会弹出100
</script>

没有使用 var 声明的变量,均为 window 作用域的全局变量,即便声明存在于函数中。


在Node.js 的 REPL 环境下,a 是全局变量,被挂载在 global 对象之下。在 Node.js 的模块环境之下,全局变量必须显式声明称 global 对象的属性。或者不使用 var 修饰。
browser 下两者都会被视为全局变量。

标签的作用域

标签的作用域只限于本代码块内,在全局作用域无效,也不会被块内函数继承,以下代码将报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
list: {
function breakFun () {
break list;
}
console.log(cars[0] + "<br>");
console.log(cars[1] + "<br>");
console.log(cars[2] + "<br>");
console.log(cars[3] + "<br>");
breakFun();
console.log(cars[4] + "<br>");
console.log(cars[5] + "<br>");
}
//ERR: Undefined label 'list'

正确的写法:

1
2
3
4
5
6
7
8
9
list: {
console.log(cars[0] + "<br>");
console.log(cars[1] + "<br>");
console.log(cars[2] + "<br>");
console.log(cars[3] + "<br>");
break list;
console.log(cars[4] + "<br>");
console.log(cars[5] + "<br>");
}

其他参见:
js函数作用域和对象作用域里变量的不同
Node.js 启动方式:一道关于全局变量的题目引发的思考