最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

用 js 寫一個 js 解釋器過程詳解

 更新時間:2019年08月02日 11:17:43   作者:noahlam  
這篇文章主要介紹了用 js 寫一個 js 解釋器過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

用 js 來 編譯 js 看起來是個高大上的東西,實(shí)際原理其實(shí)很簡單,無非就是利用 js 對象屬性可以用字符串表示 這個特性來實(shí)現(xiàn)的黑魔法罷了。

之所以看起來那么 深奧, 大概是由于網(wǎng)上現(xiàn)有的教程,都是動不動就先來個 babylon / @babel/parser 先讓大家看個一大串的 AST, 然后再貼出一大串的代碼,

直接遞歸 AST 處理所有類型的節(jié)點(diǎn). 最后成功的把我這樣的新手就被嚇跑了。

那么今天我寫這篇的目的,就是給大家一個淺顯易懂,連剛學(xué) js 的人都能看懂的 js2js 教程。

先來看一下效果

一個最簡單的解釋器

上面有提到,js 有個特性是 對象屬性可以用字符串表示,如 console.log 等價于 console['log'], 辣么根據(jù)這個特性,我們可以寫出一個兼容性極差,極其簡陋的雛形

 function callFunction(fun, arg) {
 this[fun](arg);
 }
 callFunction('alert', 'hello world');
 // 如果你是在瀏覽器環(huán)境的話,應(yīng)該會彈出一個彈窗

既然是簡易版的,肯定是問題一大堆,js 里面得語法不僅僅是函數(shù)調(diào)用,我們看看賦值是如何用黑魔法實(shí)現(xiàn)的

 function declareVarible(key, value) {
 this[key] = value;
 }
 declareVarible.call(window, 'foo', 'bar');
 // window.foo = 'bar'

Tips: const 可以利用 Object.defineProperty 實(shí)現(xiàn);

如果上面的代碼能看懂,說明你已經(jīng)懂得了 js 解釋器 的基本原理了,看不懂那只好怪我咯。

稍微加強(qiáng)一下

