在了解JavaScript具体是怎样解析之前,我们先来了解几个重要的概念:
1.代码块
JavaScript中的代码块是指由
<script type="text/javascript">
alert("这是代码块一");
</script>
<script type="text/javascript">
alert("这是代码块二");
</script>
JS是按照代码块来进行编译和执行的,代码块间相互独立,但变量和方法共享。
浏览器在解析html文档流的时候,如果遇到一个<script>
标签,则JS会等到这个代码块加载完之后再对代码进行预编译,然后再执行。执行完毕后,浏览器会继续解析html文档流,同时js也准备好处理下一个代码块。由于JS是按块执行的,因此在一个JS块中调用后面声明的变量或者函数就会提示语法错误。但是不同块都属于一个全局作用域,也就是说,块之间的变量和函数可以共享。
举例:
<title>JS执行顺序</title>
<script type="text/javascript">
alert("这是代码块1");//先弹出"这是代码块1"
alert(str);//因为str没有定义,所以浏览器会报错,后面代码不能运行
alert("haha");
var test = "我是代码块一中的变量";
</script>
<script type="text/javascript">
alert("我是代码块二");//弹出"我是代码块二"
alert(test);//弹出"我是代码块一中的变量"
</script>
代码块一运行报错,但是不影响代码块二的执行,这就是代码块间的独立性,而代码块二能调用到代码块一中的=变量,即为块间的共享性。
2.函数声明与函数赋值
JS函数定义分为两种:韩顺声明和函数赋值式。
<script type="text/javascript">
function Fn(){
//函数声明
}
var Fn = function(){
//赋值式函数
}
</script>
这两者在JS的与编译器的区别会体现出来。下面就来说一说预编译。
3.预编译器与执行期
JS的解析分为两个阶段:预编译期(预处理期)与执行期
预编译期JS会对本代码中所有声明的变量和函数进行处理(类似于c语言的编译),但是处理函数只是处理声明式函数,将其提前,而变量也只是进行了声明但是并未进行初始胡以及赋值。
举例:
<script type="text/javascript">
Fn();//"执行了声明式函数",在预编译期函数被处理了
function Fn(){//声明式函数
alert(""执行了声明式函数);
}
var Fn = function(){//赋值式函数
alert("执行了赋值式函数");
}
</script>
了解了以上几个概念,下面来看一个例子:
<script type="text/javascript">
Fn();//Fn is not defined
</script>
<script type="text/javascript">
function Fn(){
alert("执行了函数1");
}
</script>
代码块一在执行函数Fn时报错,这是为什么?
原因在于JS引擎是按照代码块来顺序执行的,完整的说是按照代码块来进行预处理和执行的,也就是预处理的只是执行到代码块的声明函数和变量,对于还未加载的代码块,没有办法进行预处理。
JS是单线程的
举例:
function foo(){
console.log("first");
setTimeout((function(){console.log('second');}),5);
}
for(var i = 0;i<10000000;i++){
foo();
}
执行结果会首先会全部输出first,然后全部输出second.
JS运行在浏览器中是单线程的,每一个window的一个JS线程,既然是单线程的,在某个特定的时刻,只有特定的代码能够执行,并且阻塞其他代码的执行。而浏览器是事件驱动的,浏览器中很多行为是异步的,会创建事件放在执行队列中。Javascript引擎是单线程处理它的任务队列,所以当多个事件触发时,会依次放入队列,然后一个个响应(所以上面的代码是5ms后把输出second的任务加入队列中),而当前有任务,所以只能等到1000000个first输出完成后才会输出second。
浏览器是多线程的
虽然JS运行在浏览器中,是单线程的,但是浏览器不是单线程的。浏览器中的很多异步行为都是由浏览器新开一个线程去完成的。Javascript引擎线程是浏览器多个线程中的一个,它本身是单线程的。浏览器还包括很多其他线程,如界面渲染线程,浏览器事件触发线程,Http请求线程等。
所谓的javascript是单线程的,是指javascript运行在浏览器中是单线程的,叫做javascript引擎线程。
下面这个例子会让我们差生怀疑,产生一种JS是多线程的错觉:
function fn1(){
setTimeout(function(){
alert("我先调用")
},1000);
}
function fn2(){
alert("我后调用");
}
fn1();
fn2();
//先弹出:"我后调用"
//1s后弹出:"我先调用"
看上去,fn2()和延时程序是分两个程序走,但其实,这是JS的“回调”机制在起作用,类似于操作系统中的“中断和响应”——延时程序设置一个“中断”,然后执行fn2(),等待1000ms之后,再回调执行函数fn1()。
下面这个例子也是JS回调函数的实例:
<script type="text/javascript">
function fnOnLoad(){
alert("I am outside the Wall!");
}
</script>
<body onload="fnOnLoad();">
<script type="text/javascript">
alert("I am inside the Wall..");
</script>
</body>
//先弹出“I am inside the Wall..”;
//后弹出“I am outside the Wall!”
body的onload事件调用的函数,也是利用了回调机制——body加载完成之后,回调执行fnOnLoad()函数