<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 常見 AST 梳理

          共 5375字,需瀏覽 11分鐘

           ·

          2021-07-27 21:39

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

          babel 編譯的第一步是把源碼 parse 成抽象語法樹 AST (Abstract Syntax Tree),后續(xù)對這個 AST 進行轉(zhuǎn)換。(之所以叫抽象語法樹是因為省略掉了源碼中的分隔符、注釋等內(nèi)容)

          AST 也是有標準的,JS parser 的 AST 大多是 estree 標準,從 SpiderMonkey 的 AST 標準擴展而來。babel 的整個編譯流程都是圍繞 AST 來的,這一節(jié)我們來學一下 AST。

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

          常見的 AST 節(jié)點

          AST 是對源碼的抽象,字面量、標識符、表達式、語句、模塊語法、class 語法 都有各自的 AST。我們分別來了解一下:

          Literal

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

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

          Identifier

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

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

          const name = 'guang';

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

          const obj = {
            name'guang'
          }

          答案是這些

          Identifier 是變量和變量的引用,代碼中也是隨處可見。

          Statement

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

          語句末尾一般會加一個分號分隔,或者用換行分隔。

          下面這些我們經(jīng)常寫的代碼,每一行都是一個 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){}

          他們對應(yīng)的 AST 節(jié)點如圖所示

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

          Declaration

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

          比如下面這些聲明語句:

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

          import d from 'e';

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

          他們對應(yīng)的 AST 節(jié)點如圖:

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

          Expression

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

          下面是一些常見的表達式

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

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

          細心的同學可能會問 identifier 和 super 怎么也是表達式呢?

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

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

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

          function(){};
          class{}

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

          a = function({}
          b = class{}

          表達式語句解析成 AST 的時候會包裹一層 ExpressionStatement 節(jié)點,代表這個表達式是被當成語句執(zhí)行的。

          小結(jié):表達式的特點是有返回值,有的表達式可以獨立作為語句執(zhí)行,會包裹一層 ExpressionStatement。

          Class

          class 的語法比較特殊,有專門的 AST 節(jié)點來表示。

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

          比如下面的代碼

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

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

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

          Modules

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

          import

          import 有 3 種語法:

          named import:

          import {c, d} from 'c';

          default import:

          import a from 'a';

          namespaced import:

          import * as b from 'b';

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

          圖中黃框標出的是 specifier 部分。可以直觀的看出整體結(jié)構(gòu)相同,只是specifier 部分不同,所以 import 語法的 AST 的結(jié)構(gòu)是ImportDeclaration 包含著各種 import specifier。

          export

          export 也有3種語法:

          named export:

          export { b, d};

          default export:

          export default a;

          all export:

          export * from 'c';

          分別對應(yīng) ExportNamedDeclaration、ExportDefaultDeclaration、ExportAllDeclaration 的節(jié)點

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

          比如這三種 export

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

          對應(yīng)的 AST 節(jié)點為

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

          Program & Directive

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

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

          File & Comment

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

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

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

          AST 可視化查看工具

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

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

          AST 的公共屬性

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

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

          總結(jié)

          這一節(jié)我們學習了代碼中常見的語法在 babel 的 AST 中對應(yīng)的節(jié)點。

          我們學習了:標識符 Identifer、各種字面量 xxLiteral、各種語句 xxStatement,各種聲明語句 xxDeclaration,各種表達式 xxExpression,以及 Class、Modules、File、Program、Directive、Comment 這些 AST 節(jié)點。

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

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

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

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



          瀏覽 55
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  成人毛片女人AAA久久 | 变态另类天堂 | 无码人妻精品一区二区蜜桃在 | 影音先锋日韩资源网 | 日韩婷婷五月天亚洲黄色视频 |