<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>

          【JS】992- JavaScript 常見 AST 梳理

          共 5626字,需瀏覽 12分鐘

           ·

          2021-06-19 14:03

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

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

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

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

          常見的 AST 節(jié)點

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

          Literal

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

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

          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í)行的代碼,都是語句。

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

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

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

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

          Declaration

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

          比如下面這些聲明語句:

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

          import d from 'e';

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

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

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

          Expression

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

          下面是一些常見的表達式

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

          它們對應的AST如圖:

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

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

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

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

          function(){};
          class{}

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

          a = function({}
          b = class{}

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

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

          Class

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

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

          比如下面的代碼

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

          對應的AST是這樣的

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

          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 種語法都對應 ImportDeclaration 節(jié)點,但是 specifiers 屬性不同,分別對應 ImportSpicifier、ImportDefaultSpecifier、ImportNamespaceSpcifier。

          圖中黃框標出的是 specifier 部分??梢灾庇^的看出整體結構相同,只是specifier 部分不同,所以 import 語法的 AST 的結構是ImportDeclaration 包含著各種 import specifier。

          export

          export 也有3種語法:

          named export:

          export { b, d};

          default export:

          export default a;

          all export:

          export * from 'c';

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

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

          比如這三種 export

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

          對應的 AST 節(jié)點為

          import 和 export 是語法級別的模塊化實現(xiàn),也是經常會操作的 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é)點。

          注釋分為塊注釋和行內注釋,對應 CommentBlock 和 CommentLine 節(jié)點。

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

          AST 可視化查看工具

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

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

          AST 的公共屬性

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

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

          總結

          這一節(jié)我們學習了代碼中常見的語法在 babel 的 AST 中對應的節(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é)點有不同的屬性來存放各自對應的源碼內容,但是都有一些公共屬性如 type、xxComments、loc 等。

          學會了 AST,就可以把對代碼的操作轉為對 AST 的操作了。

          1. JavaScript 重溫系列(22篇全)
          2. ECMAScript 重溫系列(10篇全)
          3. JavaScript設計模式 重溫系列(9篇全)
          4. 正則 / 框架 / 算法等 重溫系列(16篇全)
          5. Webpack4 入門(上)|| Webpack4 入門(下)
          6. MobX 入門(上) ||  MobX 入門(下)
          7. 120+篇原創(chuàng)系列匯總

          回復“加群”與大佬們一起交流學習~

          點擊“閱讀原文”查看 120+ 篇原創(chuàng)文章

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  在线观看无码高清 | 天天看天天摸 | 国产区一二| 操美女视频网站 | 精品一区二区久久久久久久网站 |