5. body的onload函数与body内部函数的执行
body内部的函数会先于onload的函数执行,测试代码:
01 //html head...
02 <script type="text/javascript">
03 function fnOnLoad(){
04 alert("I am outside the Wall!");
05 }
06 </script>
07 <body onload="fnOnLoad();">
08 <script type="text/javascript">
09 alert("I am inside the Wall..");
10 </script>
11 </body>
12 //先弹出“I am inside the Wall..”;
13 //后弹出“I am outside the Wall!”
body的onload事件触发条件是body内容加载完成,而body中的js代码会在这一事件触发之前运行(为什么呢?6告诉你..)
6. JavaScript是多线程or单线程?
严格来说,JavaScript是没有多线程概念的,所有的程序都是“单线程”依次执行的。
举个不太恰当的例子:
01 function fn1(){
02 var sum = 0;
03 for(var ind=0; ind<1000; ind++) {
04 sum += ind;
05 }
06 alert("答案是"+sum);
07 }
08 function fn2(){
09 alert("早知道了,我就是不说");
10 }
11 fn1();
12 fn2();
13 //先弹出:“答案是499500”,
14 //后弹出:“早知道了,我就是不说”
那你肯定要问:那延时执行、Ajax异步加载,不是多线程的吗?没错,下面这样的程序确实看起来像“多线程”:
01 function fn1(){
02 setTimeout(function(){
03 alert("我先调用")
04 },1000);
05 }
06 function fn2(){
07 alert("我后调用");
08 }
09 fn1();
10 fn2();
11 // 先弹出:“我后调用”,
12 // 1秒后弹出:“我先调用”
看上去,fn2()和延时程序是分两个过程再走,但其实,这是JavaScript中的“回调”机制在起作用,类似于操作系统中的“中断和响应” —— 延时程序设置一个“中断”,然后执行fn2(),待1000毫秒时间到后,再回调执行fn1()。
同样,5中body的onload事件调用的函数,也是利用了回调机制——body加载完成之后,回调执行fnOnLoad()函数。
Ajax请求中的数据处理函数也是一样的道理。
关于JavaScript线程问题的更深入讨论,看这篇 javascript中的线程之我见,以及infoQ上的 JavaScript多线程编程简介。
困了,再说一下回调函数吧。
7. 回调函数
回调函数是干嘛用的?就是回调执行的函数嘛,又废话:D
如6所说,最常见的回调就是onclick、onmouseover、onmousedown、onload等等浏览器事件的调用函数;还有Ajax异步请求数据的处理函数;setTimeOut延时执行、setInterval循环执行的函数等。
干脆我们写一个纯粹的回调函数玩:
01 function onBack(num){
02 alert("姗姗我来迟了");
03 // 执行num个耳光
04 }
05 function dating(hours, callBack){
06 var SP= 0; // SP,愤怒值
07 //女猪脚在雪里站了hours个钟头
08 //循环开始..
09 SP ++;
10 //循环结束...
11 callBack(SP);
12 }
13 dating(1, onBack);
dating运行完之后再执行回调函数onBack —— 约会结束了,暴风骤雨开始了。
=========================================================================================
虽然变量和函数声明可以在文档任意位置,但是良好的习惯应该是在所有JavaScript代码之前声明全局变量和函数,并对变量进行初始化赋值。在函数内部也是先声明变量,然后再引用。
按块执行JavaScript代码
所谓代码块就是使用<script>标签分隔的代码段。例如,下面两个<script>标签分别代表两个JavaScript代码块。
<script>
//JavaScript代码块1
vara=1;
</script>
<script>
//JavaScript代码块2
functionf(){
alert(1);
}
</script>
JavaScript解释器在执行脚本时,是按块来执行的。通俗地说,就是浏览器在解析HTML文档流时,如果遇到一个<script>标签,则JavaScript解释器会等到这个代码块都加载完后,先对代码块进行预编译,然后再执行。执行完毕后,浏览器会继续解析下面的HTML文档流,同时JavaScript解释器也准备好处理下一个代码块。
由于JavaScript是按块执行的,所以如果在一个JavaScript块中调用后面块中声明的变量或函数就会提示语法错误。例如,当JavaScript解释器执行下面代码时就会提示语法错误,显示变量a未定义,对象f找不到。
<script>
//JavaScript代码块1
alert(a);
f();
</script>
<script>
//JavaScript代码块2
vara=1;
functionf(){
alert(1);
}
</script>
虽然说,JavaScript是按块执行的,但是不同块都属于同一个全局作用域,也就是说,块之间的变量和函数是可以共享的。
1.6.4借助事件机制改变JavaScript执行顺序
由于JavaScript是按块处理代码,同时又遵循HTML文档流的解析顺序,所以在上面示例中会看到这样的语法错误。但是当文档流加载完毕,如果再次访问就不会出现这样的错误。例如,把访问第2块代码中的变量和函数的代码放在页面初始化事件函数中,就不会出现语法错误了。
<script>
//JavaScript代码块1
window.onload=function(){//页面初始化事件处理函数
alert(a);
f();
}
</script>
<script>
//JavaScript代码块2
vara=1;
functionf(){
alert(1);
}
</script>
为了安全起见,我们一般在页面初始化完毕之后才允许JavaScript代码执行,这样可以避免网速对JavaScript执行的影响,同时也避开了HTML文档流对于JavaScript执行的限制。
注意
如果在一个页面中存在多个windows.onload事件处理函数,则只有最后一个才是有效的,为了解决这个问题,可以把所有脚本或调用函数都放在同一个onload事件处理函数中,例如:
window.onload=function(){
f1();
f2();
f3();
}
而且通过这种方式可以改变函数的执行顺序,方法是:简单地调整onload事件处理函数中调用函数的排列顺序。
除了页面初始化事件外,我们还可以通过各种交互事件来改变JavaScript代码的执行顺序,如鼠标事件、键盘事件及时钟触发器等方法,详细讲解请参阅第14章的内容。