本文深入解析JavaScript中全局变量与局部变量的作用域划分规则,重点讲解变量提升机制对代码执行结果的影响。通过对比不同代码段,阐明function块与循环语句在作用域上的差异,以及重名变量覆盖与访问全局变量的正确方式。
JavaScript变量作用域与提升机制解析
近日,团队中有位实习生向我请教了两段JavaScript代码的执行差异。
代码一示例如下:
<script type="text/javascript">
var a = "Hello";
function test(){
var a;
alert(a);
a = "World";
alert(a);
}
</script>
代码二示例如下:
<script type="text/javascript">
var a = "Hello";
function test(){
alert(a);
a = "World";
alert(a);
}
</script>
起初我认为这仅是基础的全局与局部变量作用域问题,便解释道:当局部变量与全局变量同名时,局部变量的作用域会覆盖全局变量;一旦退出局部作用域,便恢复访问全局变量。因此,两段代码的运行结果分别为:1) undefined World 2) Hello World。为了进一步验证,我构造了一个测试案例。
<script>
var a =1;
function test(){
alert(a);
var a = 2;
alert(a);
}
test();
alert(a);
</script>
大家猜测输出结果是否为 1 2 1?在发送测试用例前,我也持相同观点,但实际运行结果却是 undefined 2 1。这一反直觉的结果让我困惑,经查阅资料并深入测试后,总结如下核心要点:
第一,JavaScript的作用域是基于函数块划分的,即由 function 的一对大括号 { } 界定。需要注意的是,for、while、if 等语句块并不构成独立的作用域。以下示例可佐证:
<script>
function test2(){
alert ("before for scope:"+i); // i未赋值(并不是未声明!使用未声明的变量或函数全抛出致命错误而中断脚本执行)
// 此时i的值是underfined
for(var i=0;i<3;i++){
alert("in for scope:"+i); // i的值是 0、1、2, 当i为3时跳出循环
}
alert("after for scope:"+i); // i的值是3,注意,此时已经在for scope以外,但i的值仍然保留为3
while(true){
var j = 1;
break;
}
alert(j); // j的值是1,注意,此时已经在while scope以外,但j的值仍然保留为1
if(true){
var k = 1;
}
alert(k); //k的值是1,注意,此时已经在if scope以外,但k的值仍然保留为1
}
test2();
//若在此时(function scope之外)再输出只存在于test2 这个function scope里的 i、j、k变量会发生神马效果呢?
alert(i); //error! 没错,是error,原因是变量i未声明(并不是未赋值,区分test2函数的第一行输出),导致脚本错误,程序到此结束!
alert("这行打印还会输出吗?"); //未执行
alert(j); //未执行
alert(k); //未执行
</script>
第二,JavaScript在代码执行前会进行预编译,对脚本文件中所有声明(包括局部变量)进行完整分析,从而确定变量的作用域。具体理解可参考以下示例:
<script>
var a =1;
function test(){
alert(a); //a为undefined! 这个a并不是全局变量,这是因为在function scope里已经声明了(函数体倒数第4行)一个重名的局部变量,
//所以全局变量a被覆盖了,这说明了Javascript在执行前会对整个脚本文件的定义部分做完整分析,所以在函数test()执行前,
//函数体中的变量a就被指向内部的局部变量.而不是指向外部的全局变量. 但这时a只有声明,还没赋值,所以输出undefined。
a=4
alert(a); //a为4,没悬念了吧? 这里的a还是局部变量哦!
var a; //局部变量a在这行声明
alert(a); //a还是为4,这是因为之前已把4赋给a了
}
test();
alert(a); //a为1,这里并不在function scope内,a的值为全局变量的值
</script>
第三,当全局变量与局部变量重名时,局部变量会屏蔽全局变量;退出局部作用域后,重新访问全局变量。若需在局部作用域中强制访问全局变量,可通过 window.globalVariableName 形式获取。
<script>
var a =1;
function test(){
alert(window.a); //a为1,这里的a是全局变量哦!
var a=2; //局部变量a在这行定义
alert(a); //a为2,这里的a是局部变量哦!
}
test();
alert(a); //a为1,这里并不在function scope内,a的值为全局变量的值
</script>
更多相关技术文章可参考文末推荐链接。