亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

javascript設(shè)計模式之解釋器模式詳解

 更新時間:2014年06月05日 11:01:11   投稿:junjie  
這篇文章主要介紹了javascript設(shè)計模式之解釋器模式詳解,當(dāng)有一個語言需要解釋執(zhí)行,并且可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式,需要的朋友可以參考下


神馬是“解釋器模式”?

先翻開《GOF》看看Definition:
給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

在開篇之前還是要科普幾個概念:

抽象語法樹:

解釋器模式并未解釋如何創(chuàng)建一個抽象語法樹。它不涉及語法分析。抽象語法樹可用一個表驅(qū)動的語法分析程序來完成,也可用手寫的(通常為遞歸下降法)語法分析程序創(chuàng)建,或直接client提供。

解析器:

指的是把描述客戶端調(diào)用要求的表達(dá)式,經(jīng)過解析,形成一個抽象語法樹的程序。

解釋器:

指的是解釋抽象語法樹,并執(zhí)行每個節(jié)點(diǎn)對應(yīng)的功能的程序。

要使用解釋器模式,一個重要的前提就是要定義一套語法規(guī)則,也稱為文法。不管這套文法的規(guī)則是簡單還是復(fù)雜,必須要有這些規(guī)則,因?yàn)榻忉屍髂J骄褪前凑者@些規(guī)則來進(jìn)行解析并執(zhí)行相應(yīng)的功能的。

先來看看解釋器模式的結(jié)構(gòu)圖和說明:



AbstractExpression:定義解釋器的接口,約定解釋器的解釋操作。
TerminalExpression:終結(jié)符解釋器,用來實(shí)現(xiàn)語法規(guī)則中和終結(jié)符相關(guān)的操作,不再包含其他的解釋器,如果用組合模式來構(gòu)建抽象語法樹的話,就相當(dāng)于組合模式中的葉子對象,可以有多種終結(jié)符解釋器。
NonterminalExpression:非終結(jié)符解釋器,用來實(shí)現(xiàn)語法規(guī)則中非終結(jié)符相關(guān)的操作,通常一個解釋器對應(yīng)一個語法規(guī)則,可以包含其他的解釋器,如果用組合模式來構(gòu)建抽象語法樹的話,就相當(dāng)于組合模式中的組合對象??梢杂卸喾N非終結(jié)符解釋器。
Context:上下文,通常包含各個解釋器需要的數(shù)據(jù)或是公共的功能。
Client:客戶端,指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的表達(dá)式轉(zhuǎn)換成為使用解釋器對象描述的抽象語法樹,然后調(diào)用解釋操作。

下面我們通過一個xml示例來理解解釋器模式:

首先要為表達(dá)式設(shè)計簡單的文法,為了通用,用root表示根元素,abc等來代表元素,一個簡單的xml如下:

復(fù)制代碼 代碼如下:

<?xml version="1.0" encoding="UTF-8">
  <root id="rootId">
      <a>
          <b>
              <c name="testC">12345</c>
              <d id="1">d1</d>
              <d id="2">d2</d>
              <d id="3">d3</d>
              <d id="4">d4</d>
          </b>
      </a>
  </root>

約定表達(dá)式的文法如下:

1.獲取單個元素的值:從根元素開始,一直到想要獲取取值的元素,元素中間用“/”分隔,根元素前不加“/”。比如,表達(dá)式“root/a/b/c”就表示獲取根元素下,a元素下,b元素下,c元素的值。
2.獲取單個元素的屬性的值:當(dāng)然是多個,要獲取值的屬性一定是表達(dá)式的最后一個元素的屬性,在最后一個元素后面添加“.”然后再加上屬性的名稱。比如,表達(dá)式“root/a/b/c.name”就表示獲取根元素下,a元素下,b元素下,c元素的name屬性的值。
3.獲取相同元素名稱的值,當(dāng)然是多個,要獲取值的元素一定是表達(dá)式的最后一個元素,在最后一個元素后面添加“$”。比如,表達(dá)式“root/a/b/d$”就表示獲取根元素下,a元素下,b元素下的多個d元素的值的集合。
4.獲取相同元素名稱的屬性的值,當(dāng)然也是多個:要獲取屬性值的元素一定是表達(dá)式的最后一個元素,在最后一個元素后面添加"$"。比如,表達(dá)式“root/a/b/d$.id$”就表示獲取根元素下,a元素下,b元素下的多個d元素的id屬性的值的集合。

上面的xml,對應(yīng)的抽象語法樹,可能的結(jié)構(gòu)如圖:



下面我們來看看具體的代碼:

1.定義上下文:

