在前面的第4節(jié)中,已經(jīng)介紹了如何生成并執(zhí)行js代碼,本篇將補充一些其它的內(nèi)容。
1. 基本步驟
第一步:引入js庫
<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>
第二步:調(diào)用workspaceToCode方法生成js代碼
Blockly.JavaScript.addReservedWords('code'); // 1
var code = Blockly.JavaScript.workspaceToCode(workspace);
注意: 上面的第1句代碼將code其添加到保留字列表中,以便如果用戶的代碼包含該名稱的變量,它將自動重命名而不是相互沖突。任何局部變量都應(yīng)該以這種方式保留。
第三步:執(zhí)行js代碼
try {
eval(code);
} catch (e) {
alert(e);
}
注意:eval 是js提供的一個強大的方法。在純前端應(yīng)用中,我們很少會用到它。但在這里,eval函數(shù)必不可少:它可以把一個字符串解析成js語句并執(zhí)行!
2 高亮塊(僅限Web)
在blockly web應(yīng)用程序中,我們通常會需要一種逐句執(zhí)行的效果:在執(zhí)行某句代碼時,把它對應(yīng)的block高亮顯示,起到分步提醒的作用。這是一種非常好的用戶體驗。
這個效果可以通過在生成JavaScript代碼之前,逐個語句級別設(shè)置STATEMENT_PREFIX來實現(xiàn):
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
Blockly.JavaScript.addReservedWords('highlightBlock');
定義highlightBlock在工作區(qū)上標(biāo)記塊。
function highlightBlock(id) {
workspace.highlightBlock(id);
}
這會導(dǎo)致在highlightBlock('123');每個語句前添加語句,其中123是要突出顯示的塊的序列號。
3. 無限循環(huán)的特殊處理
盡管我們生成的代碼在語法上是正確的,但它可能包含無限循環(huán)。包含無限循環(huán)的代碼會超出了Blockly的處理范圍。處理這些情況的最佳方法是維護一個計數(shù)器并在每次迭代執(zhí)行時減少計數(shù)器。要做到這一點,只需設(shè)置Blockly.JavaScript.INFINITE_LOOP_TRAP一個代碼片段,將其插入每個循環(huán)和每個函數(shù)中。這里是一個例子:
window.LoopTrap = 1000;
Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);


例
這是 一個 生成和執(zhí)行JavaScript 的現(xiàn)場演示。
JS解釋器
如果你認(rèn)真地正確地運行用戶塊,那么 JS解釋器項目就是要走的路。此項目與Blockly分開,但是專門為Blockly編寫。
- 以任何速度執(zhí)行代碼。
- 暫停/恢復(fù)/逐步執(zhí)行。
- 在執(zhí)行時突出顯示塊。
- 完全與瀏覽器的JS隔離。
這是一個 使用Blockly和JS解釋器來生成和執(zhí)行JavaScript 的現(xiàn)場演示。
運行解釋器
首先,從GitHub下載JS Interpreter:github

然后將其添加頁面中:
<script src="acorn_interpreter.js"></script>
調(diào)用它的最簡單方法是:生成JavaScript,創(chuàng)建解釋器,并運行代碼:
var code = Blockly.JavaScript.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
也就是用上面的三行代碼來代替下面的兩行代碼去運行js。
var code = Blockly.JavaScript.workspaceToCode(demoWorkspace);
eval(code)
如果你在運行的過程中遇到的問題,不要急,先向下看。
步驟解釋器
為了更慢地執(zhí)行代碼或以更受控制的方式執(zhí)行代碼,請將調(diào)用替換run為循環(huán)(在此情況下每10ms執(zhí)行一步):
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 10);
}
}
nextStep();
請注意,每一步不是一行或一個塊,它是JavaScript中的一個語義單位,它可能非常精細。
瀏覽器隔離的沙盒
JS解釋器是一個完全與瀏覽器隔離的沙盒。任何執(zhí)行外部操作的塊都需要將API添加到解釋器中。有關(guān)完整說明,請參閱 JS解釋器文檔。
上面的這段說明很重要:通過如下的
var code = Blockly.JavaScript.workspaceToCode(workspace);
var myInterpreter = new Interpreter(code);
myInterpreter.run();
的方式運行js代碼有一個前提:即code中的函數(shù)調(diào)用,方法調(diào)用都必須提前在InterPreter這個沙盒中注冊過。
如果在code中有代碼"alert(1)",如果你用eval(code)去運行,則是可以的,因為當(dāng)前是在window環(huán)境中運行,而用上面的方式去運行就會出錯:原因是alert并沒有在interpreter環(huán)境中提前去注入!
這樣做的好處是:用Interpreter統(tǒng)一管理blockly的代碼。形成一個封裝的環(huán)境,從而避免了代碼污染。缺點要先注冊你的函數(shù),這個過程是比較繁瑣的。
注入api的示例
我們把alert 和 prompt注入到interpreter環(huán)境中。
第一步:定義注入工具函數(shù)
function initApi(interpreter, scope) {
// Add an API function for the alert() block.
var wrapper = function(text) {
return alert(arguments.length ? text : '');
};
interpreter.setProperty(scope, 'alert',
interpreter.createNativeFunction(wrapper));
// Add an API function for the prompt() block.
wrapper = function(text) {
return prompt(text);
};
interpreter.setProperty(scope, 'prompt',
interpreter.createNativeFunction(wrapper));
}
第二步:在解釋器初始化時傳遞initApi函數(shù):
var myInterpreter = new Interpreter(code, initApi);
經(jīng)過這兩步之后,如果你的代碼中有alert或者prompt 函數(shù),它們也會正常運行了。
示例2 highlightBlock()
highlighBlock是blockly提供的,如果你希望它在JS Interpreter中也能正常運行,你必須創(chuàng)建一個包裝函數(shù)highlightBlock()來捕獲函數(shù)參數(shù),并將其注冊為本機函數(shù)。
function initApi(interpreter, scope) {
// Add an API function for highlighting blocks.
var wrapper = function(id) {
return workspace.highlightBlock(id);
};
interpreter.setProperty(scope, 'highlightBlock',
interpreter.createNativeFunction(wrapper));
}
更復(fù)雜的應(yīng)用程序可能希望重復(fù)執(zhí)行步驟而不會暫停,直到達到高亮命令,然后暫停。該策略模擬逐行執(zhí)行。示例、演示