可以看出,上面為了方便, 我們把函數(shù)調(diào)用寫成了 callFunction('alert', 'hello world'); 但是著看起來一點(diǎn)都不像是 js 解釋器,
我們心里想要的解釋器至少應(yīng)該是長這樣的 parse('alert("hello world")''), 那么我們來稍微改造一下, 在這里我們要引入 babel 了,

不過先不用擔(dān)心, 我們解析出來的語法樹(AST)也是很簡單的。

import babelParser from '@babel/parser';
const code = 'alert("hello world!")';
const ast = babelParser.parse(code);

以上代碼, 解析出如下內(nèi)容

{
 "type": "Program",
 "start": 0,
 "end": 21,
 "body": [
 {
 "type": "ExpressionStatement",
 "start": 0,
 "end": 21,
 "expression": {
 "type": "CallExpression",
 "start": 0,
 "end": 21,
 "callee": {
 "type": "Identifier",
 "start": 0,
 "end": 5,
 "name": "alert"
 },
 "arguments": [
 {
 "type": "Literal",
 "start": 6,
 "end": 20,
 "value": "hello world!",
 "raw": "\"hello world!\""
 }
 ]
 }
 }
 ],
 "sourceType": "module"
}

上面的內(nèi)容看起來很多,但是我們實(shí)際有用到到其實(shí)只是很小的一部分, 來稍微簡化一下, 把暫時用不到的字段先去掉

{
 "type": "Program",
 "body": [
 {
 "type": "ExpressionStatement",
 "expression": {
 "type": "CallExpression",
 "callee": {
 "type": "Identifier",
 "name": "alert"
 },
 "arguments": [
 {
 "type": "Literal",
 "value": "hello world!",
 }
 ]
 }
 }
 ],
}

我們先大概瀏覽一遍 AST 里面的所有屬性名為 type 的數(shù)據(jù)

  • ExpressionStatement
  • CallExpression
  • Identifier
  • Literal

一共有 4 種類型, 那么接下來我們把這 4 種節(jié)點(diǎn)分別解析, 從最簡單的開始

Literal

{
 "type": "Literal",
 "value": "hello world!",
}

針對 Literal 的內(nèi)容, 我們需要的只有一個 value 屬性, 直接返回即可.

if(node.type === 'Literal') {
 return node.value;
}

是不是很簡單?

Identifier

{
 "type": "Identifier",
 "name": "alert"
},

Identifier 同樣也很簡單, 它代表的就是我們已經(jīng)存在的一個變量, 變量名是node.name, 既然是已經(jīng)存在的變量, 那么它的值是什么呢?

if(node.type === 'Identifier') {
 return {
 name: node.name,
 value:this[node.name]
 };
}

上面的 alert 我們從 node.name 里面拿到的是一個字符, 通過 this['xxxxx'] 可以訪問到當(dāng)前作用域(這里是 window)里面的這個標(biāo)識符(Identifier)

ExpressionStatement

{
 "type": "ExpressionStatement",
 "expression": {...}
}

這個其實(shí)也是超簡單, 沒有什么實(shí)質(zhì)性的內(nèi)容, 真正的內(nèi)容都在 expression 屬性里,所以可以直接返回 expression 的內(nèi)容

if(node.type === 'ExpressionStatement') {
 return parseAstNode(node.expression);
}

CallExpression

CallExpression 按字面的意思理解就是 函數(shù)調(diào)用表達(dá)式,這個稍微麻煩一點(diǎn)點(diǎn)

{
 "type": "CallExpression",
 "callee": {...},
 "arguments": [...]
}

CallExpression 里面的有 2 個我們需要的字段:

callee 是 函數(shù)的引用, 里面的內(nèi)容是一個 Identifier, 可以用上面的方法處理.

arguments 里面的內(nèi)容是調(diào)用時傳的參數(shù)數(shù)組, 我們目前需要處理的是一個 Literal, 同樣上面已經(jīng)有處理方法了.

說到這里,相信你已經(jīng)知道怎么做了

if(node.type === 'CallExpression') {
 // 函數(shù)
 const callee = 調(diào)用 Identifier 處理器
 // 參數(shù)
 const args = node.arguments.map(arg => {
 return 調(diào)用 Literal 處理器
 });
 callee(...args);
}

代碼

這里有一份簡單的實(shí)現(xiàn), 可以跑通上面的流程, 但也僅僅可以跑通上面而已, 其他的特性都還沒實(shí)現(xiàn)。

https://github.com/noahlam/practice-truth/tree/master/js2js

其他實(shí)現(xiàn)方式

除了上面我介紹得這種最繁瑣得方式外,其實(shí) js 還有好幾種可以直接執(zhí)行字符串代碼得方式

1.插入 script DOM

 const script = document.createElement("script");
 script.innerText = 'alert("hello world!")';
 document.body.appendChild(script);

2.eval

 eval('alert("hello world!")')

3.new Function

 new Function('alert("hello world")')();

4.setTimeout 家族

setTimeout('console.log("hello world")');

不過這些在小程序里面都被無情得封殺了...

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • javascript特效實(shí)現(xiàn)——當(dāng)前時間和倒計時效果的簡單實(shí)例

    javascript特效實(shí)現(xiàn)——當(dāng)前時間和倒計時效果的簡單實(shí)例

    下面小編就為大家?guī)硪黄猨avascript特效實(shí)現(xiàn)——當(dāng)前時間和倒計時效果的簡單實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • JavaScript中return用法示例

    JavaScript中return用法示例

    這篇文章主要介紹了JavaScript中return用法,結(jié)合實(shí)例形式簡單分析了javascript中return語句的功能與使用技巧,需要的朋友可以參考下
    2016-11-11
  • webpack-dev-server原理解析及跨域解決方法

    webpack-dev-server原理解析及跨域解決方法

    這篇文章主要介紹了webpack-dev-server原理解析及跨域解決方法,需要的朋友可以參考下
    2024-01-01
  • 使用JavaScript實(shí)現(xiàn)鏈表的數(shù)據(jù)結(jié)構(gòu)的代碼

    使用JavaScript實(shí)現(xiàn)鏈表的數(shù)據(jù)結(jié)構(gòu)的代碼

    鏈表(Linked list)是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),是一種線性表,但是并不會按線性的順序存儲數(shù)據(jù),而是在每一個節(jié)點(diǎn)里存到下一個節(jié)點(diǎn)的指針(Pointer) 。下面我們用 JavaScript 代碼對鏈表的數(shù)據(jù)結(jié)構(gòu)進(jìn)行實(shí)現(xiàn)
    2017-08-08
  • js實(shí)現(xiàn)文本框選中的方法

    js實(shí)現(xiàn)文本框選中的方法

    這篇文章主要介紹了js實(shí)現(xiàn)文本框選中的方法,涉及javascript中select()方法的使用技巧,需要的朋友可以參考下
    2015-05-05
  • JavaScript中手動實(shí)現(xiàn)Array.prototype.map方法

    JavaScript中手動實(shí)現(xiàn)Array.prototype.map方法

    在前端開發(fā)中,我們經(jīng)常需要對數(shù)組進(jìn)行操作和處理,本文主要介紹了JavaScript中手動實(shí)現(xiàn)Array.prototype.map方法,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • 加隨機(jī)數(shù)引入腳本不讓瀏覽器讀取緩存

    加隨機(jī)數(shù)引入腳本不讓瀏覽器讀取緩存

    不讓瀏覽器讀取緩存,故采用家隨機(jī)數(shù)方式引入腳本的方式,此法適用于及時刷新不讀緩存的情況
    2014-09-09
  • JS數(shù)組操作大全對象數(shù)組根據(jù)某個相同的字段分組

    JS數(shù)組操作大全對象數(shù)組根據(jù)某個相同的字段分組

    這篇文章主要介紹了JS數(shù)組操作大全對象數(shù)組根據(jù)某個相同的字段分組,需要注意的是,在開發(fā)過程這種數(shù)組的處理函數(shù),應(yīng)當(dāng)被編寫到項(xiàng)目的公共工具函數(shù)庫中全局調(diào)用,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • 使用typescript改造koa開發(fā)框架的實(shí)現(xiàn)

    使用typescript改造koa開發(fā)框架的實(shí)現(xiàn)

    這篇文章主要介紹了使用typescript改造koa開發(fā)框架的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • JS簡單表單驗(yàn)證功能完整示例

    JS簡單表單驗(yàn)證功能完整示例

    這篇文章主要介紹了JS簡單表單驗(yàn)證功能,結(jié)合完整實(shí)例形式分析了JavaScript表單驗(yàn)證相關(guān)的字符串判斷、正則驗(yàn)證、計算等相關(guān)操作技巧,需要的朋友可以參考下
    2020-01-01

最新評論

庆阳市| 乌恰县| 成安县| 德庆县| 饶平县| 图们市| 石台县| 南康市| 南召县| 天水市| 桐乡市| 扎兰屯市| 顺昌县| 胶州市| 巨野县| 禹城市| 松潘县| 巴东县| 金堂县| 瑞安市| 平谷区| 彭山县| 萍乡市| 右玉县| 福贡县| 宜都市| 大宁县| 普安县| 文成县| 岚皋县| 张家港市| 黄大仙区| 兴国县| 东光县| 巫山县| 巴彦淖尔市| 东阿县| 丁青县| 崇礼县| 新郑市| 台江县|