日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

【js】JavaScript parser实现浅析

發布時間:2025/3/8 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【js】JavaScript parser实现浅析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  最近筆者的團隊遷移了webpack2,在遷移過程中,筆者發現webpack2中有相當多的兼容代碼,雖然外界有很多聲音一直在質疑作者為什么要破壞性更新,其實大家也都知道webpack1那種過于“靈活”的配置方式是有待商榷的,所以作者才會在webpack2上進行了很多規范,但是,筆者卻隱隱的覺得,等到webpack3的時候,估計會有更多的破壞性更新,不然也不會有這個webpack2了。于是心中有關webpack的話題便也擱置了,且等它更穩定一些,再談不遲,今天先來講講在劇烈的版本變化中,不變的部分。

  大家都知道,webpack是做模塊綁定用的,那么就不得不牽涉到語法解析的內容,而且其極高的擴展性,也往往需要依賴于語法解析,而在webpack內部使用acorn做語法解析,類似的還有babel使用的babylon,今天就帶來兩者的簡要分析。

  官方給兩者的定義都叫做JavaScript parser,內部也一致的使用了AST(Abstract syntax tree,即抽象語法樹)的概念。如果對這個概念不明白的同學可以參考WIKIAST的解釋

  因為babylon引用了flow,eslint等一些checker,所以整個項目結構相當的規范,筆者僅已7.0.0為例:

  文件夾目錄如下:

index.js //程序入口,會調用parser進行初始化 types.js //定義了基本類型和接口 options.js //定義獲取配置的方法以及配置的缺省值 parser //所有的parser都在此
  index.js //parser入口類,繼承自 StatementParser 即 ./statement.js
  statement.js //聲明StatementParser 繼承自 ExpressionParser 即 ./expression.js
  expression.js //聲明ExpressionParser 繼承自 LValParser 即 ./lval.js
  
lval.js //聲明 LValParser 繼承自 NodeUtils 即 ./node.js
  node.js //聲明 NodeUtils 繼承自 UtilParser 即 ./util.js, 同時還實現了上一級目錄中types.js 的nodebase接口為Node類
  util.js //聲明 UtilParser 繼承自 Tokenizer 即 ../tokenizer/index.js
  location.js //聲明 LocationParser 主要用于拋出異常 繼承自 CommentsParser 即./comments.js
  comments.js //聲明 CommentsParser 繼承自 BaseParser 即./base.js
  base.js //所有parser的基類
plugins
tokenizer
  index.js //定義了 Token類 繼承自上級目錄parser的LocationParser 即 ../parser/location.js
util

  大概流程是這樣的:

1、首先調用index.js的parse;
2、實例化一個parser對象,調用parser對象的parse方法,開始轉換;
3、初始化node開始構造ast;
  1) node.js 初始化node
  2) tokenizer.js 初始化token
  3) statement.js 調用 parseBlockBody,開始解析。這個階段會構造File根節點和program節點,并在parse完成之后閉合
  4) 執行parseStatement, 將已經合法的節點插入到body中。這個階段會產生各種*Statement type的節點
  5)分解statement, parseExpression。這個階段除了產生各種expression的節點以外,還將將產生type為Identifier的節點
  6) 將上步驟中生成的原子表達式,調用toAssignable ,將其參數歸類
4、迭代過程完成后,封閉節點,完成body閉合

  不過在筆者看來,babylon的parser實現似乎并不能稱得上是一個很好的實現,而實現中往往還會使用的forward?declaration(類似虛函數的概念),如下圖

  

  一個“+”在方法前面的感覺就像是要以前的IIFE一樣。。

  有點扯遠了,總的來說依然是傳統語法分析的幾個步驟,不過筆者在讀源碼的時候一直覺得蠻奇怪的為何他們內部要使用繼承來實現parser,parser的場景更像是mixin或者高階函數的場景,不過后者在具體處理中確實沒有繼承那樣清晰的結構。

  說了這么多,babylon最后會生成什么呢?以es2016的冪運算“3 ** 2”為例:

