麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 編程 > JavaScript > 正文

用 js 寫一個(gè) js 解釋器過(guò)程詳解

2019-11-19 11:05:09
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

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

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

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

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

先來(lái)看一下效果

一個(gè)最簡(jiǎn)單的解釋器

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

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

既然是簡(jiǎn)易版的,肯定是問(wèn)題一大堆,js 里面得語(yǔ)法不僅僅是函數(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);

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

稍微加強(qiáng)一下

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

不過(guò)先不用擔(dān)心, 我們解析出來(lái)的語(yǔ)法樹(AST)也是很簡(jiǎn)單的。

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)容看起來(lái)很多,但是我們實(shí)際有用到到其實(shí)只是很小的一部分, 來(lái)稍微簡(jiǎn)化一下, 把暫時(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 種類型, 那么接下來(lái)我們把這 4 種節(jié)點(diǎn)分別解析, 從最簡(jiǎn)單的開始

Literal

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

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

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

是不是很簡(jiǎn)單?

Identifier

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

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

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

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

ExpressionStatement

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

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

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

CallExpression

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

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

CallExpression 里面的有 2 個(gè)我們需要的字段:

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

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

說(shuō)到這里,相信你已經(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);}

代碼

這里有一份簡(jiǎn)單的實(shí)現(xiàn), 可以跑通上面的流程, 但也僅僅可以跑通上面而已, 其他的特性都還沒(méi)實(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")');

不過(guò)這些在小程序里面都被無(wú)情得封殺了...

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 美国黄色毛片女人性生活片 | 免费在线观看午夜视频 | va免费视频 | 亚洲成人自拍电影 | h视频在线免费看 | 久久精品国产久精国产 | 精品国产一区二区三区久久久蜜月 | 黄色成人短视频 | 国产一级淫片在线观看 | 免费h片| 成人区一区二区 | 久久久久久麻豆 | 欧美成人精品不卡视频在线观看 | a级高清免费毛片av在线 | 成人毛片网站 | 国产精品美女久久久久久网站 | www国产免费 | 欧美特级一级毛片 | 一级做a爰性色毛片免费 | 色诱亚洲精品久久久久久 | 日韩av在线网址 | 国产精品野外av久久久 | 国产成年人在线观看 | 黄色片快播 | 精品国产一区二区三区四 | 精品久久久久久久久久久久久久久久久久久 | 成人福利视频在线 | 黄色视频一级毛片 | 毛片免费看电影 | 日韩黄网站 | 国产噜噜噜噜噜久久久久久久久 | 亚洲性爰| 一级成人免费 | 精品视频一区二区三区四区 | 亚洲欧洲日韩av | 久久精品亚洲欧美日韩精品中文字幕 | 国产88久久久国产精品免费二区 | 成人福利视频在线观看 | 免费国产一级淫片 | 久久骚| 国产精品成人亚洲一区二区 |