JavaScript中的解析机制详解
JS中的解析机制若是没有理解透彻,还是会碰到许多坑的,比如如下代码:
var a = 1;
function doSth () {
alert(a);
var a = 2;
}
doSth();
这段代码会弹出的警告框是1还是2呢?实际是undefined!下面我们就来看看JS中的解析过程到底是怎么样的。
一、JS中代码的解读过程
JS中代码的解读过程分为两步,首先是预解析,然后是对代码的逐行解读。在预解析过程中,会依次把全局作用域(执行环境)和局部作用域中用var声明的变量(或函数表达式)和函数参数赋值为undefined,而用function声明的函数则就是函数本身。第二部就是按执行顺序,逐行的对代码进行解读。
我们来分析一下上面提到的例子,首先是预解析,全局作用域中的a会变为undefined,函数doSth就是函数本身;局部作用域中,局部变量a同样也是undefined。
预解析完成之后就是按执行顺序对代码的逐行解读,首先是第一行,将全局变量a赋值为1;然后是最后一行对函数doSth的调用,这时候就进入函数的局部作用域中,第一样代码弹出a的值,这就根据标识符的解析规则,首先搜索作用域链的最前端,就是当前执行环境的变量对象,发现正好有一个变量a,其在预解析的过程中被赋值为undefined,所以就会弹出undefined,再下一步才是把局部变量a赋值为2。如果再函数最后弹出a的值,就会是2;如下:
var a = 1;
function doSth () {
alert(a); //undefined
var a = 2;
alert(a); // 2
}
doSth();
再来看一个例子:
var a = 1;
function doSth (a) {
alert(a); //undefined
a = 2;
}
doSth();
这里如果对作用域链、变量对象以及标识符的解析规则都还不是很了解的小伙伴可以看看我的上一篇文章。
二、JS代码解析中常见的问题
1、如果函数名与变量名冲突,那么预解析过程中会舍弃掉变量,保留函数。函数名与函数名冲突,预解析过程中会留下后面定义的函数。另外还要注意的是,不推荐在如if(){},for(){}这样的代码块里定义函数,因为有些老版本浏览器无法正确预解析这些代码块里的函数,会造成错误。看一个例子:
console.log(a); //function a (){console.log(4)}
var a = 1;
console.log(a);//1
function a () {
console.log(2);
}
console.log(a);//1
var a =3;
console.log(a);//3
function a () {
console.log(4)
}
console.log(a); //3
a(); // 报错
2、没有用var声明的变量不会被预解析,所以推荐声明变量的时候都要用var。
3、预解析机制是分标签进行的,也就是说在两对<script></script>标签中,预解析是各自独立运行的,如下:
<script>
console.log(a) //报错
</script>
<script>
var a = 1;
</script>
如果这是在一个标签内,就会返回undefined。
三、总结
其实这解析机制也挺简单的,就两步,预解析:把var声明的变量和函数参数参数变为undefined,函数就是本身;代码依次解读。只要注意下预解析时函数名与变量名冲突,函数名本身冲突的问题就OK了。