中文字幕在线一区二区在线,久久久精品免费观看国产,无码日日模日日碰夜夜爽,天堂av在线最新版在线,日韩美精品无码一本二本三本,麻豆精品三级国产国语,精品无码AⅤ片,国产区在线观看视频

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

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

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

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

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

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

        抽象語(yǔ)法樹(shù)舉例

        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 |

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

        */

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

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

        JavaScript 抽象語(yǔ)法樹(shù)

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

        UglifyJS

        UglifyJS 是使用最廣的 JavaScript 壓縮工具之一,而且自身也是用 JavaScript 寫(xiě)的,使用它的方法很簡(jiǎn)單(需要 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 代碼解析成抽象語(yǔ)法樹(shù)

        code generator,通過(guò)抽象語(yǔ)法樹(shù)生成代碼

        mangler,混淆 JavaScript 代碼

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

        tree walker,遍歷樹(shù)節(jié)點(diǎn)

        tree transformer,改變樹(shù)節(jié)點(diǎn)

        生成抽象語(yǔ)法樹(shù)

        使用 UglifyJS 生成抽象語(yǔ)法樹(shù)很簡(jiǎn)單:

        首先安裝 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 即為那一段代碼的抽象語(yǔ)法樹(shù)。那么我們?cè)趺词褂媚?

        使用 mangler 壓縮代碼

        使用 mangler 可以通過(guò)將局部變量都縮短成一個(gè)字符來(lái)壓縮代碼。

        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 遍歷抽象語(yǔ)法樹(shù)

        使用 walker 可以遍歷抽象語(yǔ)法樹(shù),這種遍歷是深度遍歷。

        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 看上去貌似也沒(méi)啥用,那么這些工具有什么使用場(chǎng)景呢?

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

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

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

        下面考慮這樣一個(gè)需求:

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

        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 不同瀏覽器不一樣,低版本瀏覽器會(huì)轉(zhuǎn)換成八進(jìn)制parseInt('0x11'); // 17 轉(zhuǎn)換成十六進(jìn)制

        因?yàn)橛幸恍┣闆r是和我們預(yù)期不同的,所以建議任何時(shí)候都加上第二個(gè)參數(shù)。

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

        使用 UglifyJS 可以實(shí)現(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)用的地方,然后檢查是否有第二個(gè)參數(shù),沒(méi)有的話,記錄下來(lái),之后根據(jù)每個(gè)記錄,用新的包含第二個(gè)參數(shù)的內(nèi)容替換掉原內(nèi)容,完成代碼的重構(gòu)。

        也許有人會(huì)問(wèn),這種簡(jiǎn)單的情況,用正則匹配也可以方便的替換,干嘛要用抽象語(yǔ)法樹(shù)呢?

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

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

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

        在美團(tuán)前端團(tuán)隊(duì),我們使用 YUI 作為前端底層框架,之前面臨的一個(gè)實(shí)際問(wèn)題是,模塊之間的依賴關(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'

        ]

        });

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

        此處 mod1 出現(xiàn)了兩個(gè)常見(jiàn)錯(cuò)誤,一個(gè)是 simulate 是 Y.Node.prototype 上的方法,容易忘掉聲明依賴 node-event-simulate3,另一個(gè)是 Y.Array 上只有部分方法需要依賴 array-extras,故此處多聲明了依賴 array-extras4;mod2 中添加注釋后,容易忘記刪除原來(lái)寫(xiě)的依賴 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)系的檢測(cè)自動(dòng)化,我們創(chuàng)建了模塊依賴關(guān)系檢測(cè)工具,它利用抽象語(yǔ)法樹(shù),分析出定義了哪些接口,使用了哪些接口,然后查找這些接口應(yīng)該依賴哪些模塊,進(jìn)而找到模塊依賴關(guān)系的錯(cuò)誤,大致的過(guò)程如下:

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

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

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

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

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

        分析 requires 中是否有錯(cuò)誤

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

        總結(jié)

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

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

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

      Javascript中typeof 用法歸納04-01

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

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

      perl- javascript中class的機(jī)制03-25

      JavaScript中的三種對(duì)象04-01

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

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

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

      主站蜘蛛池模板: 麻豆av一区二区天堂| 无遮挡粉嫩小泬| 91中文字幕一区在线| 精品国产亚洲av麻豆尤物| 一二三四中文字幕日韩乱码 | 校花高潮一区日韩| 国产中文久久精品| 久久久久国产精品片区无码| 国产内射视频在线播放| 精品粉嫩国产一区二区三区| 男女在线免费视频网站| 无码熟妇人妻AV不卡| 南木林县| 亚洲AV永久无码精品一区二国| 亚洲天堂中文字幕君一二三四| 国产欧美日韩专区毛茸茸| 九色91精品最新在线| 人妻少妇被猛烈的进入| 日韩高清av一区二区| 国产中文字幕乱码在线| 国产午夜精品视频在线播放| 最新国产精品精品视频| 赞皇县| 99久久综合国产精品免费| 亚洲女同系列高清在线观看 | 亚洲综合一| 国产成人自拍视频在线免费| 国内精品久久久久影院蜜芽| 盘山县| 中文精品久久久久中文| 国产精品亚洲综合一区| 久久精品国产亚洲av麻豆毛片| 中文字幕精品乱码亚洲一区99| 亚洲人成无码网站十八禁| 国产精品成人无码久久久久久| 久久这里只有精品黄色| 久久人成免费网站小草| 亚洲先锋影院一区二区| 中文字幕第一页亚洲观看| 素人激情福利视频| 亚洲中文字幕av一区|