答
因为编辑器会在判断有已经声明的同名变量时忽略var,然后直接赋值
原理
在JS代码运行过程中:
- 引擎负责整个代码的编译以及运行
- 编译器则负责词法分析、语法分析、代码生成等工作
- 作用域负责维护所有的标识符(变量)。
var a = 2;
var a = 3;
a = 4;
alert(a); // 4
当我们执行上面的代码时,我们可以简单的理解为新变量分配一块儿内存,命名为a,并赋值为2,但在运行的时候编译器与引擎还会进行两项额外的操作:判断变量是否已经声明
- 重复声明时:首先编译器对代码进行分析拆解,从左至右遇见
var a
,则编译器会询问作用域是否已经存在叫a的变量了。如果不存在,则招呼作用域声明一个新的变量a;若已经存在,则忽略var
继续向下编译,这时a = 2
被编译成可执行的代码供引擎使用。 - 赋值时:引擎遇见
a=2
时同样会询问在当前的作用域下是否有变量a。若存在,则将a赋值为2(由于第一步编译器忽略了重复声明的var
,且作用域中已经有a,所以重复声明会发生值的覆盖而不会报错);若不存在,则顺着作用域链向上查找,若最终找到了变量a则将其赋值2,若没有找到,则招呼作用域声明一个变量a并赋值为2(这就是为什么第二段代码可以正确执行且a变量为全局变量的原因,当然,在严格模式下JS会直接抛出异常:a is not defined)。