2010-8-8 update:在IE8下的eval是不支持下面两种的用法的,很杯具,都会提示“对象不支持此属性或方法”的错误。
[javascript]
//第一:
eval("onmessage=function(str){alert(str);}");
onmessage("shllo");
//第二:
var s = document.createElement("script"),h=document.getElementsByTagName("head")[0];
s.text="onmessage=function(str){alert(str);}";
h.appendChild(s);
onmessage("hello");
[/javascript]
说到这个,还发现一点很容易造成失误的地方:在浏览器解析过的script中的代码,浏览器不会重新对该script标签内的行内脚本、外联脚本执行,就算是重新给script标签定义行内脚本或者修改它的src来链接到其他的javascript脚本,浏览器都不会重新解析。
——————- 提IE8 eval的兼容性分界线 ——————–
从上一篇文章《Web Worker浅析》中,我们了解到Worker的思维是跟Ajax类似的,包括它也不支持跨域调用javascript文件,这说明底层的数据交互还是跟Ajax的模式类似(也有可能就是使用了Ajax的方式)。但是由于IE8及以下版本都不支持Worker,所以IE就不能充分利用Worker的优点来优化浏览器执行javascript代码的性能了。但是既然Worker跟Ajax类似,那么就让IE使用Ajax的方式来实现吧,这也不是不行的。
在IE中实现Worker机制,一个比较棘手的问题是postMessage、onmessage在主页面和Worker之间的调用问题,从代码上看,主页面是通过Worker类的实例化对象来调用postMessage、onmessage,而Worker里是直接声明和调用。所以在IE里,就可以模拟Worker的操作方式了。下面是我在IE下实现的方式:
- 在IE下重新定义Worker类,并且带有一个postMessage方法,这样就的话不会跟支持Worker的浏览器相冲突
- 使用Ajax的方式来加载外联的javascript文件,并通过eval执行返回的代码
- 通过一个全局的字面量对象,来实现两个文件之间的数据传输
- 不能改变标准的Worker类的编写方式,这个是一定要做到的
从上面的几点思路出发,编写了下面的实现代码:
[javascript]
(function(g){
if(!document.all) return;
var xhr=function(){
var x = null;
try{
x = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
x = new ActiveXObject("Microsoft.XMLHTTP");
}
return x;
}
g.postMessage = function(data){
g._evt_.data = data;
}
var Worker = function(url){
this.url = url;
}
Worker.prototype={
postMessage:function(data){
g._evt_={};
g._evt_.data=data;
var x=xhr(),t=this;
x.open("GET",this.url,true);
x.onreadystatechange=function(){
if(x.readyState === 4 && (x.status === 304 || x.status === 200)){
//直接使用eval(x.responseText)在IE8下会提示错误,很奇怪,可能跟eval函数有关了
//为此不得不使用这种不太牢固的方法
eval(x.responseText.replace("onmessage","var onmessage"));
onmessage(g._evt_); //执行Worker中定义的onmessage方法
t.onmessage(g._evt_); //执行主页面中的onmessage方法
g._evt_ = null;
}
}
x.send();
}
}
g.Worker = Worker;
})(this);
[/javascript]
在使用上有一点得注意,虽然在代码上没改变,但是在主页面中postMessage和onmessage的顺序得保持:先写onmessage、接着写postMessage方法,主要是为了在IE下能兼容。比如:
[javascript]
var worker = new Worker("js.js");
worker.onmessage = function(evt){
alert(evt.data);
}
//注意postMessage方法一定要在onmessage后面声明,否则会导致代码只会有一次有效。
worker.postMessage("supersha");
[/javascript]
到目前为止,测试还算良好,《测试页面》,在各个浏览器下都能够跑起来。
目前这个只是个简单的实现方案,代码上还是比较简单的,有待进一步的完善……
摘录一段我跟瓶子关于Worker的讨论:
—————————————
瓶子<E_Virus> 10:41:26
最后eval的时候线程就终止了
不是载入的问题
是运行的问题
supersha 10:42:44
或者可以再加个setTimeout
瓶子<E_Virus> 10:42:01
用worker即使运行很占cpu的运算,也不会导致UI卡死
supersha 10:43:27
但是前提是使用Worker,得必须在ie下有个兼容方案才行
瓶子<E_Virus> 10:42:42
都是后台处理的运算,最后拿到结果的时候通过postMessage提交给主线程的js
瓶子<E_Virus> 10:43:49
异步加载这些内容只是这个worker的一个小实现
它最大的意义在于可以在浏览器的后台运行js
supersha 10:46:03
嗯
瓶子<E_Virus> 10:45:21
所以worker的使用还很小
应用比较少
瓶子<E_Virus> 10:50:10
Worker如果浏览器不支持,是没有办法做到兼容的
比如一个代码很少,但cpu占用很高的循环,如果要通过真正的Worker,可以做到UI无阻碍的运行。但是要让IE实现模拟,必须对代码进行修改,还要保存每一次循环时的运行状态,代码改动比较大,也没办法一份js让两边都最高效率
supersha 10:51:56
这个是比较极端的,但是在普通的外联js文件的时候,我们可以这样来使用
既能做到数据交互
瓶子<E_Virus> 10:51:31
呵呵,那个就没有必要用worker这样的东西了
跟json没啥区别嘛
supersha 10:52:21
又能充分的利用Worker
瓶子<E_Virus> 10:51:53
Worker的主要工作就是大规模运算的
supersha 10:52:43
当你需要链入一个js文件的时候
我不行new它需要很大的内存消耗
信
supersha 10:54:42
这不是说大材小用,而是在平时的使用中能充分的使用它
利用它的优点
瓶子<E_Virus> 10:54:35
呵呵,实现不是问题。主要是对于Worker的理解,我觉得Worker仅仅被用作异步加载js,就失去了本身的意义了,那不就是普通的json嘛
supersha 10:56:17
异步加载是一部分,就你说的,主要是独立线程来运行,这样可以一举两得的
网站文章写的很不错,RSS已经被我订阅了!