日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者:京東科技 周明亮

AST 基礎與功能

在前端里面有一個很重要的概念,也是最原子化的內(nèi)容,就是 AST ,幾乎所有的框架,都是基于 AST 進行改造運行,比如:React / Vue /Taro 等等。 多端的運行使用,都離不開 AST 這個概念。

在大家理解相關原理和背景后,我們可以通過手寫簡單的編譯器,簡單實現(xiàn)一個 JAVAscript 的代碼編譯器,編譯后在瀏覽器端正常運行。

創(chuàng)建數(shù)字小明,等于六加一。
創(chuàng)建數(shù)字小亮,等于七減二。
輸出,小明乘小亮。

通過實現(xiàn)一個自定義的編譯器,我們發(fā)現(xiàn)我們自己也能寫出很多新的框架。最終目標都是通過編譯轉換,翻譯為瀏覽器識別的 JavaScript + css + html。

沒錯!翻譯翻譯~

當然我們也可以以這個為基礎,去實現(xiàn)跨端的框架,直接翻譯為機器碼,跑到各種硬件上。當然一個人肯定比較困難,你會遇到各種各樣的問題需要解決,不過沒關系,只要你有好的想法,拉上一群人,你就能實現(xiàn)。

大家記得點贊,評論,收藏,一鍵三連啊~

分析器

說到這個代碼語義化操作前,我們先說說分析器,其實就是編譯原理。當你寫了一段代碼,要想讓機器知道,你寫了啥。

那機器肯定是要開始掃描,掃描每一個關鍵詞,每一個符號,我們將進行詞法分析的程序或者函數(shù)叫作詞法分析器(Lexical analyzer),通過它的掃描可以將字符序列轉換為單詞(Token)序列的過程。

掃描到了關鍵詞,我們怎么才能把它按照規(guī)則,轉換為機器認識的特定規(guī)則呢?比如你掃描到:

const a = 1

機器怎么知道要創(chuàng)建一個 變量 a 并且等于 1 呢?

所以,這時候就引入一個概念:語法分析器(Syntactic analysis,Parser)。通過語法分析器,不斷的調(diào)用詞法分析器,進行語法檢查、并構建由輸入的單詞組成的數(shù)據(jù)結構(一般是語法分析樹、抽象語法樹等層次化的數(shù)據(jù)結構)。

在JS的世界里,這個掃描后得到的數(shù)據(jù)結構 抽象語法樹 【AST】??赡芎芏嗳寺犨^這個概念,但是具體沒有深入了解。機緣巧合,剛好我需要用到這個玩意,今天就簡單聊聊。

抽象語法樹 AST

AST 是 Abstract Syntax Tree 的縮寫,也就是:抽象語法樹。在代碼的世界里,它叫這個。在語言的世界里面,他叫語法分析樹。

語言世界,舉個栗子:

我寫文章。

語法分析樹:
主語:我,人稱代詞。
謂語:寫,動詞。
賓語:文章,名詞。

長一點的可能會有:主謂賓定狀補。是不是發(fā)現(xiàn)好熟悉,想當年大家學語文和英語,那是一定要進行語法分析,方便你理解句子要表達的含義。

PS:對我來說,語法老難了?。。」蠹沂遣皇钦业礁杏X了~

接下來我們講講代碼里面的抽象語法樹。

const me = "我"
function write() {
  console.log("文章")
}

那我們用來進行語法分析,能夠得到什么內(nèi)容了?這時候我們可以借助已有的工具,將他們進行分析,進行一個初級入門。

其實我們也可以完全自己進行分析,不過這樣就不容易入門,定義的語法規(guī)則很多,如果只是看,很容易就被勸退了。而通過輔助工具,我們可以很快接受相關的概念。

常用的工具有很多,比如:Recast 、Babel、Acorn 等等

也可以使用在線 AST 解析:AST Explorer,左上角菜單可以切換到各種解析工具,并且支持各類編程語言的解析,強大好用,可以用來學習,幫助你理解 AST。

 

為了幫助大家理解,我們一點點的進行解析,并且去掉了部分屬性,留下主干部分,完整的可以通過在線工具查看?!?strong>不同解析器,對于根節(jié)點或者部分屬性稍有區(qū)別,但是本質(zhì)是一樣的。】

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "me"
          },
          "init": {
            "type": "Literal",
            "value": "我",
            "raw": ""我""
          }
        }
      ],
      "kind": "const"
    },
    {
      "type": "FunctionDeclaration",
      "id": {
        "type": "Identifier",
        "name": "write"
      },
      "params": [],
      "body": {
        "type": "BlockStatement",
        "body": [
          {
            "type": "ExpressionStatement",
            "expression": {
              "type": "CallExpression",
              "callee": {
                "type": "MemberExpression",
                "object": {
                  "type": "Identifier",
                  "name": "console"
                },
                "property": {
                  "type": "Identifier",
                  "name": "log"
                }
              },
              "arguments": [
                {
                  "type": "Literal",
                  "value": "文章",
                  "raw": ""文章""
                }
              ]
            }
          }
        ]
      }
    }
  ],
  "sourceType": "module"
}

