先來段簡單的代碼:
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。有興趣的繼續關注。