Babel(抽象語法樹,又稱AST)

文章:http://www.javashuo.com/article/p-xwepwyup-hm.htmljavascript

          https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md  java

  1. 你瞭解過Babel嗎?

瞭解過抽象語法樹,又稱AST,有學習過,也寫過一個基於AST的乞丐版模板引擎,先是詞法解析token,而後生產抽象語法樹,而後更改抽象語法樹,固然這是插件作的事情,最後根據新的AST生成代碼。node

  1. 寫過Babel插件嗎

沒有,只是看過相關文檔git

  1. 若是讓你寫一個插件,你能寫的出來嗎?

應該能夠吧...github

遂卒....web

開玩笑的,既然提到了,又沒回答上來什麼,哎喲我這暴脾氣,一想到今晚就睡不着,連夜把它擼了。面試

那麼咱們來從零寫個插件吧。bash

寫一個預計算簡單表達式的插件babel

預覽

Before:post

const result = 1 + 2 + 3 + 4 + 5; 

After:

const result = 15; 

以上的例子可能你們不會常常遇到,由於傻x纔會這麼寫,可是有可能你會這麼寫

setTimeout(function(){ // do something }, 1000 * 2) // 插件要作的事,就是把 1000 * 2 替換成 2000 

前提條件

開工

再寫代碼以前,你須要明白Babel它的原理,簡單點說: Babel解析成AST,而後插件更改AST,最後由Babel輸出代碼

那麼Babel的插件模塊須要你暴露一個function,function內返回visitor

module.export = function(babel){ return { visitor:{ } } } 

visitor是對各種型的AST節點作處理的地方,那麼咱們怎麼知道Babel生成了的AST有哪些節點呢?

很簡單,你能夠把Babel轉換的結果打印出來,或者這裏有傳送門: AST explorer

 

1

 

這裏咱們看到 const result = 1 + 2中的1 + 1是一個BinaryExpression節點,那麼在visitor中,咱們就處理這個節點

var babel = require('babel-core'); var t = require('babel-types'); const visitor = { BinaryExpression(path) { const node = path.node; let result; // 判斷表達式兩邊,是否都是數字 if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) { // 根據不一樣的操做符做運算 switch (node.operator) { case "+": result = node.left.value + node.right.value; break case "-": result = node.left.value - node.right.value; break; case "*": result = node.left.value * node.right.value; break; case "/": result = node.left.value / node.right.value; break; case "**": let i = right; while (--i) { result = result || node.left.value; result = result - node.left.value; } break; default: } } // 若是上面的運算有結果的話 if (result !== undefined) { // 把表達式節點替換成number字面量 path.replaceWith(t.numericLiteral(result)); } } }; module.exports = function (babel) { return { visitor }; } 

插件寫好了,咱們運行下插件試試

const babel = require("babel-core"); const result = babel.transform("const result = 1 + 2;",{ plugins:[ require("./index") ] }); console.log(result.code); // const result = 3; 

與預期一致,那麼轉換 const result = 1 + 2 + 3 + 4 + 5;呢?

結果是: const result = 3 + 3 + 4 + 5;

這就奇怪了,爲何只計算了1 + 2以後,就沒有繼續往下運算了嗎?

咱們看一下這個表達式的AST樹

 

2

 

你會發現Babel解析成表達式裏面再嵌套表達式。

表達式( 表達式( 表達式( 表達式(1 + 2) + 3) + 4) + 5)

而咱們的判斷條件並不符合全部的,只符合1 + 2

// 判斷表達式兩邊,是否都是數字 if (t.isNumericLiteral(node.left) && t.isNumericLiteral(node.right)) {} 

那麼咱們得改一改

第一次計算1 + 2以後,咱們會獲得這樣的表達式

表達式( 表達式( 表達式(3+ 3) + 4) + 5)

其中 3 + 3又符合了咱們的條件, 咱們經過向上遞歸的方式遍歷父級節點

// 若是上面的運算有結果的話 if (result !== undefined) { // 把表達式節點替換成number字面量 path.replaceWith(t.numericLiteral(result)); let parentPath = path.parentPath; // 向上遍歷父級節點 parentPath && visitor.BinaryExpression.call(this, parentPath); } 

到這裏,咱們就得出告終果 const result = 15;

那麼其餘運算呢:

const result = 100 + 10 - 50 >>> const result = 60;

const result = (100 / 2) + 50 >>> const result = 100;

const result = (((100 / 2) + 50 * 2) / 50) ** 2 >>> const result = 9;

完結

到這裏,已經向你大概的講解了,若是編寫一個Babel插件,不再怕面試官問我答不出什麼了哈...

你覺得這就完了嗎?

並無

若是轉換這樣呢: const result = 0.1 + 0.2;

預期確定是0.3, 可是實際上,Javascript有浮點計算偏差,得出的結果是0.30000000000000004

那是否是這個插件就沒卵用?

這就須要你去矯正浮點運算偏差了,能夠使用Big.js;

好比: result = node.left.value + node.right.value; 改爲 result = +new Big(node.left.value).plus(node.right.value);

你覺得完了嗎? 這個插件還能夠作不少

好比: Math.PI * 2 >>> 6.283185307179586

好比: Math.pow(2,2) >>> 4

...

...

最後上項目地址: github.com/axetroy/bab…

做者:Axetroy 連接:https://juejin.im/post/5a9315e46fb9a0633a711f25 來源:掘金 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索