自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

你怎么可以不了解 AST 呢?

開(kāi)發(fā) 前端
我們編寫(xiě)業(yè)務(wù)代碼的時(shí)候,可能很少人會(huì)使用到AST,以至于大多數(shù)同學(xué)都不大了解AST。本文將通過(guò)以下幾個(gè)方面對(duì)AST進(jìn)行學(xué)習(xí)。

[[410644]]

前言

我們編寫(xiě)業(yè)務(wù)代碼的時(shí)候,可能很少人會(huì)使用到AST,以至于大多數(shù)同學(xué)都不大了解AST。有的同學(xué)曾經(jīng)學(xué)過(guò),但是不去實(shí)踐的話(huà),過(guò)段時(shí)間又忘的差不多了。看到這里,你會(huì)發(fā)現(xiàn)說(shuō)的就是你。聽(tīng)說(shuō)貴圈現(xiàn)在寫(xiě)文章都要編故事,時(shí)不時(shí)還要整點(diǎn)表情包。這是真的嗎?作為公司最頭鐵的前端,我就不放。

本文將通過(guò)以下幾個(gè)方面對(duì)AST進(jìn)行學(xué)習(xí)

1.基礎(chǔ)知識(shí)

  • AST是什么
  • AST有什么用
  • AST如何生成

2.實(shí)戰(zhàn)小例子

  • 去掉debugger
  • 修改函數(shù)中執(zhí)行的console.log參數(shù)

3.總結(jié)

基礎(chǔ)知識(shí)

AST是什么先貼下官方的解釋

  • 在計(jì)算機(jī)科學(xué)中,抽象語(yǔ)法樹(shù)(abstract syntax tree 或者縮寫(xiě)為 AST),或者語(yǔ)法樹(shù)(syntax tree),是源代碼的抽象語(yǔ)法結(jié)構(gòu)的樹(shù)狀表現(xiàn)形式,這里特指編程語(yǔ)言的源代碼。

為了方便大家理解抽象語(yǔ)法樹(shù),來(lái)看看具體的例子。

  1. var tree = 'this is tree' 

js 源代碼將會(huì)被轉(zhuǎn)化成下面的抽象語(yǔ)法樹(shù)

  1.   "type""Program"
  2.   "start": 0, 
  3.   "end": 25, 
  4.   "body": [ 
  5.     { 
  6.       "type""VariableDeclaration"
  7.       "start": 0, 
  8.       "end": 25, 
  9.       "declarations": [ 
  10.         { 
  11.           "type""VariableDeclarator"
  12.           "start": 4, 
  13.           "end": 25, 
  14.           "id": { 
  15.             "type""Identifier"
  16.             "start": 4, 
  17.             "end": 8, 
  18.             "name""tree" 
  19.           }, 
  20.           "init": { 
  21.             "type""Literal"
  22.             "start": 11, 
  23.             "end": 25, 
  24.             "value""this is tree"
  25.             "raw""'this is tree'" 
  26.           } 
  27.         } 
  28.       ], 
  29.       "kind""var" 
  30.     } 
  31.   ], 
  32.   "sourceType""module" 

可以看到一條語(yǔ)句由若干個(gè)詞法單元組成。這個(gè)詞法單元就像 26 個(gè)字母。創(chuàng)造出個(gè)十幾萬(wàn)的單詞,通過(guò)不同單詞的組合又能寫(xiě)出不同內(nèi)容的文章。

至于有哪些詞法單元可點(diǎn)擊查看AST 對(duì)象文檔 或者 參考掘金大佬的文章高級(jí)前端基礎(chǔ)-JavaScript 抽象語(yǔ)法樹(shù) AST里面列舉了語(yǔ)法樹(shù)節(jié)點(diǎn)與解釋。

推薦一個(gè)工具 在線 ast 轉(zhuǎn)換器。可以在這個(gè)網(wǎng)站上,親自嘗試下轉(zhuǎn)換。點(diǎn)擊語(yǔ)句中的詞,右邊的抽象語(yǔ)法樹(shù)節(jié)點(diǎn)便會(huì)被選中,如下圖:

