JS-Interpreter 是主要配合 Google 的可視化編程工具 Blockly 的一個庫
這里翻譯了它的文檔
原翻譯在 GitHub 上 原翻譯
JS-Interpreter 中文文檔
翻譯自 JS-Interpreter Documentation
JS-Interpreter 是用 JavaScript 寫的具有沙箱環(huán)境的 JavaScript 解析器. 它可以讓你任意的, 一行一行地執(zhí)行 JavaScript 代碼. 它的執(zhí)行過程與主要的 JavaScript 代碼環(huán)境是分離開的. JS-Interpreter 的多個實例可以允許多線程并發(fā)JavaScript, 而無需使用Web Workers.
在這可以你可以嘗試一下它 : JS-Interpreter demo
獲取它的源代碼 源代碼
使用方法
- 引入這兩個 JavaScript 源代碼
<script src="acorn.js"></script>
<script src="interpreter.js"></script>
- 當(dāng)然, 你也可以選擇它們的壓縮包(70kb)
<script src="acorn_interpreter.js"></script>
然后, 實例化一個 interpreter, 并且把你需要還原的 JavaScript 代碼放進(jìn)去
var myCode = 'var a=1; for(var i=0;i<4;i++){a*=i;} a;';
var myInterpreter = new Interpreter(myCode);
可以隨時添加其他 JavaScript 代碼(經(jīng)常用于交互式調(diào)用(譯者注:我猜是事件之類的)預(yù)先定義的函數(shù))
myInterpreter.appendCode('foo();');
為了去一步一步地跑這些代碼, 需要重復(fù)地去調(diào)用 step 函數(shù), 直到它返回 false
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 0);
}
}
nextStep();
或者, 如果已知代碼里面沒有死循環(huán), 則可以直接調(diào)用 run 函數(shù)執(zhí)行一次完成全部的 step
myInterpreter.run();
在代碼遇到異步API調(diào)用的情況下(見下文), 如果阻塞了并且需要在以后重新執(zhí)行, run將返回 true
外部的 API
與 eval 類似, 最后一個語句的結(jié)果可以在 myInterpreter.value 中找到
var myInterpreter = new Interpreter('6 * 7');
myInterpreter.run();
alert(myInterpreter.value);
另外, API 的調(diào)用可以在創(chuàng)建的時候被添加到 interpreter, 下面是添加了 alert() 和 變量 url
var initFunc = function(interpreter, scope) {
interpreter.setProperty(scope, 'url', String(location));
var wrapper = function(text) {
return alert(text);
};
interpreter.setProperty(scope, 'alert',
interpreter.createNativeFunction(wrapper));
};
var myInterpreter = new Interpreter(myCode, initFunc);
JSON demo 是在瀏覽器和 interpreter 之間轉(zhuǎn)換 JSON 的一個例子. 有關(guān)更復(fù)雜的示例, 請參閱 initGlobalScope 函數(shù), 該函數(shù)為 Math, Array, Function 和其他全局變量創(chuàng)建API.
異步的 API 函數(shù)也可以被包起來, 使它們看起來與解釋器同步. 例如, 可以在 initFunc 中定義返回 XMLHttpRequest 內(nèi)容的getXhr(url)函數(shù), 如下所示:
var wrapper = function(href, callback) {
var req = new XMLHttpRequest();
req.open('GET', href, true);
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
callback(req.responseText);
}
};
req.send(null);
};
interpreter.setProperty(scope, 'getXhr',
interpreter.createAsyncFunction(wrapper));
上面的代碼片段使用了 createAsyncFunction, 正如之前使用的 createNativeFunction 一樣. 區(qū)別是被包裹(wrapped)的異步函數(shù)的返回值被忽略了. 取而代之的是, 當(dāng) wrapper 函數(shù)被調(diào)用的時候, 會有一個回調(diào)函數(shù)會被傳遞進(jìn)去. 當(dāng) wrapper 函數(shù)要有返回值的時候, 它會調(diào)用這個回調(diào)函數(shù), 這樣你就有了它的返回值了. 從 JS-Interpreter 內(nèi)部運行的代碼的角度來看, 進(jìn)行了函數(shù)調(diào)用并立即返回結(jié)果.
這里是一個能跑的例子 async demo
序列化
JS-Interpreter的一個獨特功能是它能夠暫停執(zhí)行, 序列化(把變量從內(nèi)存中變成可存儲或傳輸?shù)倪^程)當(dāng)前狀態(tài), 然后在稍后的時間點恢復(fù)執(zhí)行. 保留了循環(huán), 變量, 閉包和所有其他狀態(tài).
使用這個功能包括連續(xù)執(zhí)行在服務(wù)器重新啟動后繼續(xù)執(zhí)行的程序, 加載已計算到某個點的堆棧映像, 分支執(zhí)行或回滾到一個已經(jīng)被存儲了的狀態(tài).
一個缺點是序列化格式不是可讀的, 并且也不能保證 JS-Interpreter 的未來版本能夠解析舊版本的序列化. 另一個缺點是序列化格式相當(dāng)大; 在標(biāo)準(zhǔn)的 polyfill 下, 它的開銷為 60kb.
這里是一個能跑的例子 serialization demo
多線程
JavaScript 是單線程的語言, 但是 JS-Interpreter 允許同時運行多個進(jìn)程. 創(chuàng)建兩個或更多的獨立進(jìn)程, 它們彼此分開運行是很簡單的: 只需創(chuàng)建兩個或多個 Interpreter 實例, 每個實例都有自己的代碼, 并且可以調(diào)用每個 interpreter 的 step 函數(shù). 它們可以通過提供的任何外部 API 間接地相互通信.
稍微復(fù)雜的情況是兩個或多個線程想要共享相同全局作用域. 要實現(xiàn)這一點, 1)創(chuàng)建一個JS-Interpreter, 2)創(chuàng)建一個單獨的堆棧列表, 3)將每個堆棧的根節(jié)點的.scope屬性分配給 interpreter 的 .global屬性, 4)然后將所需的堆棧分配給調(diào)用 step 之前interpreter的 stateStack 屬性.
這里有一個例子: tread demo
限制
interpreter 實現(xiàn)的 JavaScript 版本與在瀏覽器中執(zhí)行的版本略有不同
API
沒有暴露 DOM 的 API, 這也是沙箱的重點. 如果您需要這些, 請編寫自己的接口ES6
最近添加的 JavaScript (例如 let 或 Set)未實現(xiàn). 如果您需要超過 ES5, 請為這個項目貢獻(xiàn)代碼.toString & valueOf
將對象轉(zhuǎn)換為 JS-Interpreter 的原語(primitives)時,不會調(diào)用用戶創(chuàng)建的函數(shù)性能
interpreter 并不是十分快, 它大概比原生 JS 慢 200 倍
依賴
唯一的依賴就是 Acorn, 這是 Marijn Haverbeke 寫的 JavaScript 的還原器. 它已經(jīng)被包含進(jìn)了 JS-Interpreter
兼容性
需要瀏覽器支持使用 Object.create(null)在 Acorn 和 JS-Interpreter 中創(chuàng)建 Object. 這最低的瀏覽器要求:
- Chrome 5
- Firefox 4.0
- IE 9
- Opera 11.6
- Safari 5
免責(zé)聲明
這個并不是 Google 官方的產(chǎn)品