JavaScript解析器原理
当在HTML中遇到标签包含的内容时,则需要调用浏览器的解析器。解析器的工作原理为:
(1)预解析
1.var的部分:
在当前作用域下,在JS代码执行之前,解析器会在整个页面中(从头至尾)找到var、function和参数的内容,进行声明提升。
- var的部分:解析器会将变量提前到其作用域的顶部,但是会为其存放一个初始值为undefined,然后将这个变量存放到“仓库”中,这些变量的赋值情况,由后面代码运行过程来决定[这里的var也包含了函数(function)中var部分];
- function部分,对于由函数声明方式定义的函数,函数声明“整个”被提前;对于由函数表达式定义的函数,只会将变量提升,函数赋值依然在原来的位置。
2关于重名的处理:
- i.如果变量和函数重名了,提升到顶部的会是函数,会忽略后面的变量声明,因为函数声明提升的优先级高于变量声明提升的优先级;
- ii.如果函数重名了,在函数声明提升中,根据上下文顺序,那么留下最后一个。
(2)
经过(1)的处理后,解析器会逐行运行代码,解析一行,执行一行。
在第(2)步中,逐步执行代码,对之前提升的变量进行赋值,那么对于同一变量名,后面的操作会覆盖前面的操作,因为JavaScript是松散类型的,所以变量的类型也是可以一直改变的。
实例一:
<!DOCTYPE html>
<html>
<head>
<meta charset = "UTF-8">
<title>Document</title>
</head>
<body>
<script>
var num = 10;
fun();
function fun(){
document.write(num);
var num = 20;
}
</script>
</body>
</html>
处理的过程为:
</script>
<script>
var num;
function fun(){
var num;
document.write(num);
num = 20;
}
num = 10;
fun();
</script>
结果为:undefined
实例二:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>jiexiqiceshi</title>
</head>
<body>
<script>
var a = 18;
f1();
function f1(){
var b = 9;
document.write(a);
document.write("<br>");
document.write(b);
var a = "123";
}
</script>
</body>
</html>
处理过程:
<script>
var a;
function f1(){
var b;
var a;
b = 9;
document.write(a);
document.wtite("<br>");
document.write(b);
a = "123";
}
a = 18;
f1();
</script>
结果为:undefined ;9
实例三:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>jiexiqi03</title>
<script>
f1();
document.write("original c:"+c+"<br>");
document.write("original a:"+a+"<br>");
document.write("original b:"+b+"<br>");
function f1(){
var a = b = c = 9;
document.write("a:"+a+"<br>");
document.write("b:"+b+"<br>");
document.write("c:"+c+"<br>");
}
</script>
</head>
<body>
</body>
</html>
解析器的过程:
<script>
var b;
var c;
function f1(){
var a ;
a = 9;
b = 9;
c = 9;
document.write("a:"+a+"<br>");
document.write("b:"+b+"<br>");
document.write("c:"+c+"<br>");
}
f1();
document.write("original c:"+c+"<br>");
document.write("original a:"+a+"<br>");
document.write("original b:"+b+"<br>");
</script>
结果为:a:9;b:9;c:9;original c:9
**注意:**var a = b = c = 9这行代码中,只有一个var,也就意味着,这个var是“修饰”a这个变量的,那么a就是一个局部变量,而b和c变量就会成为全局变量,所以在我们分析解析器的解析过程中,b和c变量是当做全局变量来处理的。而最后的打印结果中只有“original c:9”,说明了当运行到
document.write("original a:"+a+"<br>")
这一行时,由于超出了a的作用域,所以a是未定义的量,解析器是不认识的,所以出现错误,之后的代码也不会被执行了。
如果将代码修改为这样的:
<script>
function f1(){
var a = 9 ;
var b = 9;
var c = 9;
document.write("a:"+a+"<br>");
document.write("b:"+b+"<br>");
document.write("c:"+c+"<br>");
}
f1();
document.write("original c:"+c+"<br>");
document.write("original a:"+a+"<br>");
document.write("original b:"+b+"<br>");
</script>
那么结果为:a:9;b:9;c:9
实例四
关于函数和变量重名,声明提升的优先级问题
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>重名分析</title>
<script>
document.write(typeof two + "<br>");
var two = 4;
two();
function two(){
document.write("two函数");
}
</script>
</head>
<body>
</body>
</html>
结果
在上面的截图中,我们看到在声明提升后,函数声明提升的优先级高于变量提升的优先级,所以typeof操作符的结果为function,而在右侧错误提示说:two不是一个函数,是因为var two = 4这一行代码为two变量进行了赋值,two成为了Number类型的了,所以会报错。