AST 有什么用

  • IDE 的錯(cuò)誤提示、代碼格式化、代碼高亮、代碼自動(dòng)補(bǔ)全等
  • JSLint、JSHint 對(duì)代碼錯(cuò)誤或風(fēng)格的檢查等
  • webpack、rollup 進(jìn)行代碼打包等
  • CoffeeScript、TypeScript、JSX 等轉(zhuǎn)化為原生 Javascript.
  • vue 模板編譯、react 模板編譯

AST 如何生成

看到這里,你應(yīng)該已經(jīng)知道抽象語(yǔ)法樹(shù)大致長(zhǎng)什么樣了。那么AST又是如何生成的呢?

AST 整個(gè)解析過(guò)程分為兩個(gè)步驟

  • 詞法分析 (Lexical Analysis):掃描輸入的源代碼字符串,生成一系列的詞法單元 (tokens)。這些詞法單元包括數(shù)字,標(biāo)點(diǎn)符號(hào),運(yùn)算符等。詞法單元之間都是獨(dú)立的,也即在該階段我們并不關(guān)心每一行代碼是通過(guò)什么方式組合在一起的。
  • 語(yǔ)法分析 (Syntax Analysis):建立分析語(yǔ)法單元之間的關(guān)系

還是以上面var tree = 'this is tree'為例

正規(guī)理解

詞法分析

先經(jīng)過(guò)詞法分析,掃描輸入的源代碼字符串,生成一系列的詞法單元 (tokens)。這些詞法單元包括數(shù)字,標(biāo)點(diǎn)符號(hào),運(yùn)算符等

 

語(yǔ)法分析

語(yǔ)法分析階段就會(huì)將上一階段生成的 tokens 列表轉(zhuǎn)換為如下圖所示的 AST(我把start、end字段去掉了不用在意)

非正規(guī)理解

鄭重聲明:我周某人語(yǔ)文很少及格,大致意思能理解就好。

例子:"它是豬。"

詞法分析

先經(jīng)過(guò)詞法分析,掃描輸入的源代碼字符串,生成一系列的詞法單元 (tokens)。這些詞法單元包括數(shù)字,標(biāo)點(diǎn)符號(hào),運(yùn)算符等

語(yǔ)法分析

語(yǔ)法分析階段就會(huì)將上一階段生成的 tokens 列表轉(zhuǎn)換為如下圖所示的 AST

JsParser

JavaScript Parser,把 js 源碼轉(zhuǎn)化為抽象語(yǔ)法樹(shù)的解析器。

  • acorn
  • esprima
  • traceur
  • @babel/parser

實(shí)戰(zhàn)小例子

例子 1:去 debugger