{"type": "File","start": 0,"end": 7,"loc": {"start": {"line": 1,"column": 0},"end": {"line": 1,"column": 7}},"program": {"type": "Program","start": 0,"end": 7,"loc": {"start": {"line": 1,"column": 0},"end": {"line": 1,"column": 7}},"sourceType": "script","body": [{"type": "ExpressionStatement","start": 0,"end": 7,"loc": {"start": {"line": 1,"column": 0},"end": {"line": 1,"column": 7}},"expression": {"type": "BinaryExpression","start": 0,"end": 6,"loc": {"start": {"line": 1,"column": 0},"end": {"line": 1,"column": 6}},"left": {"type": "NumericLiteral","start": 0,"end": 1,"loc": {"start": {"line": 1,"column": 0},"end": {"line": 1,"column": 1}},"extra": {"rawValue": 3,"raw": "3"},"value": 3},"operator": "**","right": {"type": "NumericLiteral","start": 5,"end": 6,"loc": {"start": {"line": 1,"column": 5},"end": {"line": 1,"column": 6}},"extra": {"rawValue": 2,"raw": "2"},"value": 2}}}],"directives": []} }

完整的列表看著未免有些可怕,筆者將有關location信息的去除之后,構造了以下這個對象:

{"type": "File","program": {"type": "Program","sourceType": "script","body": [{"type": "ExpressionStatement","expression": {"type": "BinaryExpression","left": {"type": "NumericLiteral","value": 3},"operator": "**","right": {"type": "NumericLiteral","value": 2}}}]} }?

  可以看出,這個類AST的的對象是內部,大部分內容是其實是有關位置的信息,因為很大程度上,需要以這些信息去描述這個node的具體作用。

  然后讓我們再來看看webpack使用的acorn:

  也許是acorn的作者和筆者有類似閱讀babylon的經歷,覺得這種實現不太友好。。于是,acorn的作者用了更為簡單直接的實現:

index.js //程序入口 引用了 ./state.js 的Parser類 state.js //構造Parser類 parseutil.js //向Parser類 添加有關 UtilParser 的方法 statement.js //向Parser類 添加有關 StatementParser 的方法 lval.js //向Parser類 添加有關 LValParser 的方法 expression.js //向Parser類 添加有關 ExpressionParser 的方法 location.js //向Parser類 添加有關 LocationParser 的方法 scope.js //向Parser類 添加處理scope的方法 identifier.js locutil.js node.js options.js tokencontext.js tokenize.js tokentype.js util.js whitespace.js

  雖然內部實現基本是類似的,有很多連方法名都是一致的(注釋中使用的類名在acorn中并沒有實現,只是表示具有某種功能的方法的集合),但是在具體實現上,acorn不可謂不暴力,連多余的目錄都沒有,所有文件全在src目錄下,其中值得一提的是它并沒有使用繼承的方式,而是使用了對象擴展的方式來實現的Parser類,如下圖:

在具體的文件中,直接擴展Paser的prototype

  

  沒想到筆者之前戲談的mixin的方式真的就這樣被使用了,然而mixin的可讀性一定程度上還要差,經歷過類似ReactComponentWithPureRenderMixin的同學想必印象尤深。

  不過話說回來,acorn內部實現與babylon并無二致,連調用的方法名都是類似的,不過acorn多實現了一個scope的概念,用于限制作用域。

  緊接著我們來看一下acorn生成的結果,以“x ** y”為例:

  

{type: "Program",body: [{type: "ExpressionStatement",expression: {type: "BinaryExpression",left: {type: "Identifier",name: "x",loc: {start: {line: 1,column: 0},end: {line: 1,column: 1}}},operator: "**",right: {type: "Identifier",name: "y",loc: {start: {line: 1,column: 5},end: {line: 1,column: 6}}},loc: {start: {line: 1,column: 0},end: {line: 1,column: 6}}},loc: {start: {line: 1,column: 0},end: {line: 1,column: 6}}}],loc: {start: {line: 1,column: 0},end: {line: 1,column: 6}} }, {ecmaVersion: 7,locations: true }

可以看出,大部分內容依然是位置信息,我們照例去掉它們:

{type: "Program",body: [{type: "ExpressionStatement",expression: {type: "BinaryExpression",left: {type: "Identifier",name: "x",},operator: "**",right: {type: "Identifier",name: "y",}}}] }

  除去一些參數上的不同,最大的區別可能就是最外層babylon還有一個File節點,而acorn的根節點就是program了,畢竟babel和webpack的工作場景還是略有區別的。

  也許,僅聽筆者講述一切都那么簡單,然而這只是理想情況,現實的復雜遠超我們的想象,簡單的舉個印象比較深的例子,在兩個parser都有有關whitespace的抽象,主要是用于提供一些匹配換行符的正則,通常都想到的是:

