JavaScript作用域

变量是所有编程语言中最基础的概念,我们通过存储,修改变量的值来运行程序。那么变量是如何存放的,如何访问变量?我们将这些规则称为作用域。

看一个例子 

1
var a =2;

我们看到这段代码,会认为它是一个语句。但是Javascript的引擎并不这么认为,它首先看到

1
var a

看到这个之后就去查询作用域是否存在 变量a ,如果作用域存在变量a,那么它就会忽略这个声明,否则就将a加入作用域。接下来再处理

1
a=2

的赋值。

JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里

在JS中,每一个函数都有一个作用域,进入一个函数就可以访问到这个函数作用域内的变量。在作用域中, 一个声明出现的位置并不重要。因为JS会在最初的时候就将作用域内声明的变量添加到作用域当中去,并且函数声明的优先级要高于普通变量的声明 (同一个变量既有函数声明又有变量声明,那么变量声明会被忽略) 。

举几个例子

1
2
3
4
5
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}

这里有两个变量,foo和a,虽然声明都位于访问之后,但是访问的时候并不会发生错误,因为程序执行最开始做的就是将声明提到最前面,等价于下面这段代码。

1
2
3
4
5
6
7
8
9
function foo() {
var a;

console.log( a ); // undefined

a = 2;
}

foo();

再看一个例子

1
2
3
4
5
6
7
8
9
10
11
foo(); // 1

var foo;

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

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

有一个变量叫做foo,有一个函数变量也叫foo,那么变量foo就会被忽略。最后一个并不是声明,而是赋值,所以并不会有变量提升的过程。

JS中不存在{}框住的块状作用域,但是with,try/catch具有的是块状作用域,在ES6中引入的let关键字将为Javascript带来块状作用域。

1
2
3
4
5
6
7
var foo = true;
if (foo) {
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
console.log( bar ); // ReferenceError

最后再看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
function foo() {
console.log( a ); // 2
}

function bar() {
var a = 3;
foo();
}

var a = 2;

bar();

这里bar执行的结果,或者说在控制台输出的结果是什么呢?我们最初的时候引用了一句Javascript权威指南中的一句话,JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。通过这个例子应该明白这句话的意思,它说的是foo在哪里执行并不重要,它访问的作用域是定义时候就被确定下来了的。它具有的是静态作用域。发明this就是为了让Javascript具有动态作用域。

至于作用域链,应该还是很好理解的,可以看看Javascript作用域原理,比较深入的解释了作用域的原理。