復(fù)制代碼 代碼如下:

/**
 * 上下文,用來包含解釋器需要的一些全局信息
 * @param {String} filePathName [需要讀取的xml的路徑和名字]
 */
function Context(filePathName) {
    // 上一個被處理元素
    this.preEle = null;
    // xml的Document對象
    this.document = XmlUtil.getRoot(filePathName);
}

Context.prototype = {
    // 重新初始化上下文
    reInit: function () {
        this.preEle = null;
    },
    /**
     *  各個Expression公共使用的方法
     * 根據(jù)父元素和當(dāng)前元素的名稱來獲取當(dāng)前元素
     * @param  {Element} pEle    [父元素]
     * @param  {String} eleName [當(dāng)前元素名稱]
     * @return {Element|null}         [找到的當(dāng)前元素]
     */
    getNowEle: function (pEle, eleName) {
        var tempNodeList = pEle.childNodes;
        var nowEle;

        for (var i = 0, len = tempNodeList.length; i < len; i++) {
            if ((nowEle = tempNodeList[i]).nodeType === 1)
                if (nowEle.nodeName === eleName)
                    return nowEle;
        }

        return null;
    },
    getPreEle: function () {
        return this.preEle;
    },
    setPreEle: function (preEle) {
        this.preEle = preEle;
    },
    getDocument: function () {
        return this.document;
    }
};

在上下文中使用了一個工具對象XmlUtil來獲取xmlDom,下面我使用的是DOM3的DOMPaser,某些瀏覽器可能不支持:

復(fù)制代碼 代碼如下:

 // 工具對象
    // 解析xml,獲取相應(yīng)的Document對象
    var XmlUtil = {
        getRoot: function (filePathName) {
            var parser = new DOMParser();
            var xmldom = parser.parseFromString('<root id="rootId"><a><b><c name="testC">12345</c><d id="1">d1</d><d id="2">d2</d><d id="3">d3</d><d id="4">d4</d></b></a></root>', 'text/xml');

            return xmldom;
        }
    };

下面就是解釋器的代碼:

復(fù)制代碼 代碼如下:

 /**
     * 元素作為非終結(jié)符對應(yīng)的解釋器,解釋并執(zhí)行中間元素
     * @param {String} eleName [元素的名稱]
     */
    function ElementExpression(eleName) {
        this.eles = [];
        this.eleName = eleName;
    }

    ElementExpression.prototype = {
        addEle: function (eleName) {
            this.eles.push(eleName);
            return true;
        },
        removeEle: function (ele) {
            for (var i = 0, len = this.eles.length; i < len; i++) {
                if (ele === this.eles[i])
                    this.eles.splice(i--, 1);
            }
            return true;
        },
        interpret: function (context) {
            // 先取出上下文中的當(dāng)前元素作為父級元素
            // 查找到當(dāng)前元素名稱所對應(yīng)的xml元素,并設(shè)置回到上下文中
            var pEle = context.getPreEle();

            if (!pEle) {
                // 說明現(xiàn)在獲取的是根元素
                context.setPreEle(context.getDocument().documentElement);
            } else {
                // 根據(jù)父級元素和要查找的元素的名稱來獲取當(dāng)前的元素
                var nowEle = context.getNowEle(pEle, this.eleName);
                // 把當(dāng)前獲取的元素放到上下文中
                context.setPreEle(nowEle);
            }

            var ss;
            // 循環(huán)調(diào)用子元素的interpret方法
            for (var i = 0, len = this.eles.length; i < len; i++) {
                ss = this.eles[i].interpret(context);
            }

            // 返回最后一個解釋器的解釋結(jié)果,一般最后一個解釋器就是終結(jié)符解釋器了
            return ss;
        }
    };

    /**
     * 元素作為終結(jié)符對應(yīng)的解釋器
     * @param {String} name [元素的名稱]
     */
    function ElementTerminalExpression(name) {
        this.eleName = name;
    }

    ElementTerminalExpression.prototype = {
        interpret: function (context) {
            var pEle = context.getPreEle();
            var ele = null;
            if (!pEle) {
                ele = context.getDocument().documentElement;
            } else {
                ele = context.getNowEle(pEle, this.eleName);
                context.setPreEle(ele);
            }

            // 獲取元素的值
            return ele.firstChild.nodeValue;
        }
    };

    /**
     * 屬性作為終結(jié)符對應(yīng)的解釋器
     * @param {String} propName [屬性的名稱]
     */
    function PropertyTerminalExpression(propName) {
        this.propName = propName;
    }

    PropertyTerminalExpression.prototype = {
        interpret: function (context) {
            // 直接獲取最后的元素屬性的值
            return context.getPreEle().getAttribute(this.propName);
        }
    };

