先来段简单的代码:
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函数变量的传递就要麻烦些。因为其中涉及到闭包等问题,所以仅仅传递函数字符串是肯定行不通的。解决这个办法,需要和存储对象类型一个办法:发送方在传递函数前先储存起来,传递的只是一个序列号;接收方收到序列号后,新建一个代理函数,里面包含了这个序列号。当以后被调用时,代理函数将序列号作为参数通知给对方,对方通过序列号从数组里取出原函数,执行。
这样一个大致的轮廓就出来了:
目前一些常用功能可以正常运行。下面写了几个简单的JSDemo,能在ActionScript正常运行:
http://www.etherdream.com/FunnyScript/RunJS/RunJS.html
不要忘了,这些JS可是运行在ActionScript环境下的!
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。有兴趣的继续关注。