當前位置:
首頁 > 最新 > 以一個interpreter實現者的視角去看閉包:寫一個interpreter

以一個interpreter實現者的視角去看閉包:寫一個interpreter

作為一個前端開發,一個javascript的使用者,對閉包的實現非常好奇,於是研究了一翻並寫了一個簡單的interpreter. 這個interpreter支持VarStatement,RereturnStaturn,FunctionLiterial,和CallExpression。

Interpreter一直以來都是以黒盒的形式展現在前端開發面前,本文的目的是打開盒子去更深入的了解我們日常所用的編程語言。

Interpreter從功能上主要包含三部分:Lexer,Parser和Evaluator

1. Lexer的主要作用是將code轉化為一個個Token

例如Lexer的輸入為var a = 1;輸出如下圖

2.Parser的作用是把Lexer輸出的tokens以一定的規律組合生成抽象語法樹,抽象語法樹是一種樹形結構,這個樹形結構描述了code中所包含的所用信息。

vara=1;

function() {

varb=1;

};

上面這段代碼生成的語法樹結構如下:

這個抽象語法樹的跟元素是Program對象,它的statements屬性包含兩個對象:一個VarStatement和一個ExpressionStatement。

ExpressionStatement的expression屬性是FunctionLiteral。

FunctionLiteral包含body屬性,這個屬性是個BlockStatement。

BlockStatement對象的結構和Program類似。

3. Evaluator的作用是遍歷抽象語法樹,根據碰到的node類型去做不同的操作,其實Evaluator就是一個遞歸函數,代碼如下:

export default functionEval(node, env) {

switch(node.constructor) {

caseProgram:

returnevalStatements(node.statements, env)

caseExpressionStatement:

returnEval(node.expression, env)

caseVarStatement:

letvalue=Eval(node.value, env)

env.set(node.name.value,value)

returnvalue

caseFunctionLiteral:

return newFunction(node.body, env)

caseCallExpression:

letfunc=Eval(node.func, env)

returnapplyFunction(func)

}

return null

}

這個遞歸函數的第二個入參env是一個對象,它的作用是綁定和獲取變了。其代碼如下:

export default classEnvironment {

constructor() {

this.store= {}

}

set(name, value) {

this.store[name] = value

}

get(name) {

return this.store[name]

}

}

當Evaluator遇到VarStatement節點的時候,會把VarStatement所包含的信息以鍵值對的形式綁定到當前env中。當它遇到Identifier節點的時候會去當前的env中拿之前綁定的變數。

functionevalIdentifier(node, env) {

letval= env.get(node.value)

returnval

}

接下來重點講閉包:結合下面的代碼來說,被返回了的inner函數能獲取到outer函數中所定義的a變數就是閉包的表現。

vartest=function() {// outer

var a=1;

return function(){// inner

return a+2;

};

};

test()();

1.首先Eval會先遇到VarStatement,把test變數存儲在當前的environment對象env1中

2.Eval遇到test()(),這是一個CallExpression,要調用的對象是test()這顯然還是個CallExpression,

因此會先解析test(),到目前為止environment對象還是env1

3.開始解析test(),首先從當前的environment對象env1中拿出test,test是一個FunctionLiteral

4.調用test,也就是解析這個FunctionLiteral,也就是解析這個函數的函數體:Eval(func.body,env?)

5.關鍵是在什麼environment下去解析這個函數體,在解析這個函數體的時候我們會碰到一個VarStatement,

VarStatement會向當前的envirnment中綁定變數test,我們肯定是不希望這個test直接綁定在env1中的,

因為可能會覆蓋env1中綁定的變數。所以在解析這個函數的函數體的時候我們去新建一個envirnment,這個

envirnment可以綁定變數,並且有一個屬性指向env1。

6.這時候就輪到EnclosedEnvironment對象上場了,這個對象的定義如下

export classEnclosedEnvironment {

constructor(outer) {

this.outer= outer

this.env=newEnvironment()

}

set(name, value) {

this.env.set(name, value)

}

get(name) {

return this.env.get(name)this.outer.get(name)

}

}

7.也就是我們會新建一個對象env2,這個env2的outer屬性是env1,這時候當前的environment對象

已經是env2了

8. 在env2環境下解析outer函數的函數體,返回一個FunctionLiteral,也就是inner函數表達式

9. 解析inner函數體,在什麼環境下? 當然是env3環境下,env3有一個outer屬性是指向env2的

總結一下其實就是當沒遇到CallExpression的時候解析的環境env(n)是不變的,一旦遇到CallExpression,就需要

新建一個EnclosedEnvironment對象env(n+1),env(n+1)的outer屬性是指向env(n)的。

interpreter demo效果如下

想要代碼的同學分享本文到朋友圈並截圖發到公眾號

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端磚家 的精彩文章:

TAG:前端磚家 |

您可能感興趣

「猿·視角」Supreme:Nike你走吧,我覺得adidas更好。
解構| MM6maisonmargiela 怪才的另類視角
輕鬆分享展示HoloLens視角,微軟正式發布SpectatorView
中尉視角:2018年上海音響展之 DenonMarantzDefinitivePolk Audio 篇
Being in the World——淺談Olafur Eliasson作品在建築、景觀、及城市設計視角下的表達
Imagination Technologies 獨家視角:2018年CES上的科技趨勢
Piaget推出Sunlight Escape系列高級珠寶 以不同視角詮釋作品
猿視角:呵,瘋漲的 Air Jordan 1
西澳大學:NovelPerspective:人物視角識別
交易平台視角下的Staking Economy
盲人視角射擊遊戲《Muffled Warfare》在Steam平台開啟搶先體驗
Madame Figaro 10月刊 | 時尚的另一個視角
視角 Stone Island品牌預覽
Ambarella又來了!GGAI視角
每日一圖|養魚場之上帝視角。|攝影師:Bernhard Lang
Lisa和Rose的閨蜜視角 我擺pose你拍照 逆光也很美
「猿·視角」飛躍罰球線:Jordan Brand 是個讓人歡喜的"大騙子"
photoshop中怎麼調整照片的透視角度
來自母親的視角——Sally Mann
從營銷視角揭秘Facebook、Twitter、Ins等5大社交平台的演算法運作原理