先來看看如何使用解釋器獲取單個元素的值:

復(fù)制代碼 代碼如下:

void function () {
    var c = new Context();
    // 想要獲取多個d元素的值,也就是如下表達(dá)式的值:“root/a/b/c”
    // 首先要構(gòu)建解釋器的抽象語法樹
    var root = new ElementExpression('root');
    var aEle = new ElementExpression('a');
    var bEle = new ElementExpression('b');
    var cEle = new ElementTerminalExpression('c');

    // 組合
    root.addEle(aEle);
    aEle.addEle(bEle);
    bEle.addEle(cEle);

    console.log('c的值是 = ' + root.interpret(c));

}();

輸出: c的值是 = 12345

然后我們再用上面代碼獲取單個元素的屬性的值:

復(fù)制代碼 代碼如下:

 void function () {
        var c = new Context();
        // 想要獲取d元素的id屬性,也就是如下表達(dá)式的值:“a/b/c.name”
        // 這個時候c不是終結(jié)了,需要把c修改成ElementExpression
        var root = new ElementExpression('root');
        var aEle = new ElementExpression('a');
        var bEle = new ElementExpression('b');
        var cEle = new ElementExpression('c');
        var prop = new PropertyTerminalExpression('name');

        // 組合
        root.addEle(aEle);
        aEle.addEle(bEle);
        bEle.addEle(cEle);
        cEle.addEle(prop);

        console.log('c的屬性name值是 = ' + root.interpret(c));

        // 如果要使用同一個上下文,連續(xù)進(jìn)行解析,需要重新初始化上下文對象
        // 比如,要連續(xù)的重新再獲取一次屬性name的值,當(dāng)然你可以重新組合元素
        // 重新解析,只要是在使用同一個上下文,就需要重新初始化上下文對象
        c.reInit();
        console.log('重新獲取c的屬性name值是 = ' + root.interpret(c));
    }();


輸出: c的屬性name值是 = testC 重新獲取c的屬性name值是 = testC

講解:

1.解釋器模式功能:

解釋器模式使用解釋器對象來表示和處理相應(yīng)的語法規(guī)則,一般一個解釋器處理一條語法規(guī)則。理論上來說,只要能用解釋器對象把符合語法的表達(dá)式表示出來,而且能夠構(gòu)成抽象的語法樹,就可以使用解釋器模式來處理。

2.語法規(guī)則和解釋器

語法規(guī)則和解釋器之間是有對應(yīng)關(guān)系的,一般一個解釋器處理一條語法規(guī)則,但是反過來并不成立,一條語法規(guī)則是可以有多種解釋和處理的,也就是一條語法規(guī)則可以對應(yīng)多個解釋器。

3.上下文的公用性

上下文在解釋器模式中起著非常重要的作用。由于上下文會被傳遞到所有的解釋器中。因此可以在上下文中存儲和訪問解釋器的狀態(tài),比如,前面的解釋器可以存儲一些數(shù)據(jù)在上下文中,后面的解釋器就可以獲取這些值。

另外還可以通過上下文傳遞一些在解釋器外部,但是解釋器需要的數(shù)據(jù),也可以是一些全局的,公共的數(shù)據(jù)。

上下文還有一個功能,就是可以提供所有解釋器對象的公共功能,類似于對象組合,而不是使用繼承來獲取公共功能,在每個解釋器對象中都可以調(diào)用

4.誰來構(gòu)建抽象語法樹

在前面的示例中,是自己在客戶端手工構(gòu)建抽象語法樹,是很麻煩的,但是在解釋器模式中,并沒有涉及這部分功能,只是負(fù)責(zé)對構(gòu)建好的抽象語法樹進(jìn)行解釋處理。后面會介紹可以提供解析器來實(shí)現(xiàn)把表達(dá)式轉(zhuǎn)換成為抽象語法樹。

還有一個問題,就是一條語法規(guī)則是可以對應(yīng)多個解釋器對象的,也就是說同一個元素,是可以轉(zhuǎn)換成多個解釋器對象的,這也就意味著同樣一個表達(dá)式,是可以構(gòu)成不用的抽象語法樹的,這也造成構(gòu)建抽象語法樹變得很困難,而且工作量非常大。

5.誰負(fù)責(zé)解釋操作

只要定義好了抽象語法樹,肯定是解釋器來負(fù)責(zé)解釋執(zhí)行。雖然有不同的語法規(guī)則,但是解釋器不負(fù)責(zé)選擇究竟用哪個解釋器對象來解釋執(zhí)行語法規(guī)則,選擇解釋器的功能在構(gòu)建抽象語法樹的時候就完成了。

