JS的变量作用域

作者 johansen 日期 2017-03-10
JS的变量作用域

Javascript变量作用域详解


最近在用babel-node 进行代码转换,也在看ES6相关的东西,看ES6时,真是温故知新的感觉,因此有了这篇博客。

JS作用域规则

  • 1.JS大部分情况下没有块级作用域, 除非你使用let
  • 2.当你使用var的情况下, JS仅仅支持函数作用域
  • 3.不使用var, let声明的变量为全局变量
  • 4.不用let情况下, JS中局部变量只能通过var和函数参数声明
1. JS大部分情况下没有块级作用域, 除非你使用let

与很多语言不同, JS在ES6之前并没有块级作用域:

1
2
3
4
5
6
7
8
9
10
11
function test() { // 一个作用域
for(var i = 0; i < 10; i++) { // 不是一个作用域
// count
}
console.log(i); // 10
for(let j = 0; j < 10; j++) { // 是一个独立作用域
// count
}
console.log(j); // j is not defined
}

本例中i并不只存在于for循环的区域中而是存在于整个test函数中. 如果我们用let声明变量i, i则拥有了块级作用域

2. 在你使用var的情况下, JS仅仅支持函数作用域
1
2
3
4
5
function test() {
var a = 'hello'
}
test()
console.log(a) // a is not defined

本例中在函数内部声明的变量a的作用域仅限于函数内部, 所以在函数外部我们不能访问到.

3. 不使用var, let声明的变量为全局变量

如果我们把上面的例子中的var去掉会发生什么呢?

1
2
3
4
5
function test() {
a = 'hello'
}
test()
console.log(a) // hello
4. 不用let情况下, JS中局部变量只能通过var和函数参数声明

由上面的分析我们知道, 函数内部声明的变量的作用域为函数内也就是属于局部变量.

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
//Exp1: 典型错例
for (var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
}, 1000);
} // 10 10 10...
//Exp2: 利用setTimeout函数传入参数
for (var i = 0; i < 10; i++){
setTimeout(function(i){
console.log(i);
}, 1000, i);
} // 1 2 3 4 ...
//Exp3: 利用bind传入函数参数
for (var i = 0; i < 10; i++){
setTimeout(function(i){
console.log(i);
}.bind(null, i), 1000);
} // 1 2 3 4 ...
//Exp4: 利用闭包的性质传入参数
var cb = function(i) {
return function() {
console.log(i)
}
}
for (var i = 0; i < 10; i++){
setTimeout(cb(i), 1000);
} // 1 2 3 4 ...

后三个例子看似无关联其实原理是一致的: 把变量当作函数参数传入函数, 这样变量就在函数中有了局部作用域而非全局作用域.在ES6时代我们只需要通过let创建拥有快级作用域的变量就可以轻松解决上述问题了.

1
2
3
4
5
6
//Exp5: 利用let创建块级作用域参数
for (let i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
}, 1000);
} // 1 2 3...