接下來,我們一個一個節(jié)點看,首先是第一個節(jié)點 Program

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "kind": "const"
      ...
    },
    {
      "type": "FunctionDeclaration",
      "id": {
        "type": "Identifier",
        "name": "write"
      },
      ....
    }
  ],
  "sourceType": "module"
}

Program 是代碼程序的根節(jié)點,通過它進行節(jié)點一層一層的遍歷操作。 上面我們看出它有兩個節(jié)點,一個是變量聲明節(jié)點,另外一個是函數(shù)聲明節(jié)點。

如果我們再定義一個變量或者函數(shù),這時候 body 就又會產(chǎn)生一個節(jié)點。我們要掃描代碼文件時,我們就是基于 body 進行層層的節(jié)點掃描,直到把所有的節(jié)點掃描完成。

    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": {
            "type": "Identifier",
            "name": "me"
          },
          "init": {
            "type": "Literal",
            "value": "我",
            "raw": ""我""
          }
        }
      ],
      "kind": "const"
    },

上面對應的代碼,就是 const me = "我" ,這個節(jié)點告訴我們。 聲明一個變量,使用類型是:VariableDeclaration, 他的唯一標識名是:me,初始化值:"我"。

后續(xù)的函數(shù)分析,也是一樣的。

{
      "type": "FunctionDeclaration",
      "id": {
        "type": "Identifier",
        "name": "write"
      },
      "params": [],
      "body": {
        "type": "BlockStatement",
        "body": [
          {
            "type": "ExpressionStatement",
            "expression": {
              "type": "CallExpression",
              "callee": {
                "type": "MemberExpression",
                "object": {
                  "type": "Identifier",
                  "name": "console"
                },
                "property": {
                  "type": "Identifier",
                  "name": "log"
                },
              },
              "arguments": [
                {
                  "type": "Literal",
                  "value": "文章",
                  "raw": ""文章""
                }
              ],
            }
          }
        ]
      }
    }

這個節(jié)點,清楚的告訴我們,這個函數(shù)名是什么,他里面有哪些內(nèi)容,入?yún)⑹鞘裁矗{(diào)用了什么函數(shù)對象。

我們發(fā)現(xiàn),通過語法分析器的解析,我們可以把代碼,變成一個對象。這個對象將代碼分割為原子化的內(nèi)容,很容易能夠幫助機器或者我們?nèi)ダ斫馑慕M成。

這個就是分析器的作用,我們不再是一大段一大段的看代碼邏輯,而是一小段一小段的看節(jié)點。

有了這個我們可以干什么呢?

AST 在 JS 中的用途

1. 自定義語法分析器,寫一個新的框架。

通過對現(xiàn)有的 AST 理解,我們可以依葫蘆畫瓢,寫出自定義的語法分析器,轉成自定義的抽象語法樹,再進行解析轉為瀏覽器可識別的 Javascript 語言,或者其他硬件上能識別的語言。

比如:React / Vue 等等框架。其實這些框架,就是自定義了一套語法分析器,用他們特定的語言,進行轉換,翻譯翻譯,生成相關的DOM節(jié)點,操作函數(shù)等等 JS 函數(shù)。

2. 利用已有語法分析器,實現(xiàn)多端運行。

通過已有的 AST,我們將代碼進行翻譯翻譯,實現(xiàn)跨平臺多端運行。我們將得到代碼進行語法解析,通過遍歷所有的節(jié)點,我們將他們進行改造,使得它能夠運行在其他的平臺上。

比如:Taro / uni-App 等等框架。我們只要寫一次代碼,框架通過分析轉換,就可以運行到 H5 / 小程序等等相關的客戶端。

3. 進行代碼改造,預編譯增強處理。

依舊是通過已有的 AST,我們將代碼進行分析。再進行代碼混淆,代碼模塊化處理,自動進行模塊引入,低版本兼容處理。

比如:Webpack / Vite 等等打包工具。我們寫完代碼,通過他們的處理,進行增強編譯,增強代碼的健壯性。

AST 的應用實踐

我們在進行框架的改造或者適配時,我們可能才會用到這個。常規(guī)的方法,可能有兩種:

  • 按照特定的寫法,通過正則表達式,直接進行大段代碼替換。
  • /** mingliang start */ const a = 1 /** mingliang end */

如,我們找到這段代碼注釋,直接通過 code.replace(/mingliang/g, 'xxxx') 類似這種方式替換。

  • 通過引入運行,改造相關的變量,再重新寫入。
// a.js
cost config = { a: 1 }
return config

我們可能先 let config = require(a.js) 運行這個文件,我們就得到了這個 config 這個變量值。

之后我們改寫變量 config.a = 2,

最后,重新通過 fs.writeSync('a.js', 'return ' + JSON.stringify(config, null, 2)) 寫入。

現(xiàn)在,我們就可以掌握新的方法,進行代碼改造。

分享到:
標簽:javascript
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定