【探索】用ActionScript模拟运行JavaScript
先来段简单的代码:
function JSDemo() { var doc = window.document; var div = doc.createElement("div"); div.innerHTML = "Hello! <i>This box is created by JavaScript!</i>"; div.style.background = "#CCC"; div.style.font = "bolder 18px 'Courier New'"; div.style.border = "1px dashed #693"; doc.body.appendChild(div); }
这是再简单不过的JS代码,最基本的DOM创建和操作。不过把他复制到ActionScript里,它还能运行吗?显然不可能。虽然他们有着相似语法,但运行环境完全不同,当然是连编译都通不过的。
仔细思考下,AS3虽然是JavaScript2.0的风格,但也向下兼容当前的JS语法。仅语法上说,JS复制到AS下是没有语法错误的,只是变量没有定义类型的提示的警告。但Flash SDK没有提供Web的接口,所以window,document这些变量就不存在了,因此无法通过编译。显然,如果我们能够提供这些接口,那么代码至少能通过编译。
纵观Web下的各种接口,都是从window对象延伸开来。所以我们只需模拟出window对象,之后其他对象就可以从这个顶级接口中获取。由于不同浏览器下的接口都各不相同,并且错综复杂,所以手工的去模拟每一个接口的功能是不现实的。因此我们需要一个AS和Web之间的代理程序,实现接口的自动转换。
ActionScript自带一个功能强大的类: flash.utils.Proxy。继承它之后,我们的类就可以实现一些底层的操作。我们可以覆盖对象默认的属性读写,方法调用等等,类似C++的operator操作符。通过ExternalInterface.call,我们可以向Flash所在Web页面进行交互,并返回数据,于是我们就可以实现AS/JS接口自动化代理了。
例如,当访问window对象的document属性时,我们的getProperty重载函数向Web发送“ 获取window的document属性”指令。Web端的JS收到指令后,将document属性从window对象读取。不过由于document也是个对象,不是基本类型,所以不能直接返回给AS,而是将其储存在数组里,返回给AS一个对象序列号,这个字符串里包含了数组的id位置。当以后访问document的属性时,这个代表document对象的序列号就会传递过去,js就能从数组里还原这个对象。
不过要实现JS/AS函数变量的传递就要麻烦些。因为其中涉及到闭包等问题,所以仅仅传递函数字符串是肯定行不通的。解决这个办法,需要和存储对象类型一个办法:发送方在传递函数前先储存起来,传递的只是一个序列号;接收方收到序列号后,新建一个代理函数,里面包含了这个序列号。当以后被调用时,代理函数将序列号作为参数通知给对方,对方通过序列号从数组里取出原函数,执行。
这样一个大致的轮廓就出来了:
package { import flash.display.*; public class RunJS extends Sprite { private var window:JSEnv = JSEnv.$; public function RunJS() { JSLine("DOM Demo:"); JSDemo1(); JSLine("Event Demo:"); JSDemo2(); JSLine("Closure Demo:"); JSDemo3(); JSLine("AJAX Demo:"); JSDemo4(); } function JSLine(str) { var doc = window.document; var div = doc.createElement("div"); div.innerHTML = "<p>" + str + "<hr/></p>" doc.body.appendChild(div); } function JSDemo1() { var doc = window.document; var div = doc.createElement("div"); div.innerHTML = "Hello! <i>This box is created by ActionScript!</i>"; div.style.background = "#CCC"; div.style.font = "bolder 18px 'Courier New'"; div.style.border = "1px dashed #693"; doc.body.appendChild(div); } function JSDemo2() { var doc = window.document; var btn = doc.createElement("button"); btn.innerHTML = "Click Me!"; btn.onclick = function() { var i = 0; window.setInterval(function() { btn.innerHTML = "Run in ActionScript: i=" + i++; }, 10) }; doc.body.appendChild(btn); } function JSDemo3() { var doc = window.document; for(var i=0; i<5; i++) { var btn = doc.createElement("button"); doc.body.appendChild(btn); btn.innerHTML = "Button" + i; btn.onclick = (function(i) { return function(){window.alert(i)}; })(i); } } function JSDemo4() { var doc = window.document; var btn = doc.createElement("button"); doc.body.appendChild(btn); btn.innerHTML = "Load Test.xml"; btn.onclick = function() { var xhr = window.ActiveXObject? new window.ActiveXObject("Microsoft.XMLHTTP"): new window.XMLHttpRequest; xhr.onreadystatechange = function() { if(xhr.readyState != 4) return; window.alert(xhr.responseText); }; xhr.open("GET", "Test.xml", true); xhr.send(); }; } } }
当然,目前仍有不少问题有待解决。这里将继续研究,借用ActionScript强大的IDE来调试JavaScript。并且升级之前的JS解释器,将ActionScript解释成更高效的JS。从而彻底抛弃混乱纠结的JS-OOP。有兴趣的继续关注。
共同学习,写下你的评论
评论加载中...
作者其他优质文章