源代碼:

  1. function fn() { 
  2.   console.log('debugger'
  3.   debugger; 

根據(jù)前面學(xué)過(guò)的知識(shí)點(diǎn),我們先腦海中意淫下如何去掉這個(gè)debugger

  1. 先將源代碼轉(zhuǎn)化成AST
  2. 遍歷**AST**上的節(jié)點(diǎn),找到**debugger**節(jié)點(diǎn),并刪除
  3. 將轉(zhuǎn)換過(guò)的AST再生成JS代碼

將源代碼拷貝到 在線 ast 轉(zhuǎn)換器 中,并點(diǎn)擊左邊區(qū)域的debugger,可以看到左邊的debugger節(jié)點(diǎn)就被選中了。所以只要把圖中選中的debugger抽象語(yǔ)法樹(shù)節(jié)點(diǎn)刪除就行了。

這個(gè)例子比較簡(jiǎn)單,直接上代碼。

這個(gè)例子我使用@babel/parser、@babel/traverse、@babel/generator,它們的作用分別是解析、轉(zhuǎn)換、生成。

  1. const parser = require('@babel/parser'); 
  2. const traverse = require("@babel/traverse"); 
  3. const generator = require("@babel/generator"); 
  4.  
  5. // 源代碼 
  6. const code = ` 
  7. function fn() { 
  8.   console.log('debugger'
  9.   debugger; 
  10. `; 
  11.  
  12. // 1. 源代碼解析成 ast 
  13. const ast = parser.parse(code); 
  14.  
  15.  
  16. // 2. 轉(zhuǎn)換 
  17. const visitor = { 
  18.   // traverse 會(huì)遍歷樹(shù)節(jié)點(diǎn),只要節(jié)點(diǎn)的 type 在 visitor 對(duì)象中出現(xiàn),變化調(diào)用該方法 
  19.   DebuggerStatement(path) { 
  20.     // 刪除該抽象語(yǔ)法樹(shù)節(jié)點(diǎn) 
  21.     path.remove(); 
  22.   } 
  23. traverse.default(ast, visitor); 
  24.  
  25. // 3. 生成 
  26. const result = generator.default(ast, {}, code); 
  27.  
  28. console.log(result.code) 
  29.  
  30. // 4. 日志輸出 
  31.  
  32. // function fn() { 
  33. //   console.log('debugger'); 
  34. // } 

babel核心邏輯處理都在visitor里。traverse會(huì)遍歷樹(shù)節(jié)點(diǎn),只要節(jié)點(diǎn)的type在visitor對(duì)象中出現(xiàn),便會(huì)調(diào)用該type對(duì)應(yīng)的方法,在方法中調(diào)用path.remove()將當(dāng)前節(jié)點(diǎn)刪除。demo中使用到的path的一些api可以參考babel-handbook。

例子 2:修改函數(shù)中執(zhí)行的 console.log 參數(shù)

我們有時(shí)候在函數(shù)里打了日志,但是又想在控制臺(tái)直觀的看出是哪個(gè)函數(shù)中打的日志,這個(gè)時(shí)候就可以使用AST,去解析、轉(zhuǎn)換、生成最后想要的代碼。

源代碼:

  1. function funA() { 
  2.  console.log(1) 
  3.  
  4. // 轉(zhuǎn)換成 
  5.  
  6. function funA() { 
  7.  console.log('from function funA:', 1) 

在編碼之前,我們先理清思路,再下手也不遲。這個(gè)時(shí)候就需要借助 在線 ast 轉(zhuǎn)換器來(lái)分析了。

通過(guò)工具發(fā)現(xiàn)想要實(shí)現(xiàn)這個(gè)案例只需要往arguments前面插入段節(jié)點(diǎn)就可以了。

這里也像例子 1 一樣先梳理下思路

  1. 使用 @babel/parser 將源代碼解析成 ast
  2. 監(jiān)聽(tīng) @babel/traverse 遍歷到 CallExpression
  3. 觸發(fā)后,判斷如果執(zhí)行的方法是 console.log 時(shí),往 arguments unshift一個(gè) StringLiteral
  4. 將轉(zhuǎn)換后的 ast 生成代碼

將 js 代碼解析成 ast 與 將 ast 生成 js 代碼與去 debugger 例子一致,這里將不再描述。

首先監(jiān)聽(tīng)CallExpression遍歷

  1. const visitor = { 
  2.   CallExpression(path) { 
  3.     // console.log(path) 
  4.   } 

觀察 在線 ast 轉(zhuǎn)換器 解析后的樹(shù),我們只要判斷path 的 callee中存在對(duì)象 console 及屬性 property 。就可以往當(dāng)前的 path 的 arguments unshift 一個(gè) StringLiteral

這里的 types 對(duì)象是使用了一個(gè)新包 @babel/types , 用來(lái)判斷類(lèi)型。

上面用到的isMemberExpression,isIdentifier,getFunctionParent,stringLiteral都可以在babel-handbook文檔中找到,本文就不解釋了。

  1. const visitor = { 
  2.   // 當(dāng)遍歷到 CallExpression 時(shí)候觸發(fā) 
  3.   CallExpression(path) { 
  4.     const callee = path.node.callee; 
  5.     // 判斷當(dāng)前當(dāng)前執(zhí)行的函數(shù)是否是組合表達(dá)式 
  6.     if (types.isMemberExpression(callee)) { 
  7.       const { object, property } = callee; 
  8.       if (types.isIdentifier(object, { name'console' }) && types.isIdentifier(property, { name'log' })) { 
  9.         // 查找最接近的父函數(shù)或程序 
  10.         const parent = path.getFunctionParent(); 
  11.         const parentFunName = parent.node.id.name
  12.         path.node.arguments.unshift(types.stringLiteral(`from function ${parentFunName}`)) 
  13.       } 
  14.     } 
  15.   } 

總結(jié)

就像前言所說(shuō)的,我們的日常工作中很少會(huì)去使用AST,以至于大多數(shù)同學(xué)都不大了解AST。但了解了 AST 可以幫助我們更好地理解開(kāi)發(fā)工具、編譯器的原理,并產(chǎn)出提高代碼效率的工具。還記得我在之前的前端小組遇到一個(gè)問(wèn)題,我們項(xiàng)目是SSR項(xiàng)目,在服務(wù)端執(zhí)行的生命周期不允許出現(xiàn)客戶(hù)端才能執(zhí)行的代碼。但是小組成員有時(shí)候無(wú)意的寫(xiě)了,導(dǎo)致服務(wù)端渲染降級(jí)。在學(xué)習(xí)AST之前,我為了解決這個(gè)問(wèn)題,寫(xiě)了個(gè)loader通過(guò)正則去匹配校驗(yàn),當(dāng)時(shí)可真是逼死我了,正則需要去適配各種場(chǎng)景。后面我學(xué)習(xí)了AST了之后,編寫(xiě)了個(gè)eslint插件實(shí)現(xiàn)了客戶(hù)端代碼校驗(yàn)。

參考

  • babel-handbook(https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md)
  • 深入 Babel,這一篇就夠了(https://juejin.im/post/6844903746804137991)
  • 高級(jí)前端基礎(chǔ)-JavaScript 抽象語(yǔ)法樹(shù) AST(https://juejin.cn/post/6844903798347939853#heading-12)

 

責(zé)任編輯:姜華 來(lái)源: 微醫(yī)大前端技術(shù)
相關(guān)推薦

2011-03-29 15:44:41

對(duì)日軟件外包

2019-11-21 15:08:13

DevOps云計(jì)算管理

2019-04-03 09:10:35

Rediskey-value數(shù)據(jù)庫(kù)

2010-08-19 10:12:34

路由器標(biāo)準(zhǔn)

2017-12-26 11:37:32

云原生CNCF容器

2013-11-11 10:07:43

靜態(tài)路由配置

2018-07-16 09:00:32

LinuxBash數(shù)組

2015-06-05 09:52:41

公有云風(fēng)險(xiǎn)成本

2017-03-13 17:25:00

移動(dòng)支付技術(shù)支撐易寶

2019-05-14 14:51:40

Java語(yǔ)法糖用法

2023-02-16 07:38:38

非機(jī)械鍵電路薄膜,

2020-11-30 06:27:35

Java泛型Object

2020-09-16 07:59:40

數(shù)組內(nèi)存

2020-04-20 10:55:57

大數(shù)據(jù)人工智能技術(shù)

2021-01-14 08:31:54

Web開(kāi)發(fā)應(yīng)用程序

2012-02-21 09:20:50

Hadoop大數(shù)據(jù)

2010-07-27 09:00:32

MySQL鎖

2020-12-10 08:13:15

ARM架構(gòu) 嵌入式

2023-02-12 21:54:32

架構(gòu)AI元宇宙

2017-11-17 18:43:38

云技術(shù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)