script标记的加载方式(多浏览器测试)

< DOCTYPE html PUBLIC -WCDTD XHTML StrictEN httpwwwworgTRxhtmlDTDxhtml-strictdtd>

Script Call, 与XMLHttpRequest、IFrame Call,是Ajax开发中最常见的三种数据传输器。在传输方式上,XMLHttpRequest可以通过参数设置使用异步还是同步; IFrame Call 一定是异步的,那Script Call是怎样呢?在不同的浏览器上是否遵循一样的规则呢?

本文设计一个简单的测试用例,看看静态<script>载入与动态<script>载入的区别,以及两者在主流浏览器(FireFox1.5|Opera8.5|IE6)上的表现异同。

测试如下:
//test.js:
function printV(msg) {
    document.write("<br>" + msg + " : v = ");
    try {
        document.write(v);
    } catch (e) {
        document.write("null");
    }
}

<!-- test1.html-->
<script type="text/javascript" src="test.js"></script>
<script>
printV("after static script loaded");
</script>
test1.html在三个浏览器中的表现一模一样,输入都是:

after static script loaded : v = 100
所以可以推知,静态<script>加载脚本在浏览器解析的过程中是同步的。
document.write("<script>"),用document.write的形式写入一个<script>标签是一种常用的动态载入脚本的方法,很多JavaScript类库都使用这样的方式来模拟php或者python的 "require" 或者 "import" 语句,达到动态并且可控制加载脚本的目的。

首先,测试 document.write语句与printV函数调用在同一个<script>块的情况:

<!-- test2.html-->
<script>
document.write('<script type="text/javascript" src=""></script>');
printV("after documentWriteScript");
</script>
各个浏览器对test2.html的执行结果有了区别: FireFox与IE6都是马上打印出v=null,结果如下:

after documentWriteScript loaded : v = null
只有Opera能够在一会延迟后打印出v的值。接下来稍微修改一下代码,将document.write语句和printV函数调用放在两个不同的<script>块中:
<!-- test3.html-->
<script>
document.write('<script type="text/javascript" src="></script>');
</script>
<script>
printV("after documentWriteScript");
</script>
结果让我略有些吃惊,test3.html在各个浏览器中的表现再度相同,与静态<script>载入效果一样。那么可以推断出浏览器解析脚本的一些处理逻辑:

FireFox1.5 和 IE6 解析同一个<script>块中代码的时候,如果发现动态<script>载入,那么是异步执行的。但在解析下一个<script>块(或者其他html标签)之前,会保证当前<script>块执行完成,也就是说,FireFox1.5和IE6逐个解析<script>标签的过程是同步的。
Opera8.5 则是保证每句脚本都解析并执行完成,再执行下一语句。即使是动态载入脚本,也要加载完成后才继续执行。
head.appendChild([script node])

document.write 在整个DOM已经被渲染完成以后就不适用了,比如在window.onload事件方法中执行document.write的话,将覆写掉已有的内容。除了document.write写入的方式,还有一种常见的动态载入脚本的方法,那就是利用DOM为<head>(或<body>)创建一个script element。测试代码如下:

<script>
<!--test4.html-->
var script = document.createElement("script");
script.src = "test.jsp";
document.getElementsByTagName_r("head")[0].appendChild(script);
printV("after documentAppendScript");
</script>
同样还有不同<script>块的版本
<script>
<!--test5.html-->
var script = document.createElement("script");
script.src = "test.jsp";
document.getElementsByTagName_r("head")[0].appendChild(script);
</script>
<script>
printV("after documentAppendScript");
</script>
可预测的,Firefox1.5和IE6在test4 vs. test2 和test5 vs.test3中结果对应相同。Opera8.5则一直都能有效打印出v的值。

总结

通过对三个浏览器的简单测试,可以发现,FireFox1.5与IE6在不同测试中表现一致,Opera8.5则在不同测试中自身表现始终一致。

从整个html中看,每个<script>是同步的
所以我们常常看到,一些类库使用一个简单的js脚本,作为类库加载器,例如ActiveWidget的grid.js脚本源码:
function $import(path){
    var i, base, src = "grid.js", scripts = document.getElementsByTagName_r("script");
    for (i=0; i<scripts.length; i++){if (scripts[i].src.match(src)){ base = scripts[i].src.replace(src, "");break;}}
document.write("<" + "script src="" + base + path + ""></" + "script>");
}
原理就是首先获取自己的src,解析出url路径,然后调用document.write加载相同url下的指定类库。因为加载器和其后的程序调用一定是在不同的<script>块,所以不会出现对象未定义的脚本错误。
在关联的JavaScript代码中动态<script>载入是异步的(Opera为同步)
利用动态<script>载入脚本可以避免XMLHttpRequest的跨域限制,所以常常用来和JSON配套使用,JSONP 新名词也由此而生。JSONP(JSON with Padding)的原理简单说就是:将回调的JS语句或函数名作为script url中的参数提交,服务端返回的脚本数据的最后,则执行参数传递来的脚本。
Yahoo! Web Services提供JSON apiUsing JSON with Yahoo! Web Services,以及del.icio.us|利用<script>加载用户书签的例子 Javascript Objects 都是JSONP形式调用的实现。因为JSONP需要客户端与服务端的配合,所以带来避免跨域限制的长处同时,灵活性不如XMLHttpRequest。

另外,FireFox1.5为<script>提供了onload 注册事件,IE6提供了onreadystatechange注册事件。Opera8.5 则两者都不支持,但由于Opera8.5对<script>永远是同步执行方式,所以也没有支持的必要。

虽然浏览器间没有达成完全统一,但对于 JSONP 形式的处理,浏览器的表现是一致的。<script>标签与JavaScript的关系,加上Script Call比IFrame Call更利于直接获取数据,可以预见,Ajax应用中Script Call 会有更广泛应用以作为XmlHttpRequest的补充。

原文地址:https://www.cnblogs.com/netcorner/p/2912230.html