久久久久无码精品,四川省少妇一级毛片,老老熟妇xxxxhd,人妻无码少妇一区二区

抽象語法樹在JavaScript中的應(yīng)用

時間:2024-08-18 04:30:19 JavaScript 我要投稿
  • 相關(guān)推薦

抽象語法樹在JavaScript中的應(yīng)用

  抽象語法樹是什么?在 JavaScript 中該如何應(yīng)用?下面YJBYS小編為大家講解!

  在計算機科學(xué)中,抽象語法樹(abstract syntax tree 或者縮寫為 AST),或者語法樹(syntax tree),是源代碼的抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式,這里特指編程語言的源代碼。樹上的每個節(jié)點都表示源代碼中的一種結(jié)構(gòu)。之所以說語法是「抽象」的,是因為這里的語法并不會表示出真實語法中出現(xiàn)的每個細(xì)節(jié)。1

  果然比較抽象,不如先看幾個例子:

  抽象語法樹舉例

  foo = 'hello world';

  /*

  +-------------+

  | assign(=) |

  +-------------+

  X X

  X X

  +-------+ +-----------------+

  | foo | | 'hello world' |

  +-------+ +-----------------+

  */

  if (foo === true) {

  bar = 'hello world';

  alert(bar);

  }

  /*

  +------+

  | if |

  +------+

  X X

  X X

  +--------------+ +-------------+

  | equal(===) | | if_body |

  +--------------+ +-------------+

  X X X X

  X X X X

  +-------+ +--------+ +-------------+ +------------+

  | foo | | true | | assign(=) | | alert() |

  +-------+ +--------+ +-------------+ +------------+

  X X X

  X X X

  +-------+ +-----------------+ +-------+

  | bar | | 'hello world' | | bar |

  +-------+ +-----------------+ +-------+

  */

  從上述兩個例子可以看出,抽象語法樹是將源代碼根據(jù)其語法結(jié)構(gòu),省略一些細(xì)節(jié)(比如:括號沒有生成節(jié)點),抽象成樹形表達(dá)。

  抽象語法樹在計算機科學(xué)中有很多應(yīng)用,比如編譯器、IDE、壓縮優(yōu)化代碼等。下面介紹一下抽象語法樹在 JavaScript 中的應(yīng)用。

  JavaScript 抽象語法樹

  構(gòu)造 JavaScript 抽象語法樹有多種工具,比如 v8、SpiderMonkey、UglifyJS 等,這里重點介紹 UglifyJS。

  UglifyJS

  UglifyJS 是使用最廣的 JavaScript 壓縮工具之一,而且自身也是用 JavaScript 寫的,使用它的方法很簡單(需要 nodejs 環(huán)境):

  首先全局安裝:

  [sudo ]npm install -g uglify-js

  然后就可以使用了:

  uglifyjs -m srcFileName.js -o destFileName.min.js

  關(guān)于 UglifyJS 的用法這里就不多介紹了,我們要做的是一些更有趣的事情。

  UglifyJS Tools

  UglifyJS 提供了一些工具用于分析 JavaScript 代碼,包括:

  parser,把 JavaScript 代碼解析成抽象語法樹

  code generator,通過抽象語法樹生成代碼

  mangler,混淆 JavaScript 代碼

  scope analyzer,分析變量定義的工具

  tree walker,遍歷樹節(jié)點

  tree transformer,改變樹節(jié)點

  生成抽象語法樹

  使用 UglifyJS 生成抽象語法樹很簡單:

  首先安裝 UglifyJS 為 npm 包:

  npm install uglify-js --save-dev

  然后使用 parse 方法即可:

  var UglifyJS = require('uglify-js');var ast = UglifyJS.parse('function sum(foo, bar){ return foo + bar; }');

  這樣生成的 ast 即為那一段代碼的抽象語法樹。那么我們怎么使用呢?

  使用 mangler 壓縮代碼

  使用 mangler 可以通過將局部變量都縮短成一個字符來壓縮代碼。

  var UglifyJS = require('uglify-js');var ast = UglifyJS.parse('function sum(foo, bar){ return foo + bar; }');

  ast.figure_out_scope();

  ast.mangle_names();

  console.log(ast.print_to_string());// function sum(a,b){return a+b}

  使用 walker 遍歷抽象語法樹

  使用 walker 可以遍歷抽象語法樹,這種遍歷是深度遍歷。

  var UglifyJS = require('uglify-js');var ast = UglifyJS.parse('function sum(foo, bar){ return foo + bar; }');

  ast.figure_out_scope();

  ast.walk(new UglifyJS.TreeWalker(function(node) {

  console.log(node.print_to_string());

  }));/*

  function sum(foo,bar){return foo+bar}

  function sum(foo,bar){return foo+bar}

  sum

  foo

  bar

  return foo+bar

  foo+bar

  foo

  bar

  */

  UglifyJS 已經(jīng)提供了直接壓縮代碼的腳本,walker 看上去貌似也沒啥用,那么這些工具有什么使用場景呢?

  抽象語法樹的應(yīng)用

  利用抽象語法樹重構(gòu) JavaScript 代碼

  假如我們有重構(gòu) JavaScript 的需求,它們就派上用場啦。

  下面考慮這樣一個需求:

  我們知道,parseInt 用于將字符串變成整數(shù),但是它有第二個參數(shù),表示以幾進(jìn)制識別字符串,若沒有傳第二個參數(shù),則會自行判斷,比如:

  parseInt('10.23'); // 10 轉(zhuǎn)換成正整數(shù)parseInt('10abc'); // 10 忽略其他字符parseInt('10', 10); // 10 轉(zhuǎn)換成十進(jìn)制parseInt('10', 2); // 2 轉(zhuǎn)換成二進(jìn)制parseInt('0123'); // 83 or 123 不同瀏覽器不一樣,低版本瀏覽器會轉(zhuǎn)換成八進(jìn)制parseInt('0x11'); // 17 轉(zhuǎn)換成十六進(jìn)制

  因為有一些情況是和我們預(yù)期不同的,所以建議任何時候都加上第二個參數(shù)。

  下面希望有一個腳本,查看所有 parseInt 有沒有第二個參數(shù),沒有的話加上第二個參數(shù) 10,表示以十進(jìn)制識別字符串。

  使用 UglifyJS 可以實現(xiàn)此功能:

  #! /usr/bin/env nodevar U2 = require("uglify-js");function replace_parseint(code) {

  var ast = U2.parse(code); // accumulate `parseInt()` nodes in this array

  var parseint_nodes = [];

  ast.walk(new U2.TreeWalker(function(node){

  if (node instanceof U2.AST_Call

  && node.expression.print_to_string() === 'parseInt'

  && node.args.length === 1) {

  parseint_nodes.push(node);

  }

  })); // now go through the nodes backwards and replace code

  for (var i = parseint_nodes.length; --i >= 0;) { var node = parseint_nodes[i]; var start_pos = node.start.pos; var end_pos = node.end.endpos;

  node.args.push(new U2.AST_Number({

  value: 10

  })); var replacement = node.print_to_string({ beautify: true });

  code = splice_string(code, start_pos, end_pos, replacement);

  } return code;

  }function splice_string(str, begin, end, replacement) {

  return str.substr(0, begin) + replacement + str.substr(end);

  }// test itfunction test() {

  if (foo) { parseInt('12342');

  } parseInt('0012', 3);

  }

  console.log(replace_parseint(test.toString()));/*

  function test() {

  if (foo) {

  parseInt("12342", 10);

  }

  parseInt('0012', 3);

  }

  */

  在這里,使用了 walker 找到 parseInt 調(diào)用的地方,然后檢查是否有第二個參數(shù),沒有的話,記錄下來,之后根據(jù)每個記錄,用新的包含第二個參數(shù)的內(nèi)容替換掉原內(nèi)容,完成代碼的重構(gòu)。

  也許有人會問,這種簡單的情況,用正則匹配也可以方便的替換,干嘛要用抽象語法樹呢?

  答案就是,抽象語法樹是通過分析語法實現(xiàn)的,有一些正則無法(或者很難)做到的優(yōu)勢,比如,parseInt() 整個是一個字符串,或者在注釋中,此種情況會被正則誤判:

  var foo = 'parseInt("12345")';// parseInt("12345");

  抽象語法樹在美團(tuán)中的應(yīng)用

  在美團(tuán)前端團(tuán)隊,我們使用 YUI 作為前端底層框架,之前面臨的一個實際問題是,模塊之間的依賴關(guān)系容易出現(xiàn)疏漏。比如:

  YUI.add('mod1', function(Y) {

  Y.one('#button1').simulate('click');

  Y.Array.each(array, fn);

  Y.mod1 = function() {/**/};

  }, '', {

  requires: [ 'node', 'array-extras'

  ]

  });

  YUI.add('mod2', function(Y) {

  Y.mod1(); // Y.io(uri, config);}, '', {

  requires: [ 'mod1', 'io'

  ]

  });

  以上代碼定義了兩個模塊,其中 mod1 模擬點擊了一下 id 為 button1 的元素,執(zhí)行了 Y.Array.each,然后定義了方法 Y.mod1,最后聲明了依賴 node 和 array-extras;mod2 執(zhí)行了 mod1 中定義的方法,而 Y.io 被注釋了,最后聲明了依賴 mod1 和 io。

  此處 mod1 出現(xiàn)了兩個常見錯誤,一個是 simulate 是 Y.Node.prototype 上的方法,容易忘掉聲明依賴 node-event-simulate3,另一個是 Y.Array 上只有部分方法需要依賴 array-extras,故此處多聲明了依賴 array-extras4;mod2 中添加注釋后,容易忘記刪除原來寫的依賴 io。

  故正確的依賴關(guān)系應(yīng)該如下:

  YUI.add('mod1', function(Y) {

  Y.one('#button1').simulate('click');

  Y.Array.each(array, fn);

  Y.mod1 = function() {/**/};

  }, '', {

  requires: [ 'node', 'node-event-simulate'

  ]

  });

  YUI.add('mod2', function(Y) {

  Y.mod1(); // Y.io(uri, config);}, '', {

  requires: [ 'mod1'

  ]

  });

  為了使模塊依賴關(guān)系的檢測自動化,我們創(chuàng)建了模塊依賴關(guān)系檢測工具,它利用抽象語法樹,分析出定義了哪些接口,使用了哪些接口,然后查找這些接口應(yīng)該依賴哪些模塊,進(jìn)而找到模塊依賴關(guān)系的錯誤,大致的過程如下:

  找到代碼中模塊定義(YUI.add)的部分

  分析每個模塊內(nèi)函數(shù)定義,變量定義,賦值語句等,找出符合要求(以 Y 開頭)的輸出接口(如 mod1 中的 Y.mod1)

  生成「接口 - 模塊」對應(yīng)關(guān)系

  分析每個模塊內(nèi)函數(shù)調(diào)用,變量使用等,找出符合要求的輸入接口(如 mod2 中的 Y.one,Y.Array.each,Y.mod1)

  通過「接口 - 模塊」對應(yīng)關(guān)系,找到此模塊應(yīng)該依賴哪些其他模塊

  分析 requires 中是否有錯誤

  使用此工具,保證每次提交代碼時,依賴關(guān)系都是正確無誤的,它幫助我們實現(xiàn)了模塊依賴關(guān)系檢測的自動化。

  總結(jié)

  抽象語法樹在計算機領(lǐng)域中應(yīng)用廣泛,以上僅討論了抽象語法樹在 JavaScript 中的一些應(yīng)用,期待更多的用法等著大家去嘗試和探索。

【抽象語法樹在JavaScript中的應(yīng)用】相關(guān)文章:

在Java中執(zhí)行JavaScript代碼04-01

Javascript中typeof 用法歸納04-01

JavaScript中的with關(guān)鍵字03-25

整理Javascript基礎(chǔ)語法學(xué)習(xí)筆記欣賞04-01

perl- javascript中class的機制03-25

JavaScript中的三種對象04-01

javascript閉包的定義及應(yīng)用實例分析04-01

JavaScript中push(),join() 函數(shù)實例詳解03-31

詳解JavaScript中的splice()使用方法04-01