JavaScript:全面解析各种浏览器网页中的JS代码的执行顺序

src="http://p.alimama.com/code.php?t=2&i=mm_10003521_106784_109301&w=760&h=90&sz=11&bgc=FFFACD&bdc=FFFFFF&tc=000080&lc=000000&dc=000000" name="alimamaifrm" id="alimamaifrm" border="0" marginwidth="0" marginheight="0" scrolling="no" width="760" frameborder="0" height="90">
        近来我通过一些测试以全面的解析网页在各种浏览器中的JavaScript代码的执行顺序,在这儿做个记录。
        我们知道javaScript是一种解释型语言,他的执行是自上而下,但是各个浏览器对于至上而下的理解是有细微差别的,而代码的上下游也就是程序流又对于程序正确至关重要,所以我觉得有必要深入理解多个js块儿的执行顺序。

首先得知道有多少方法能把javaScript加入到页面中呢?常见下述的前2种,其实还有更多。
1.页面中直接引入外部js文件:<script src="my.js"></script>

2.页面中直接写如js片段<script>alert(1)</script>

3.在js中引入js文件document.write("<scr"+"ipt src='my.js'></scr"+"ipt>");
    注意:这时候"..</script>"必须拆成"</scr"+"ipt>",否则浏览器可能会把父js片段关闭掉,出错;
   
4.同样在js中引用其他js片段,document.write("<scr"+"ipt>alert(1)</scr"+"ipt>");
    你可能觉得这个并没有必要,既然已经在script中了还套一层干嘛?呵呵,怎么说也是一种写法,而且它具有其特殊的行为,稍后我们讨论到。

5.使用Ajax中的xmlHttpRequest结合eval()来引入js,我最早在Dojo的代码见到,写的详细些:
    var ajaxRequest = getXmlHttpRequest()//省去各个浏览器得到xmlHttpRequest的部门
    ajaxRequest.open("GET","my.js",false);//使用xmlHttpRequest对象Get方法的同步调用
    ajaxRequest.send(null);
    sJsFragment = ajax.responseText;//得到字符串为js片段
    eval(sJsFragment);//执行js片段
    注意:这里要求my.js即后来的sJsFragment内容得是非常规范的js,切没有//开头的注释,怎样检查js是否规范呢?去http://jslint.com/
   
6.无所不能的Dom方法,这个我最早在Yahoo的前端代码中见到,非常厉害,也写的详细些:
    var oScript = document.createElement("script");//创建一个Script元素
    oScript.src = "my.js";//制定src属性
    document.getElementsByTagName("head")[0].appendChild(oScript);
    说明:my.js的内容会在oScript加入到文档中之后获得并执行。仔细看下这段容易发现这个调用是异步的,可以在文档载入之后通过事件触发,我用它变通了一下,作为了xmlHttpRequest的Get方法在跨域取数时的替代,获得了很完美的效果,以后有机会专门写篇文。
   
六种不少吧,可能还会有吧,而且这几种之间还可能相互嵌套,变化无常。
其中1、2、4、6种方式引入的javaScript的执行顺序是非常自然的,随着页面的载入以及后续的事件触发,它们遵守先来后到、而其内部自上而下。

我们主要关注的是第3、4种引入js方法带来的问题
测试准备:
编写文件1.js内容只有一行:
    alert("1.1");
编写文件3.js内容只有一行:
    alert("3.1");
再编写test.html
    <html>
        <head>       
            <script>//按第2种方式引入的脚本
                document.write("<scr"+"ipt>alert(4.1);</scr"+"ipt>");//按第4种方式引入的第一个脚本
                alert(2.1);
                document.write("<scr"+"ipt>alert(4.2);</scr"+"ipt>");//按第4种方式引入的第二个脚本
                document.write("<scr"+"ipt src='3.js'></scr"+"ipt>");//按第3种方式引入的脚本
                document.write("<scr"+"ipt>alert(4.3);</scr"+"ipt>");//按第4种方式引入的第三个脚本
                alert(2.2);
            </script>
            <script src="1.js">//按第1种方式引入的脚本</script>
        </head>
        <body>
        </body>
    </html>

执行结果:
IE浏览器的alert顺序为:       4.1 - 2.1 - 4.2 - 4.3 - 2.2 - 3.1 - 1.1
FireFox浏览器的alert顺序为:  4.1 - 2.1 - 4.2 - 2.2 - 3.1 - 4.3 - 1.1
Opera浏览器的alert顺序为:    4.1 - 2.1 - 4.2 - 3.1 - 4.3 - 2.2 - 1.1

分析:
IE:把第3种方式引入的js文件放在当前脚本单元之后接着执行,把第4种方式引入的js当作宿主脚本单元的一部分,在内部其引入的位置执行。
Opera:把第3种和第4种方式引入的js都当作宿主脚本单元的一部分,在内部其引入的位置执行。
FireFox:第3种方式引入的js文件放在当前脚本单元之后接着执行;在宿主引入第3种脚本之前,把第4种方式引入的js当作宿主脚本单元的一部分,在内部其引入的位置执行;在宿主引入第3种脚本之后,把第4种方式引入的js当作单独的一个脚本块儿,在宿主脚本执行之后接着执行。FirefFox的这种做法是很不友好。

结论:
浏览器差别巨大,所以不要指望通过第3、4种方式引入的脚本在引入位置或者宿主脚本执行后执行,如果你要至少支持FireFox浏览器,那么通过这种逻辑控制程序流会有大麻烦,应该避免。

实例:
以发布Alimama的广告代码为例(google adsense、yahoo定向推广也一样)。
多数站CMS系统管理广告代码,常常需要js的方式来做代码调用。这样如果想通过一个js发布多块儿广告位就会出现麻烦,比如在一个js中:
第一种写法:
/发布第一块儿广告
var alimama_pid=xxxx_1;
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
//发布第二块儿广告
var alimama_pid=xxxx_2;
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
希望引入的js块和js文件按书写顺序执行,根据上述测试的结论,针对这个任务,IE和FireFox是得不到想要的结果的,Opera可以。

第二种写法:
//发布第一块儿广告
document.write("<scr"+"ipt>var alimama_pid=xxxx_1</scr"+"ipt>");
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
//发布第二块儿广告
document.write("<scr"+"ipt>var alimama_pid=xxxx_2</scr"+"ipt>");
document.write("<scr"+"ipt src='http://p.alimama.com/inf.js'></scr"+"ipt>");
仍然根据上述测试的结论,IE是得不到想要的结果的,Opera干得很漂亮,而且蹩脚的FireFox居然也可以。

再说一个关于setTimeout()控制程序流的问题
下面一段脚本在页面加载时执行
function fn(){
    alert("a");
}
alert(1);
window.setTimeout(fn,3000);
alert(2);

各种浏览器alert的顺序都是1 - 2 - a。
也就是说setTimeout()方法不会让程序流停止,等候指定时间后再执行其下的语句,而只是告诉浏览器要在指定时间后执行参数中的方法。所以在程序载入过程中,不要指望setTimeout来造成程序流等待。

最后再补充一下 网站页面js请慎用onload事件
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值