变量是所有编程语言中最基础的概念,我们通过存储,修改变量的值来运行程序。那么变量是如何存放的,如何访问变量?我们将这些规则称为作用域。
看一个例子 1
var a =2;
我们看到这段代码,会认为它是一个语句。但是Javascript的引擎并不这么认为,它首先看到
1 | var a |
看到这个之后就去查询作用域是否存在 变量a ,如果作用域存在变量a,那么它就会忽略这个声明,否则就将a加入作用域。接下来再处理
1 | a=2 |
的赋值。
JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里
在JS中,每一个函数都有一个作用域,进入一个函数就可以访问到这个函数作用域内的变量。在作用域中, 一个声明出现的位置并不重要。因为JS会在最初的时候就将作用域内声明的变量添加到作用域当中去,并且函数声明的优先级要高于普通变量的声明 (同一个变量既有函数声明又有变量声明,那么变量声明会被忽略) 。
举几个例子
1 | foo(); |
这里有两个变量,foo和a,虽然声明都位于访问之后,但是访问的时候并不会发生错误,因为程序执行最开始做的就是将声明提到最前面,等价于下面这段代码。1
2
3
4
5
6
7
8
9function foo() {
var a;
console.log( a ); // undefined
a = 2;
}
foo();
再看一个例子1
2
3
4
5
6
7
8
9
10
11foo(); // 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
7var foo = true;
if (foo) {
let bar = foo * 2;
bar = something( bar );
console.log( bar );
}
console.log( bar ); // ReferenceError
最后再看一个例子:
1 | function foo() { |
这里bar执行的结果,或者说在控制台输出的结果是什么呢?我们最初的时候引用了一句Javascript权威指南中的一句话,JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。通过这个例子应该明白这句话的意思,它说的是foo在哪里执行并不重要,它访问的作用域是定义时候就被确定下来了的。它具有的是静态作用域。发明this就是为了让Javascript具有动态作用域。
至于作用域链,应该还是很好理解的,可以看看Javascript作用域原理,比较深入的解释了作用域的原理。