先上几道题:
1、
<script>
alert(a);
</script>
<script>
alert(a);
var a = 1;
</script>
2、
<script>
if("a" in window){
var a = 1;
}
alert(a);
</script>
3、
<script>
function t(g){
alert(g);
function g(){
alert(1);
}
}
t(2);
</script>
4、
<script>
function t(t){
alert(t);
t();
}
t(function(){
alert(t);
});
</script>
5、在不同浏览器上看看效果
<script>
var t = function a(){
alert(a);
}
t();
alert(a);
</script>
谁能做到只分析代码就得出正确结论,并且说明原理, 那么恭喜你,说明你深刻掌握了词法分析的原理。
这五道题的结果可能让很多人感觉到意外,JS虽说是脚本语言, 但是它在执行之前会先做预解析, 也就是词法分析。
词法分析分成三个步骤:
1、检查var声明,
当一段程序或函数在运行前,会为当前作用域创建一个活动对象AO, 检查所有用var声明的变量, 将此变量挂到该AO上,但并不赋值(变量的赋值是发生在代码真正运行时);
2. 函数参数传递,
若这段程序是个函数,JS引擎会将传递的参数赋值给该函数的形参。
3、函数声明
检查当前域中的函数声明,将声明的函数挂到AO上。这样就可以形成一个AO链, 函数声明的形式为:function aa(){} ,
现在我们再来分析这五道题 。
1, 第一道题中第1小题报错。因为使用了一个未声明的变量。第2小题为undefined,在词法分析中,当前活动对象已有变量a, 只是没有赋值。 所以为undefined.
2、 第二题是想判断a是否为window的属性, 如果是,则a=1, 可是JS引擎会先检查var声明,将a挂到AO上, 然后再运行。 导致代码在运行时, a已经声明, a=1.
3、 第三题当函数t在运行前, 词法分析先检查有没有var声明,没有, 再到参数传递, 将g挂到t的活动对象AO上,并将其赋值为2, 再去检查是否有函数声明, 正好有一个函数名为g的函数声明,先判断AO上是否有g这个变量,如果没有,则创建,没有则覆盖,那么g变成了一个函数。 值为function g(){ alert(1) };
4、第四题有点绕,需要我们了解作用域的概念, 让我们来一起分析下。 当代码在运行前, 首先在window上创建一个AO。将函数t挂到window的AO上。当代码调用函数t时,需要对函数t进行词法分析了,将传过来的参数function(){alert(t)} 传递给参数t, 那么alert(t)得到的结果自然是function(){alert(t)} ,可t()为何却得到
function t(t){
alert(t);
t();
}
匿名函数function () { alert(t); } 是在哪里定义的, 是在最外层的window上。 此匿名函数用到了变量t, 在该匿名函数中找不到变量t,那么就会往它的上一级去找。它的上一级是谁, 是window而不是运行时的t, window上正好有个变量为t.
如下图:
这个例子也正说明: 变量的值取决于声明时的环境而不是运行时的环境,这是与其它语言最大的不同。
如还不理解,可以再看看这题:
<script>
var a = 1;
function t(g){
var a = 2;
g();
}
t(function(){
alert(a);
})
</script>
5、第五题,函数表达式