<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JavaScript 常見(jiàn) AST 梳理

          共 5375字,需瀏覽 11分鐘

           ·

          2021-06-19 13:44

          這是掘金小冊(cè)《babel 插件通關(guān)秘籍》的第三節(jié)(試讀章節(jié))。

          babel 編譯的第一步是把源碼 parse 成抽象語(yǔ)法樹 AST (Abstract Syntax Tree),后續(xù)對(duì)這個(gè) AST 進(jìn)行轉(zhuǎn)換。(之所以叫抽象語(yǔ)法樹是因?yàn)槭÷缘袅嗽创a中的分隔符、注釋等內(nèi)容)

          AST 也是有標(biāo)準(zhǔn)的,JS parser 的 AST 大多是 estree 標(biāo)準(zhǔn),從 SpiderMonkey 的 AST 標(biāo)準(zhǔn)擴(kuò)展而來(lái)。babel 的整個(gè)編譯流程都是圍繞 AST 來(lái)的,這一節(jié)我們來(lái)學(xué)一下 AST。

          熟悉了 AST,也就是知道轉(zhuǎn)譯器和 JS 引擎是怎么理解代碼的,對(duì)深入掌握 Javascript 也有很大的好處。

          常見(jiàn)的 AST 節(jié)點(diǎn)

          AST 是對(duì)源碼的抽象,字面量、標(biāo)識(shí)符、表達(dá)式、語(yǔ)句、模塊語(yǔ)法、class 語(yǔ)法 都有各自的 AST。我們分別來(lái)了解一下:

          Literal

          Literal 是字面量的意思,比如 let name = 'guang'中,'guang'就是一個(gè)字符串字面量 StringLiteral,相應(yīng)的還有 數(shù)字字面量 NumericLiteral,布爾字面量 BooleanLiteral,字符串字面量 StringLiteral,正則表達(dá)式字面量 RegExpLiteral 等。

          代碼中的字面量很多,babel 就是通過(guò) xxLiteral 來(lái)抽象這部分內(nèi)容的。

          Identifier

          Identifer 是標(biāo)識(shí)符的意思,變量名、屬性名、參數(shù)名等各種聲明和引用的名字,都是Identifer。我們知道,JS 中的標(biāo)識(shí)符只能包含字母或數(shù)字或下劃線(“_”)或美元符號(hào)(“$”),且不能以數(shù)字開(kāi)頭。這是 Identifier 的詞法特點(diǎn)。

          來(lái)嘗試分析一下,下面這一段代碼里面有多少 Identifier 呢?

          const name = 'guang';

          function say(name{
            console.log(name);
          }

          const obj = {
            name'guang'
          }

          答案是這些

          Identifier 是變量和變量的引用,代碼中也是隨處可見(jiàn)。

          Statement

          statement 是語(yǔ)句,它是可以獨(dú)立執(zhí)行的單位,比如 break,continue,debugger,return 或者 if 語(yǔ)句、while 語(yǔ)句、for 語(yǔ)句,還有聲明語(yǔ)句,表達(dá)式語(yǔ)句等。我們寫的每一條可以獨(dú)立執(zhí)行的代碼,都是語(yǔ)句。

          語(yǔ)句末尾一般會(huì)加一個(gè)分號(hào)分隔,或者用換行分隔。

          下面這些我們經(jīng)常寫的代碼,每一行都是一個(gè) Statement:

          break;
          continue;
          return;
          debugger;
          throw Error();
          {}
          try {} catch(e) {} finally{}
          for (let key in obj) {}
          for (let i = 0;i < 10;i ++) {}
          while (true) {}
          do {} while (true)
          switch (v){case 1break;default:;}
          labelconsole.log();
          with (a){}

          他們對(duì)應(yīng)的 AST 節(jié)點(diǎn)如圖所示

          語(yǔ)句是代碼執(zhí)行的最小單位,可以說(shuō),代碼是由語(yǔ)句(Statement)構(gòu)成的。

          Declaration

          聲明語(yǔ)句是一種特殊的語(yǔ)句,它執(zhí)行的邏輯是在作用域內(nèi)聲明一個(gè)變量、函數(shù)、class、import、export 等。

          比如下面這些聲明語(yǔ)句:

          const a = 1;
          function b(){}
          class C {}

          import d from 'e';

          export default e = 1;
          export {e};
          export * from 'e';

          他們對(duì)應(yīng)的 AST 節(jié)點(diǎn)如圖:

          聲明語(yǔ)句用于定義變量,變量聲明也是代碼中一個(gè)基礎(chǔ)的部分。

          Expression

          expression 是表達(dá)式,特點(diǎn)是執(zhí)行完以后有返回值,這是和語(yǔ)句 (statement) 的區(qū)別。

          下面是一些常見(jiàn)的表達(dá)式

          [1,2,3]
          a = 1
          1 + 2;
          -1;
          function(){};
          () => {};
          class{};
          a;
          this;
          super;
          a::b;

          它們對(duì)應(yīng)的AST如圖:

          細(xì)心的同學(xué)可能會(huì)問(wèn) identifier 和 super 怎么也是表達(dá)式呢?

          其實(shí)有的節(jié)點(diǎn)可能會(huì)是多種類型,identifier、super 有返回值,符合表達(dá)式的特點(diǎn),所以也是 expression。

          我們判斷 AST 節(jié)點(diǎn)是不是某種類型要看它是不是符合該種類型的特點(diǎn),比如語(yǔ)句的特點(diǎn)是能夠單獨(dú)執(zhí)行,表達(dá)式的特點(diǎn)是有返回值。

          有的表達(dá)式可以單獨(dú)執(zhí)行,符合語(yǔ)句的特點(diǎn),所以也是語(yǔ)句,比如賦值表達(dá)式、數(shù)組表達(dá)式等,但有的表達(dá)式不能單獨(dú)執(zhí)行,需要和其他類型的節(jié)點(diǎn)組合在一起構(gòu)成語(yǔ)句。比如匿名函數(shù)表達(dá)式和匿名 class 表達(dá)式單獨(dú)執(zhí)行會(huì)報(bào)錯(cuò)

          function(){};
          class{}

          需要和其他部分一起構(gòu)成一條語(yǔ)句,比如組成賦值語(yǔ)句

          a = function({}
          b = class{}

          表達(dá)式語(yǔ)句解析成 AST 的時(shí)候會(huì)包裹一層 ExpressionStatement 節(jié)點(diǎn),代表這個(gè)表達(dá)式是被當(dāng)成語(yǔ)句執(zhí)行的。

          小結(jié):表達(dá)式的特點(diǎn)是有返回值,有的表達(dá)式可以獨(dú)立作為語(yǔ)句執(zhí)行,會(huì)包裹一層 ExpressionStatement。

          Class

          class 的語(yǔ)法比較特殊,有專門的 AST 節(jié)點(diǎn)來(lái)表示。

          整個(gè) class 的內(nèi)容是 ClassBody,屬性是 ClassProperty,方法是ClassMethod(通過(guò) kind 屬性來(lái)區(qū)分是 constructor 還是 method)。

          比如下面的代碼

          class Guang extends Person{
              name = 'guang';
              constructor() {}
              eat() {}
          }

          對(duì)應(yīng)的AST是這樣的

          class 是 es next 的語(yǔ)法,babel 中有專門的 AST 來(lái)表示它的內(nèi)容。

          Modules

          es module 是語(yǔ)法級(jí)別的模塊規(guī)范,所以也有專門的 AST 節(jié)點(diǎn)。

          import

          import 有 3 種語(yǔ)法:

          named import:

          import {c, d} from 'c';

          default import:

          import a from 'a';

          namespaced import:

          import * as b from 'b';

          這 3 種語(yǔ)法都對(duì)應(yīng) ImportDeclaration 節(jié)點(diǎn),但是 specifiers 屬性不同,分別對(duì)應(yīng) ImportSpicifier、ImportDefaultSpecifier、ImportNamespaceSpcifier。

          圖中黃框標(biāo)出的是 specifier 部分??梢灾庇^的看出整體結(jié)構(gòu)相同,只是specifier 部分不同,所以 import 語(yǔ)法的 AST 的結(jié)構(gòu)是ImportDeclaration 包含著各種 import specifier。

          export

          export 也有3種語(yǔ)法:

          named export:

          export { b, d};

          default export:

          export default a;

          all export:

          export * from 'c';

          分別對(duì)應(yīng) ExportNamedDeclaration、ExportDefaultDeclaration、ExportAllDeclaration 的節(jié)點(diǎn)

          其中 ExportNamedDeclaration 才有 specifiers 屬性,其余兩種都沒(méi)有這部分(也比較好理解,export 不像 import 那樣結(jié)構(gòu)類似,這三種 export 語(yǔ)法結(jié)構(gòu)是不一樣的,所以不是都包含 specifier)。

          比如這三種 export

          export { b, d};
          export default a;
          export * from 'c';

          對(duì)應(yīng)的 AST 節(jié)點(diǎn)為

          import 和 export 是語(yǔ)法級(jí)別的模塊化實(shí)現(xiàn),也是經(jīng)常會(huì)操作的 AST。

          Program & Directive

          program 是代表整個(gè)程序的節(jié)點(diǎn),它有 body 屬性代表程序體,存放 statement 數(shù)組,就是具體執(zhí)行的語(yǔ)句的集合。還有 directives 屬性,存放Directive 節(jié)點(diǎn),比如"use strict" 這種指令會(huì)使用 Directive 節(jié)點(diǎn)表示。

          Program 是包裹具體執(zhí)行語(yǔ)句的節(jié)點(diǎn),而 Directive 則是代碼中的指令部分。

          File & Comment

          babel 的 AST 最外層節(jié)點(diǎn)是 File,它有 program、comments、tokens 等屬性,分別存放 Program 程序體、注釋、token 等,是最外層節(jié)點(diǎn)。

          注釋分為塊注釋和行內(nèi)注釋,對(duì)應(yīng) CommentBlock 和 CommentLine 節(jié)點(diǎn)。

          上面 6 種就是常見(jiàn)的一些 AST 節(jié)點(diǎn)類型,babel 就是通過(guò)這些節(jié)點(diǎn)來(lái)抽象源碼中不同的部分。

          AST 可視化查看工具

          當(dāng)然,我們并不需要記什么內(nèi)容對(duì)應(yīng)什么 AST 節(jié)點(diǎn),可以通過(guò) axtexplorer.net 這個(gè)網(wǎng)站來(lái)直觀的查看。

          這個(gè)網(wǎng)站可以查看代碼 parse 以后的結(jié)果,但是如果想查看全部的 AST 可以在babel parser 倉(cāng)庫(kù)里的 AST 文檔里查,或者直接去看 @babel/types 的 typescript 類型定義。

          AST 的公共屬性

          每種 AST 都有自己的屬性,但是它們也有一些公共屬性:

          • type:AST 節(jié)點(diǎn)的類型
          • start、end、loc:start 和 end 代表該節(jié)點(diǎn)對(duì)應(yīng)的源碼字符串的開(kāi)始和結(jié)束下標(biāo),不區(qū)分行列。而 loc 屬性是一個(gè)對(duì)象,有 line 和 column 屬性分別記錄開(kāi)始和結(jié)束行列號(hào)。
          • leadingComments、innerComments、trailingComments:表示開(kāi)始的注釋、中間的注釋、結(jié)尾的注釋,因?yàn)槊總€(gè) AST 節(jié)點(diǎn)中都可能存在注釋,而且可能在開(kāi)始、中間、結(jié)束這三種位置,通過(guò)這三個(gè)屬性來(lái)記錄和 Comment 的關(guān)聯(lián)。
          • extra:記錄一些額外的信息,用于處理一些特殊情況。

          總結(jié)

          這一節(jié)我們學(xué)習(xí)了代碼中常見(jiàn)的語(yǔ)法在 babel 的 AST 中對(duì)應(yīng)的節(jié)點(diǎn)。

          我們學(xué)習(xí)了:標(biāo)識(shí)符 Identifer、各種字面量 xxLiteral、各種語(yǔ)句 xxStatement,各種聲明語(yǔ)句 xxDeclaration,各種表達(dá)式 xxExpression,以及 Class、Modules、File、Program、Directive、Comment 這些 AST 節(jié)點(diǎn)。

          了解了這些節(jié)點(diǎn),就能知道平時(shí)寫的代碼是怎么用 AST 表示的,當(dāng)然也不需要記,可以去文檔或一些工具網(wǎng)站 (astexpoler.net) 去查。

          AST 節(jié)點(diǎn)可能同時(shí)有多種類型,確定一種 AST 節(jié)點(diǎn)是什么類型主要看它的特點(diǎn),比如 Statement 的特點(diǎn)是可以單獨(dú)執(zhí)行,Expression 的特點(diǎn)是有返回值,所以一些可以單獨(dú)執(zhí)行的 Expression 會(huì)包一層 ExpressionStatement 執(zhí)行。

          不同 AST 節(jié)點(diǎn)有不同的屬性來(lái)存放各自對(duì)應(yīng)的源碼內(nèi)容,但是都有一些公共屬性如 type、xxComments、loc 等。

          學(xué)會(huì)了 AST,就可以把對(duì)代碼的操作轉(zhuǎn)為對(duì) AST 的操作了。



          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  香蕉电影伊人 | 欧美日韩激情四射 | 黄色网页大全 | 玩嫩苞综合AV | 国产三级网站 |