6.解釋器模式的調(diào)用順序

1)創(chuàng)建上下文對象

2)創(chuàng)建多個解釋器對象,組合抽象語法樹

3)調(diào)用解釋器對象的解釋操作

3.1)通過上下文來存儲和訪問解釋器的狀態(tài)。

對于非終結(jié)符解釋器對象,遞歸調(diào)用它所包含的子解釋器對象。

解釋器模式的本質(zhì):*分離實(shí)現(xiàn),解釋執(zhí)行*

解釋器模使用一個解釋器對象處理一個語法規(guī)則的方式,把復(fù)雜的功能分離開;然后選擇需要被執(zhí)行的功能,并把這些功能組合成為需要被解釋執(zhí)行的抽象語法樹;再按照抽象語法樹來解釋執(zhí)行,實(shí)現(xiàn)相應(yīng)的功能。

從表面上看,解釋器模式關(guān)注的是我們平時不太用到的自定義語法的處理;但從實(shí)質(zhì)上看,解釋器模式的思想然后是分離,封裝,簡化,和很多模式是一樣的。

比如,可以使用解釋器模式模擬狀態(tài)模式的功能。如果把解釋器模式要處理的語法簡化到只有一個狀態(tài)標(biāo)記,把解釋器看成是對狀態(tài)的處理對象,對同一個表示狀態(tài)的語法,可以有很多不用的解釋器,也就是有很多不同的處理狀態(tài)的對象,然后再創(chuàng)建抽象語法樹的時候,簡化成根據(jù)狀態(tài)的標(biāo)記來創(chuàng)建相應(yīng)的解釋器,不用再構(gòu)建樹了。

同理,解釋器模式可以模擬實(shí)現(xiàn)策略模式的功能,裝飾器模式的功能等,尤其是模擬裝飾器模式的功能,構(gòu)建抽象語法樹的過程,自然就對應(yīng)成為組合裝飾器的過程。

解釋器模式執(zhí)行速度通常不快(大多數(shù)時候非常慢),而且錯誤調(diào)試比較困難(附注:雖然調(diào)試比較困難,但事實(shí)上它降低了錯誤的發(fā)生可能性),但它的優(yōu)勢是顯而易見的,它能有效控制模塊之間接口的復(fù)雜性,對于那種執(zhí)行頻率不高但代碼頻率足夠高,且多樣性很強(qiáng)的功能,解釋器是非常適合的模式。此外解釋器還有一個不太為人所注意的優(yōu)勢,就是它可以方便地跨語言和跨平臺。

解釋器模式的優(yōu)缺點(diǎn):

優(yōu)點(diǎn):

1.易于實(shí)現(xiàn)語法

在解釋器模式中,一條語法規(guī)則用一個解釋器對象來解釋執(zhí)行。對于解釋器的實(shí)現(xiàn)來講,功能就變得比較簡單,只需要考慮這一條語法規(guī)則的實(shí)現(xiàn)就可以了,其他的都不用管。 2.易于擴(kuò)展新的語法

正是由于采用一個解釋器對象負(fù)責(zé)一條語法規(guī)則的方式,使得擴(kuò)展新的語法非常容易。擴(kuò)展了新的語法,只需要創(chuàng)建相應(yīng)的解釋器對象,在創(chuàng)建抽象語法樹的時候使用這個新的解釋器對象就可以了。

缺點(diǎn):

不適合復(fù)雜的語法

如果語法特別復(fù)雜,構(gòu)建解釋器模式需要的抽象語法樹的工作是非常艱巨的,再加上有可能會需要構(gòu)建多個抽象語法樹。所以解釋器模式不太適合復(fù)雜的語法。使用語法分析程序或編譯器生成器可能會更好一些。

何時使用?

當(dāng)有一個語言需要解釋執(zhí)行,并且可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式。

在使用解釋器模式的時候,還有兩個特點(diǎn)需要考慮,一個是語法相對應(yīng)該比較簡單,太負(fù)責(zé)的語法不適合使用解釋器模式玲玲一個是效率要求不是很高,對效率要求很高的,不適合使用。

前面介紹了如何獲取單個元素的值和單個元素屬性的值,下面來看看如何獲取多個元素的值,還有多個元素中相擁名稱的值,還有前面的測試都是人工組合好的抽象語法樹,我們也順便實(shí)現(xiàn)以下簡單的解析器,把符合前面定義的語法的表達(dá)式,轉(zhuǎn)換成為前面實(shí)現(xiàn)的解釋器的抽象語法樹: 我就直接把代碼貼出來了:

復(fù)制代碼 代碼如下:

 // 讀取多個元素或?qū)傩缘闹?br />    (function () {
        /**
         * 上下文,用來包含解釋器需要的一些全局信息
         * @param {String} filePathName [需要讀取的xml的路徑和名字]
         */
        function Context(filePathName) {
            // 上一個被處理的多個元素
            this.preEles = [];
            // xml的Document對象
            this.document = XmlUtil.getRoot(filePathName);
        }

        Context.prototype = {
            // 重新初始化上下文
            reInit: function () {
                this.preEles = [];
            },
            /**
             *  各個Expression公共使用的方法
             * 根據(jù)父元素和當(dāng)前元素的名稱來獲取當(dāng)前元素
             * @param  {Element} pEle    [父元素]
             * @param  {String} eleName [當(dāng)前元素名稱]
             * @return {Element|null}         [找到的當(dāng)前元素]
             */
            getNowEles: function (pEle, eleName) {
                var elements = [];
                var tempNodeList = pEle.childNodes;
                var nowEle;

                for (var i = 0, len = tempNodeList.length; i < len; i++) {
                    if ((nowEle = tempNodeList[i]).nodeType === 1) {
                        if (nowEle.nodeName === eleName) {
                            elements.push(nowEle);
                        }
                    }
                }

                return elements;
            },
            getPreEles: function () {
                return this.preEles;
            },
            setPreEles: function (nowEles) {
                this.preEles = nowEles;
            },
            getDocument: function () {
                return this.document;
            }
        };

        // 工具對象
        // 解析xml,獲取相應(yīng)的Document對象
        var XmlUtil = {
            getRoot: function (filePathName) {
                var parser = new DOMParser();
                var xmldom = parser.parseFromString('<root id="rootId"><a><b><c name="testC">12345</c><d id="1">d1</d><d id="2">d2</d><d id="3">d3</d><d id="4">d4</d></b></a></root>', 'text/xml');

                return xmldom;
            }
        };

        /**
         * 元素作為非終結(jié)符對應(yīng)的解釋器,解釋并執(zhí)行中間元素
         * @param {String} eleName [元素的名稱]
         */
        function ElementExpression(eleName) {
            this.eles = [];
            this.eleName = eleName;
        }

        ElementExpression.prototype = {
            addEle: function (eleName) {
                this.eles.push(eleName);
                return true;
            },
            removeEle: function (ele) {
                for (var i = 0, len = this.eles.length; i < len; i++) {
                    if (ele === this.eles[i]) {
                        this.eles.splice(i--, 1);
                    }
                }
                return true;
            },
            interpret: function (context) {
                // 先取出上下文中的當(dāng)前元素作為父級元素
                // 查找到當(dāng)前元素名稱所對應(yīng)的xml元素,并設(shè)置回到上下文中
                var pEles = context.getPreEles();
                var ele = null;
                var nowEles = [];

                if (!pEles.length) {
                    // 說明現(xiàn)在獲取的是根元素
                    ele = context.getDocument().documentElement;
                    pEles.push(ele);
                    context.setPreEles(pEles);
                } else {
                    var tempEle;
                    for (var i = 0, len = pEles.length; i < len; i++) {
                        tempEle = pEles[i];
                        nowEles = nowEles.concat(context.getNowEles(tempEle, this.eleName));

                        // 找到一個就停止
                        if (nowEles.length) break;
                    }

                    context.setPreEles([nowEles[0]]);
                }

                var ss;
                // 循環(huán)調(diào)用子元素的interpret方法
                for (var i = 0, len = this.eles.length; i < len; i++) {
                    ss = this.eles[i].interpret(context);
                }

                return ss;
            }
        };

        /**
         * 元素作為終結(jié)符對應(yīng)的解釋器
         * @param {String} name [元素的名稱]
         */
        function ElementTerminalExpression(name) {
            this.eleName = name;
        }

        ElementTerminalExpression.prototype = {
            interpret: function (context) {
                var pEles = context.getPreEles();
                var ele = null;
                if (!pEles.length) {
                    ele = context.getDocument().documentElement;
                } else {
                    ele = context.getNowEles(pEles[0], this.eleName)[0];
                }

                // 獲取元素的值
                return ele.firstChild.nodeValue;
            }
        };

        /**
         * 屬性作為終結(jié)符對應(yīng)的解釋器
         * @param {String} propName [屬性的名稱]
         */
        function PropertyTerminalExpression(propName) {
            this.propName = propName;
        }

        PropertyTerminalExpression.prototype = {
            interpret: function (context) {
                // 直接獲取最后的元素屬性的值
                return context.getPreEles()[0].getAttribute(this.propName);
            }
        };

        /**
         * 多個屬性作為終結(jié)符對應(yīng)的解釋器
         * @param {String} propName [屬性的名稱]
         */
        function PropertysTerminalExpression(propName) {
            this.propName = propName;
        }

        PropertysTerminalExpression.prototype = {
            interpret: function (context) {
                var eles = context.getPreEles();
                var ss = [];

                for (var i = 0, len = eles.length; i < len; i++) {
                    ss.push(eles[i].getAttribute(this.propName));
                }

                return ss;
            }
        };

        /**
         * 以多個元素作為終結(jié)符的解釋處理對象
         * @param {[type]} name [description]
         */
        function ElementsTerminalExpression(name) {
            this.eleName = name;
        }

        ElementsTerminalExpression.prototype = {
            interpret: function (context) {
                var pEles = context.getPreEles();
                var nowEles = [];

                for (var i = 0, len = pEles.length; i < len; i++) {
                    nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
                }

                var ss = [];

                for (i = 0, len = nowEles.length; i < len; i++) {
                    ss.push(nowEles[i].firstChild.nodeValue);
                }

                return ss;
            }
        };

        /**
         * 多個元素作為非終結(jié)符的解釋處理對象
         */
        function ElementsExpression(name) {
            this.eleName = name;
            this.eles = [];
        }

        ElementsExpression.prototype = {
            interpret: function (context) {
                var pEles = context.getPreEles();
                var nowEles = [];

                for (var i = 0, len = pEles.length; i < len; i++) {
                    nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
                }
                context.setPreEles(nowEles);

                var ss;
                for (i = 0, len = this.eles.length; i < len; i++) {
                    ss = this.eles[i].interpret(context);
                }

                return ss;
            },
            addEle: function (ele) {
                this.eles.push(ele);
                return true;
            },
            removeEle: function (ele) {
                for (var i = 0, len = this.eles.length; i < len; i++) {
                    if (ele === this.eles[i]) {
                        this.eles.splice(i--, 1);
                    }
                }
                return true;
            }
        };

        void function () {
            // "root/a/b/d$"
            var c = new Context('Interpreter.xml');
            var root = new ElementExpression('root');
            var aEle = new ElementExpression('a');
            var bEle = new ElementExpression('b');
            var dEle = new ElementsTerminalExpression('d');

            root.addEle(aEle);
            aEle.addEle(bEle);
            bEle.addEle(dEle);

            var ss = root.interpret(c);

            for (var i = 0, len = ss.length; i < len; i++) {
                console.log('d的值是 = ' + ss[i]);
            }
        }();

        void function () {
            // a/b/d$.id$
            var c = new Context('Interpreter.xml');
            var root = new ElementExpression('root');
            var aEle = new ElementExpression('a');
            var bEle = new ElementExpression('b');
            var dEle = new ElementsExpression('d');
            var prop = new PropertysTerminalExpression('id');

            root.addEle(aEle);
            aEle.addEle(bEle);
            bEle.addEle(dEle);
            dEle.addEle(prop);

            var ss = root.interpret(c);

            for (var i = 0, len = ss.length; i < len; i++) {
                console.log('d的屬性id的值是 = ' + ss[i]);
            }
        }();

        // 解析器

        /**
         * 解析器的實(shí)現(xiàn)思路
         * 1.把客戶端傳遞來的表達(dá)式進(jìn)行分解,分解成為一個一個的元素,并用一個對應(yīng)的解析模型來封裝這個元素的一些信息。
         * 2.根據(jù)每個元素的信息,轉(zhuǎn)化成相對應(yīng)的解析器對象。
         * 3.按照先后順序,把這些解析器對象組合起來,就得到抽象語法樹了。
         *
         * 為什么不把1和2合并,直接分解出一個元素就轉(zhuǎn)換成相應(yīng)的解析器對象?
         * 1.功能分離,不要讓一個方法的功能過于復(fù)雜。
         * 2.為了今后的修改和擴(kuò)展,現(xiàn)在語法簡單,所以轉(zhuǎn)換成解析器對象需要考慮的東西少,直接轉(zhuǎn)換也不難,但要是語法復(fù)雜了,直接轉(zhuǎn)換就很雜亂了。
         */

        /**
         * 用來封裝每一個解析出來的元素對應(yīng)的屬性
         */
        function ParserModel() {
            // 是否單個值
            this.singleValue;
            // 是否屬性,不是屬性就是元素
            this.propertyValue;
            // 是否終結(jié)符
            this.end;
        }

        ParserModel.prototype = {
            isEnd: function () {
                return this.end;
            },
            setEnd: function (end) {
                this.end = end;
            },
            isSingleValue: function () {
                return this.singleValue;
            },
            setSingleValue: function (oneValue) {
                this.singleValue = oneValue;
            },
            isPropertyValue: function () {
                return this.propertyValue;
            },
            setPropertyValue: function (propertyValue) {
                this.propertyValue = propertyValue;
            }
        };

        var Parser = function () {
            var BACKLASH = '/';
            var DOT = '.';
            var DOLLAR = '$';
            // 按照分解的先后記錄需要解析的元素的名稱
            var listEle = null;

            // 開始實(shí)現(xiàn)第一步-------------------------------------

            /**
             * 傳入一個字符串表達(dá)式,通過解析,組合成為一個抽象語法樹
             * @param  {String} expr [描述要取值的字符串表達(dá)式]
             * @return {Object}      [對應(yīng)的抽象語法樹]
             */
            function parseMapPath(expr) {
                // 先按照“/”分割字符串
                var tokenizer = expr.split(BACKLASH);
                // 用來存放分解出來的值的表
                var mapPath = {};
                var onePath, eleName, propName;
                var dotIndex = -1;

                for (var i = 0, len = tokenizer.length; i < len; i++) {
                    onePath = tokenizer[i];

                    if (tokenizer[i + 1]) {
                        // 還有下一個值,說明這不是最后一個元素
                        // 按照現(xiàn)在的語法,屬性必然在最后,因此也不是屬性
                        setParsePath(false, onePath, false, mapPath);
                    } else {
                        // 說明到最后了
                        dotIndex = onePath.indexOf(DOT);

                        if (dotIndex >= 0) {
                            // 說明是要獲取屬性的值,那就按照“.”來分割
                            // 前面的就是元素名稱,后面的是屬性的名字
                            eleName = onePath.substring(0, dotIndex);
                            propName = onePath.substring(dotIndex + 1);

                            // 設(shè)置屬性前面的那個元素,自然不是最后一個,也不是屬性
                            setParsePath(false, eleName, false, mapPath);
                            // 設(shè)置屬性,按照現(xiàn)在的語法定義,屬性只能是最后一個
                            setParsePath(true, propName, true, mapPath);
                        } else {
                            // 說明是取元素的值,而且是最后一個元素的值
                            setParsePath(true, onePath, false, mapPath);
                        }

                        break;
                    }
                }

                return mapPath;
            }

            /**
             * 按照分解出來的位置和名稱來設(shè)置需要解析的元素名稱
             * @param {Boolean} end           [是否最后一個]
             * @param {String} ele           [元素名稱]
             * @param {Boolean} propertyValue [是否取屬性]
             * @param {Object} mapPath       [設(shè)置需要解析的元素名稱,還有該元素對應(yīng)的解析模型的表]
             */
            function setParsePath(end, ele, propertyValue, mapPath) {
                var pm = new ParserModel();
                pm.setEnd(end);
                // 如果帶有“$”符號就說明不是一個值
                pm.setSingleValue(!(ele.indexOf(DOLLAR) >= 0));
                pm.setPropertyValue(propertyValue);
                // 去掉"$"
                ele = ele.replace(DOLLAR, '');
                mapPath[ele] = pm;
                listEle.push(ele);
            }

            // 開始實(shí)現(xiàn)第二步-------------------------------------

            /**
             * 把分解出來的元素名稱根據(jù)對應(yīng)的解析模型轉(zhuǎn)換成為相應(yīng)的解釋器對象
             * @param  {Object} mapPath [分解出來的需解析的元素名稱,還有該元素對應(yīng)的解析模型]
             * @return {Array}         [把每個元素轉(zhuǎn)換成為相應(yīng)的解釋器對象后的數(shù)組]
             */
            function mapPath2Interpreter(mapPath) {
                var list = [];
                var pm, key;
                var obj = null;

                // 一定要按照分解的先后順序來轉(zhuǎn)換成解釋器對象
                for (var i = 0, len = listEle.length; i < len; i++) {
                    key = listEle[i];
                    pm = mapPath[key];

                    // 不是最后一個
                    if (!pm.isEnd()) {

                        if (pm.isSingleValue())
                        // 是一個值,轉(zhuǎn)化
                            obj = new ElementExpression(key);
                        else
                        // 是多個值,轉(zhuǎn)化
                            obj = new ElementsExpression(key);

                    } else {
                        // 是最后一個

                        // 是屬性值
                        if (pm.isPropertyValue()) {
                            if (pm.isSingleValue())
                                obj = new PropertyTerminalExpression(key);
                            else
                                obj = new PropertysTerminalExpression(key);

                            // 取元素的值
                        } else {
                            if (pm.isSingleValue())
                                obj = new ElementTerminalExpression(key);
                            else
                                obj = new ElementsTerminalExpression(key);
                        }
                    }

                    list.push(obj);
                }

                return list;
            }

            // 開始實(shí)現(xiàn)第三步-------------------------------------

            /**
             * 構(gòu)建抽象語法樹
             * @param  {[type]} list [把每個元素轉(zhuǎn)換成為相應(yīng)的解釋器對象后的數(shù)組]
             * @return {[type]}      [description]
             */
            function buildTree(list) {
                // 第一個對象,也是返回去的對象,就是抽象語法樹的根
                var returnReadXMLExpr = null;
                // 定義上一個對象
                var preReadXmlExpr = null;
                var readXml, ele, eles;

                for (var i = 0, len = list.length; i < len; i++) {
                    readXml = list[i];
                    // 說明是第一個元素
                    if (preReadXmlExpr === null) {
                        preReadXmlExpr = readXml;
                        returnReadXMLExpr = readXml;

                        // 把元素添加到上一個對象下面,同時把本對象設(shè)置成為oldRe
                        // 作為下一個對象的父節(jié)點(diǎn)
                    } else {
                        if (preReadXmlExpr instanceof ElementExpression) {
                            ele = preReadXmlExpr;
                            ele.addEle(readXml);
                            preReadXmlExpr = readXml;
                        } else if (preReadXmlExpr instanceof ElementsExpression) {
                            eles = preReadXmlExpr;
                            eles.addEle(readXml);
                            preReadXmlExpr = readXml;
                        }
                    }
                }

                return returnReadXMLExpr;
            }

            return {
                // 公共方法
                parse: function (expr) {
                    listEle = [];

                    var mapPath = parseMapPath(expr);
                    var list = mapPath2Interpreter(mapPath);

                    return buildTree(list);
                }
            };
        }();

        void function () {
            // 準(zhǔn)備上下文
            var c = new Context('Interpreter.xml');
            // 通過解析其獲取抽象語法樹
            var readXmlExpr = Parser.parse('root/a/b/d$.id$');
            // 請求解析,獲取返回值
            var ss = readXmlExpr.interpret(c);

            console.log('------------parsing--------------');
            for (var i = 0, len = ss.length; i < len; i++) {
                console.log('d的屬性id的值是 = ' + ss[i]);
            }
            console.log('---------------parsed--------------');

            // 如果要使用同一個上下文,連續(xù)進(jìn)行解析,需要重新初始化上下文對象
            c.reInit();
            var readxmlExpr2 = Parser.parse('root/a/b/d$');
            var ss2 = readxmlExpr2.interpret(c);
            console.log('------------parsing--------------');
            for (i = 0, len = ss2.length; i < len; i++) {
                console.log('d的值是 = ' + ss2[i]);
            }
            console.log('---------------parsed--------------');

            c.reInit();
            var readxmlExpr3 = Parser.parse('root/a/b/c');
            var ss3 = readxmlExpr3.interpret(c);
            console.log('------------parsing--------------');
            console.log('c的name屬性值是 = ' + ss3);
            console.log('---------------parsed--------------');

            c.reInit();
            var readxmlExpr4 = Parser.parse('root/a/b/c.name');
            var ss4 = readxmlExpr4.interpret(c);
            console.log('------------parseing--------------');
            console.log('c的name屬性值是 = ' + ss4);
            console.log('---------------parsed--------------');
        }();

        // 這樣就實(shí)現(xiàn)了類似XPath的部分功能
        // 沒錯,就類似于jQuery選擇器的部分功能
    }());

輸出: d的值是 = d1
d的值是 = d2
d的值是 = d3
d的值是 = d4
d的屬性id的值是 = 1
d的屬性id的值是 = 2
d的屬性id的值是 = 3
d的屬性id的值是 = 4
------------parsing--------------
d的屬性id的值是 = 1
d的屬性id的值是 = 2
d的屬性id的值是 = 3
d的屬性id的值是 = 4
---------------parsed--------------
------------parsing--------------
d的值是 = d1
d的值是 = d2
d的值是 = d3
d的值是 = d4
---------------parsed--------------
------------parsing--------------
c的name屬性值是 = 12345
---------------parsed--------------
------------parseing--------------
c的name屬性值是 = testC
---------------parsed--------------

相關(guān)文章

最新評論