/\r\n?|\n/

但實際上完整的卻是

/\r\n?|\n|\u2028|\u2029/

而且考慮到ASCII碼的情況,還需要很糾結的枚舉出非空格的情況

/[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/

  因為parse處理的是我們實際開發中自己coding的代碼,不同的人不同的風格,會有怎么樣的奇怪的方式其實是非??简炌陚湫运季S的一項工作,而且這往往比我們日常的業務工作的場景更為復雜,它很多時候甚至是接近一個可能性的全集,而并非“大概率可能”的一個集合。雖然我們日常工作這種parser幾乎是透明的,我們在init的前端項目時基本已經部署好了開發環境,但是對于某些情況下的實際問題定位,卻又有非凡的意義,而且,這還在一定時間內是一個常態,雖然可能在不久的未來,就會有更加智能更加強大的前端IDE。

  有關ast的實驗,可以試一下這個網站:https://astexplorer.net/

轉載于:https://www.cnblogs.com/mfoonirlee/p/7054939.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的【js】JavaScript parser实现浅析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 人人亚洲 | 亚洲人人精品 | 一区二区播放 | 亚洲免费二区 | 国产又粗又猛又爽又黄的视频一 | 鬼灭之刃柱训练篇在线观看 | 久久五月综合 | 欧美亚洲丝袜 | 日韩国产免费 | 极品尤物一区二区三区 | 国产亚州av| 欧美色香蕉 | jizz国产 | 久久成人精品视频 | 日韩欧美一级视频 | a一级网站 | 国产草草浮力影院 | 法国空姐在线观看视频 | 国产aⅴ无码片毛片一级一区2 | 日韩视频一区二区三区四区 | 亚洲国产成人精品视频 | av综合久久 | 1769国产精品视频 | 日韩激情一区二区 | 日本一区二区三区四区视频 | 一区二区三区四区国产精品 | 激情啪啪网 | 男人插入女人下面的视频 | 亚洲国产精品网站 | 国产免费久久 | 欧美天天色 | 伊人久久在线 | 日韩毛毛片| 少妇精品亚洲一区二区成人 | 九九热在线视频 | 羞羞网站在线观看 | 欧美激情中文字幕 | 欧美福利视频在线观看 | www.youjizz.com日本| 亚洲乱码视频在线观看 | 麻豆精品在线视频 | 国产xxxxx| 四色永久访问 | 正在播放adn156松下纱荣子 | 伊人快播| 一区二区中文字幕在线观看 | xxx日本黄色 | 国产一区二区精品在线 | 视频在线观看电影完整版高清免费 | 日本在线免费视频 | 精品少妇一区二区三区 | 天堂а√在线中文在线新版 | 精品视频一区二区三区四区五区 | 2021av在线| 日本国产高清 | 午夜裸体性播放 | 亚洲综合少妇 | 国产尤物av尤物在线看 | 欧美人妻少妇一区二区三区 | 爽爽免费视频 | 手机看片91 | 久久人| 美女啪啪一区二区 | 777av| 国产美女一级视频 | 亚洲精品中文无码AV在线播放 | 中日韩午夜理伦电影免费 | 久久99一区 | 国产欧美日韩综合精品一区二区三区 | 亚洲av无码成人精品国产 | 国产精品久久久久一区二区 | 国产一国产精品一级毛片 | 亚洲色图激情 | 观看av| 夫妻性生活黄色大片 | 日韩在线一区二区三区四区 | 裸体视频软件 | 艳妇乳肉豪妇荡乳av无码福利 | 国产免费视频 | 久久经典 | www色综合 | 极品国产在线 | 少妇特黄a一区二区三区88av | 色一情一乱一乱一区91av | 正在播放一区二区 | 一区二区久久久 | 欧美性在线视频 | 中文字幕乱码一区二区 | 日本不卡网站 | 日韩中文字幕一区二区三区四区 | 青青草在线视频免费观看 | 亚洲激情综合 | 欧洲美女粗暴牲交免费观看 | 热99在线| 成人国产 | 激情五月在线 | 国产精品熟女视频 | 久久久久久久人妻无码中文字幕爆 | 亚洲精品高潮久久久久久久 |