作用域
JavaScript 中没有块级作用域,只存在函数作用域,函数是它的第一类。具体表现如下:
for(var i = 0; i < 10; i++){
//do something
}
alert(i);//10
var example = function(){
var x = 10;
//do something
}
alert(x);//ReferenceError: x is not defined
上面的例子应该可以清楚地解释了 JavaScript 的作用域问题。在
for
里面定义的变量其实都是全局变量。因为 JavaScript 没有块级作用域。然后,在函数内部定义的变量均为局部变量。当在函数内部引用某个变量时,解释器会先在函数内部的作用域中查找该变量。假如在函数内部的作用域中找到了该变量,则返回该变量的值。如果在函数内部的作用域中找不到该变量,则在外一层作用域中查找变量。如下面的例子:var i = 10;
var x = 11;
var y = 20;
var ex = function(){
var i = 1;
var x = 2;
alert(i); //1
var a = function(){
alert(x); //2
alert(y); //20
}
a();
}
ex();
声明提升
在 JavaScript 中定义变量和函数时,其实会经历一个预编译过程。具体表现为:函数声明和变量声明总是被 JavaScript 解释器隐式地提升到当前作用域的顶端。来看例子:
x(); //hi
function x(){
alert("hi");
}
hoist();
var hoist = function(){
alert(a); //undefined
alert(b);
var a = 10;
}
hoist();
上面的例子其实包含了几个方面的内容。我们先来看第一个例子。这个例子中,我们在函数声明之前就执行了该函数,结果居然没报错。而在下面的例子中,我们也是在定义了
hoist
函数之前就执行它,结果控制台报错: hoist is not a function
,而最后面再执行该函数又能正常响应。这是为什么呢?在 JavaScript 中,通过函数声明定义的函数,都会被解析器提升到作用域的顶端。所以在第一个例子中,实际的顺序是这样的:
function x(){
alert("hi");
}
x();
因此该函数能够正常执行。而对于通过函数表达式定义的函数则没有这个功能。第二个例子就能看出。在函数表达式前执行该函数会报错,提示该变量不是函数。
第二个函数里面涉及到了变量声明提升的问题。你可以在浏览器中看到,该函数执行之后
alert
返回的是 undefined
之后浏览器就报错了。为什么呢?和函数声明一样,使用变量声明会使得变量的声明被隐式地提升到作用域的顶端。但是这个提升仅仅只是声明,而赋值部分则没有。所以第二个函数的实际顺序是这样子的:var hoist;
...
hoist = function(){
var a;
alert(a);
alert(b);
a = 10;
}
所以对
a
执行 alert
只是返回 undefined
,而不是跟后面那个没有定义的 b
一样,浏览器报错,显示 b is not defined
。总的来说,就是,通过函数声明(通过
function
声明的函数)和变量声明(所有通过 var
声明的变量)会使得该变量或函数被隐式地提升到该作用域的顶端。但是只有函数声明有这种效果,函数表达式没有。变量声明中也只提升声明部分,而赋值部分也没有提升。另外需要注意的是,如果在同一个作用域下定义了两个同名的函数和变量,则函数声明比变量声明拥有更高的优先级。
完。