搜索
首页web前端js教程javascript设计模式之解释器模式详解_javascript技巧


神马是“解释器模式”?

先翻开《GOF》看看Definition:
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

在开篇之前还是要科普几个概念:

抽象语法树:

解释器模式并未解释如何创建一个抽象语法树。它不涉及语法分析。抽象语法树可用一个表驱动的语法分析程序来完成,也可用手写的(通常为递归下降法)语法分析程序创建,或直接client提供。

解析器:

指的是把描述客户端调用要求的表达式,经过解析,形成一个抽象语法树的程序。

解释器:

指的是解释抽象语法树,并执行每个节点对应的功能的程序。

要使用解释器模式,一个重要的前提就是要定义一套语法规则,也称为文法。不管这套文法的规则是简单还是复杂,必须要有这些规则,因为解释器模式就是按照这些规则来进行解析并执行相应的功能的。

先来看看解释器模式的结构图和说明:

javascript设计模式之解释器模式详解_javascript技巧

AbstractExpression:定义解释器的接口,约定解释器的解释操作。
TerminalExpression:终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器。
NonterminalExpression:非终结符解释器,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其他的解释器,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象。可以有多种非终结符解释器。
Context:上下文,通常包含各个解释器需要的数据或是公共的功能。
Client:客户端,指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。

下面我们通过一个xml示例来理解解释器模式:

首先要为表达式设计简单的文法,为了通用,用root表示根元素,abc等来代表元素,一个简单的xml如下:

复制代码 代码如下:

约定表达式的文法如下:

1.获取单个元素的值:从根元素开始,一直到想要获取取值的元素,元素中间用“/”分隔,根元素前不加“/”。比如,表达式“root/a/b/c”就表示获取根元素下,a元素下,b元素下,c元素的值。
2.获取单个元素的属性的值:当然是多个,要获取值的属性一定是表达式的最后一个元素的属性,在最后一个元素后面添加“.”然后再加上属性的名称。比如,表达式“root/a/b/c.name”就表示获取根元素下,a元素下,b元素下,c元素的name属性的值。
3.获取相同元素名称的值,当然是多个,要获取值的元素一定是表达式的最后一个元素,在最后一个元素后面添加“$”。比如,表达式“root/a/b/d$”就表示获取根元素下,a元素下,b元素下的多个d元素的值的集合。
4.获取相同元素名称的属性的值,当然也是多个:要获取属性值的元素一定是表达式的最后一个元素,在最后一个元素后面添加"$"。比如,表达式“root/a/b/d$.id$”就表示获取根元素下,a元素下,b元素下的多个d元素的id属性的值的集合。

上面的xml,对应的抽象语法树,可能的结构如图:

javascript设计模式之解释器模式详解_javascript技巧

下面我们来看看具体的代码:

1.定义上下文:

复制代码 代码如下:

/**
 * 上下文,用来包含解释器需要的一些全局信息
 * @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公共使用的方法
     * 根据父元素和当前元素的名称来获取当前元素
     * @param  {Element} pEle    [父元素]
     * @param  {String} eleName [当前元素名称]
     * @return {Element|null}         [找到的当前元素]
     */
    getNowEle: function (pEle, eleName) {
        var tempNodeList = pEle.childNodes;
        var nowEle;

        for (var i = 0, len = tempNodeList.length; 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,某些浏览器可能不支持,请使用搞基浏览器:

复制代码 代码如下:

 // 工具对象
    // 解析xml,获取相应的Document对象
    var XmlUtil = {
        getRoot: function (filePathName) {
            var parser = new DOMParser();
            var xmldom = parser.parseFromString('12345d1d2d3d4', 'text/xml');

            return xmldom;
        }
    };

下面就是解释器的代码:

复制代码 代码如下:

 /**
     * 元素作为非终结符对应的解释器,解释并执行中间元素
     * @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                 if (ele === this.eles[i])
                    this.eles.splice(i--, 1);
            }
            return true;
        },
        interpret: function (context) {
            // 先取出上下文中的当前元素作为父级元素
            // 查找到当前元素名称所对应的xml元素,并设置回到上下文中
            var pEle = context.getPreEle();

            if (!pEle) {
                // 说明现在获取的是根元素
                context.setPreEle(context.getDocument().documentElement);
            } else {
                // 根据父级元素和要查找的元素的名称来获取当前的元素
                var nowEle = context.getNowEle(pEle, this.eleName);
                // 把当前获取的元素放到上下文中
                context.setPreEle(nowEle);
            }

            var ss;
            // 循环调用子元素的interpret方法
            for (var i = 0, len = this.eles.length; i                 ss = this.eles[i].interpret(context);
            }

            // 返回最后一个解释器的解释结果,一般最后一个解释器就是终结符解释器了
            return ss;
        }
    };

    /**
     * 元素作为终结符对应的解释器
     * @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;
        }
    };

    /**
     * 属性作为终结符对应的解释器
     * @param {String} propName [属性的名称]
     */
    function PropertyTerminalExpression(propName) {
        this.propName = propName;
    }

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

先来看看如何使用解释器获取单个元素的值:

复制代码 代码如下:

void function () {
    var c = new Context();
    // 想要获取多个d元素的值,也就是如下表达式的值:“root/a/b/c”
    // 首先要构建解释器的抽象语法树
    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

然后我们再用上面代码获取单个元素的属性的值:

复制代码 代码如下:

 void function () {
        var c = new Context();
        // 想要获取d元素的id属性,也就是如下表达式的值:“a/b/c.name”
        // 这个时候c不是终结了,需要把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));

        // 如果要使用同一个上下文,连续进行解析,需要重新初始化上下文对象
        // 比如,要连续的重新再获取一次属性name的值,当然你可以重新组合元素
        // 重新解析,只要是在使用同一个上下文,就需要重新初始化上下文对象
        c.reInit();
        console.log('重新获取c的属性name值是 = ' + root.interpret(c));
    }();


输出: c的属性name值是 = testC 重新获取c的属性name值是 = testC

讲解:

1.解释器模式功能:

解释器模式使用解释器对象来表示和处理相应的语法规则,一般一个解释器处理一条语法规则。理论上来说,只要能用解释器对象把符合语法的表达式表示出来,而且能够构成抽象的语法树,就可以使用解释器模式来处理。

2.语法规则和解释器

语法规则和解释器之间是有对应关系的,一般一个解释器处理一条语法规则,但是反过来并不成立,一条语法规则是可以有多种解释和处理的,也就是一条语法规则可以对应多个解释器。

3.上下文的公用性

上下文在解释器模式中起着非常重要的作用。由于上下文会被传递到所有的解释器中。因此可以在上下文中存储和访问解释器的状态,比如,前面的解释器可以存储一些数据在上下文中,后面的解释器就可以获取这些值。

另外还可以通过上下文传递一些在解释器外部,但是解释器需要的数据,也可以是一些全局的,公共的数据。

上下文还有一个功能,就是可以提供所有解释器对象的公共功能,类似于对象组合,而不是使用继承来获取公共功能,在每个解释器对象中都可以调用

4.谁来构建抽象语法树

在前面的示例中,是自己在客户端手工构建抽象语法树,是很麻烦的,但是在解释器模式中,并没有涉及这部分功能,只是负责对构建好的抽象语法树进行解释处理。后面会介绍可以提供解析器来实现把表达式转换成为抽象语法树。

还有一个问题,就是一条语法规则是可以对应多个解释器对象的,也就是说同一个元素,是可以转换成多个解释器对象的,这也就意味着同样一个表达式,是可以构成不用的抽象语法树的,这也造成构建抽象语法树变得很困难,而且工作量非常大。

5.谁负责解释操作

只要定义好了抽象语法树,肯定是解释器来负责解释执行。虽然有不同的语法规则,但是解释器不负责选择究竟用哪个解释器对象来解释执行语法规则,选择解释器的功能在构建抽象语法树的时候就完成了。

6.解释器模式的调用顺序

1)创建上下文对象

2)创建多个解释器对象,组合抽象语法树

3)调用解释器对象的解释操作

3.1)通过上下文来存储和访问解释器的状态。

对于非终结符解释器对象,递归调用它所包含的子解释器对象。

解释器模式的本质:*分离实现,解释执行*

解释器模使用一个解释器对象处理一个语法规则的方式,把复杂的功能分离开;然后选择需要被执行的功能,并把这些功能组合成为需要被解释执行的抽象语法树;再按照抽象语法树来解释执行,实现相应的功能。

从表面上看,解释器模式关注的是我们平时不太用到的自定义语法的处理;但从实质上看,解释器模式的思想然后是分离,封装,简化,和很多模式是一样的。

比如,可以使用解释器模式模拟状态模式的功能。如果把解释器模式要处理的语法简化到只有一个状态标记,把解释器看成是对状态的处理对象,对同一个表示状态的语法,可以有很多不用的解释器,也就是有很多不同的处理状态的对象,然后再创建抽象语法树的时候,简化成根据状态的标记来创建相应的解释器,不用再构建树了。

同理,解释器模式可以模拟实现策略模式的功能,装饰器模式的功能等,尤其是模拟装饰器模式的功能,构建抽象语法树的过程,自然就对应成为组合装饰器的过程。

解释器模式执行速度通常不快(大多数时候非常慢),而且错误调试比较困难(附注:虽然调试比较困难,但事实上它降低了错误的发生可能性),但它的优势是显而易见的,它能有效控制模块之间接口的复杂性,对于那种执行频率不高但代码频率足够高,且多样性很强的功能,解释器是非常适合的模式。此外解释器还有一个不太为人所注意的优势,就是它可以方便地跨语言和跨平台。

解释器模式的优缺点:

优点:

1.易于实现语法

在解释器模式中,一条语法规则用一个解释器对象来解释执行。对于解释器的实现来讲,功能就变得比较简单,只需要考虑这一条语法规则的实现就可以了,其他的都不用管。 2.易于扩展新的语法

正是由于采用一个解释器对象负责一条语法规则的方式,使得扩展新的语法非常容易。扩展了新的语法,只需要创建相应的解释器对象,在创建抽象语法树的时候使用这个新的解释器对象就可以了。

缺点:

不适合复杂的语法

如果语法特别复杂,构建解释器模式需要的抽象语法树的工作是非常艰巨的,再加上有可能会需要构建多个抽象语法树。所以解释器模式不太适合复杂的语法。使用语法分析程序或编译器生成器可能会更好一些。

何时使用?

当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式。

在使用解释器模式的时候,还有两个特点需要考虑,一个是语法相对应该比较简单,太负责的语法不适合使用解释器模式玲玲一个是效率要求不是很高,对效率要求很高的,不适合使用。

前面介绍了如何获取单个元素的值和单个元素属性的值,下面来看看如何获取多个元素的值,还有多个元素中相拥名称的值,还有前面的测试都是人工组合好的抽象语法树,我们也顺便实现以下简单的解析器,把符合前面定义的语法的表达式,转换成为前面实现的解释器的抽象语法树: 我就直接把代码贴出来了:

复制代码 代码如下:

 // 读取多个元素或属性的值
    (function () {
        /**
         * 上下文,用来包含解释器需要的一些全局信息
         * @param {String} filePathName [需要读取的xml的路径和名字]
         */
        function Context(filePathName) {
            // 上一个被处理的多个元素
            this.preEles = [];
            // xml的Document对象
            this.document = XmlUtil.getRoot(filePathName);
        }

        Context.prototype = {
            // 重新初始化上下文
            reInit: function () {
                this.preEles = [];
            },
            /**
             *  各个Expression公共使用的方法
             * 根据父元素和当前元素的名称来获取当前元素
             * @param  {Element} pEle    [父元素]
             * @param  {String} eleName [当前元素名称]
             * @return {Element|null}         [找到的当前元素]
             */
            getNowEles: function (pEle, eleName) {
                var elements = [];
                var tempNodeList = pEle.childNodes;
                var nowEle;

                for (var i = 0, len = tempNodeList.length; 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,获取相应的Document对象
        var XmlUtil = {
            getRoot: function (filePathName) {
                var parser = new DOMParser();
                var xmldom = parser.parseFromString('12345d1d2d3d4', 'text/xml');

                return xmldom;
            }
        };

        /**
         * 元素作为非终结符对应的解释器,解释并执行中间元素
         * @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                     if (ele === this.eles[i]) {
                        this.eles.splice(i--, 1);
                    }
                }
                return true;
            },
            interpret: function (context) {
                // 先取出上下文中的当前元素作为父级元素
                // 查找到当前元素名称所对应的xml元素,并设置回到上下文中
                var pEles = context.getPreEles();
                var ele = null;
                var nowEles = [];

                if (!pEles.length) {
                    // 说明现在获取的是根元素
                    ele = context.getDocument().documentElement;
                    pEles.push(ele);
                    context.setPreEles(pEles);
                } else {
                    var tempEle;
                    for (var i = 0, len = pEles.length; i                         tempEle = pEles[i];
                        nowEles = nowEles.concat(context.getNowEles(tempEle, this.eleName));

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

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

                var ss;
                // 循环调用子元素的interpret方法
                for (var i = 0, len = this.eles.length; i                     ss = this.eles[i].interpret(context);
                }

                return ss;
            }
        };

        /**
         * 元素作为终结符对应的解释器
         * @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;
            }
        };

        /**
         * 属性作为终结符对应的解释器
         * @param {String} propName [属性的名称]
         */
        function PropertyTerminalExpression(propName) {
            this.propName = propName;
        }

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

        /**
         * 多个属性作为终结符对应的解释器
         * @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                     ss.push(eles[i].getAttribute(this.propName));
                }

                return ss;
            }
        };

        /**
         * 以多个元素作为终结符的解释处理对象
         * @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                     nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
                }

                var ss = [];

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

                return ss;
            }
        };

        /**
         * 多个元素作为非终结符的解释处理对象
         */
        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                     nowEles = nowEles.concat(context.getNowEles(pEles[i], this.eleName));
                }
                context.setPreEles(nowEles);

                var ss;
                for (i = 0, len = this.eles.length; 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                     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                 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                 console.log('d的属性id的值是 = ' + ss[i]);
            }
        }();

        // 解析器

        /**
         * 解析器的实现思路
         * 1.把客户端传递来的表达式进行分解,分解成为一个一个的元素,并用一个对应的解析模型来封装这个元素的一些信息。
         * 2.根据每个元素的信息,转化成相对应的解析器对象。
         * 3.按照先后顺序,把这些解析器对象组合起来,就得到抽象语法树了。
         *
         * 为什么不把1和2合并,直接分解出一个元素就转换成相应的解析器对象?
         * 1.功能分离,不要让一个方法的功能过于复杂。
         * 2.为了今后的修改和扩展,现在语法简单,所以转换成解析器对象需要考虑的东西少,直接转换也不难,但要是语法复杂了,直接转换就很杂乱了。
         */

        /**
         * 用来封装每一个解析出来的元素对应的属性
         */
        function ParserModel() {
            // 是否单个值
            this.singleValue;
            // 是否属性,不是属性就是元素
            this.propertyValue;
            // 是否终结符
            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;

            // 开始实现第一步-------------------------------------

            /**
             * 传入一个字符串表达式,通过解析,组合成为一个抽象语法树
             * @param  {String} expr [描述要取值的字符串表达式]
             * @return {Object}      [对应的抽象语法树]
             */
            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                     onePath = tokenizer[i];

                    if (tokenizer[i + 1]) {
                        // 还有下一个值,说明这不是最后一个元素
                        // 按照现在的语法,属性必然在最后,因此也不是属性
                        setParsePath(false, onePath, false, mapPath);
                    } else {
                        // 说明到最后了
                        dotIndex = onePath.indexOf(DOT);

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

                            // 设置属性前面的那个元素,自然不是最后一个,也不是属性
                            setParsePath(false, eleName, false, mapPath);
                            // 设置属性,按照现在的语法定义,属性只能是最后一个
                            setParsePath(true, propName, true, mapPath);
                        } else {
                            // 说明是取元素的值,而且是最后一个元素的值
                            setParsePath(true, onePath, false, mapPath);
                        }

                        break;
                    }
                }

                return mapPath;
            }

            /**
             * 按照分解出来的位置和名称来设置需要解析的元素名称
             * @param {Boolean} end           [是否最后一个]
             * @param {String} ele           [元素名称]
             * @param {Boolean} propertyValue [是否取属性]
             * @param {Object} mapPath       [设置需要解析的元素名称,还有该元素对应的解析模型的表]
             */
            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);
            }

            // 开始实现第二步-------------------------------------

            /**
             * 把分解出来的元素名称根据对应的解析模型转换成为相应的解释器对象
             * @param  {Object} mapPath [分解出来的需解析的元素名称,还有该元素对应的解析模型]
             * @return {Array}         [把每个元素转换成为相应的解释器对象后的数组]
             */
            function mapPath2Interpreter(mapPath) {
                va

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
在JavaScript中替换字符串字符在JavaScript中替换字符串字符Mar 11, 2025 am 12:07 AM

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

自定义Google搜索API设置教程自定义Google搜索API设置教程Mar 04, 2025 am 01:06 AM

本教程向您展示了如何将自定义的Google搜索API集成到您的博客或网站中,提供了比标准WordPress主题搜索功能更精致的搜索体验。 令人惊讶的是简单!您将能够将搜索限制为Y

示例颜色json文件示例颜色json文件Mar 03, 2025 am 12:35 AM

本文系列在2017年中期进行了最新信息和新示例。 在此JSON示例中,我们将研究如何使用JSON格式将简单值存储在文件中。 使用键值对符号,我们可以存储任何类型的

10个jQuery语法荧光笔10个jQuery语法荧光笔Mar 02, 2025 am 12:32 AM

增强您的代码演示:开发人员的10个语法荧光笔 在您的网站或博客上共享代码片段是开发人员的常见实践。 选择合适的语法荧光笔可以显着提高可读性和视觉吸引力。 t

构建您自己的Ajax Web应用程序构建您自己的Ajax Web应用程序Mar 09, 2025 am 12:11 AM

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

8令人惊叹的jQuery页面布局插件8令人惊叹的jQuery页面布局插件Mar 06, 2025 am 12:48 AM

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

10 JavaScript和JQuery MVC教程10 JavaScript和JQuery MVC教程Mar 02, 2025 am 01:16 AM

本文介绍了关于JavaScript和JQuery模型视图控制器(MVC)框架的10多个教程的精选选择,非常适合在新的一年中提高您的网络开发技能。 这些教程涵盖了来自Foundatio的一系列主题

什么是这个'在JavaScript?什么是这个'在JavaScript?Mar 04, 2025 am 01:15 AM

核心要点 JavaScript 中的 this 通常指代“拥有”该方法的对象,但具体取决于函数的调用方式。 没有当前对象时,this 指代全局对象。在 Web 浏览器中,它由 window 表示。 调用函数时,this 保持全局对象;但调用对象构造函数或其任何方法时,this 指代对象的实例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。这些方法使用给定的 this 值和参数调用函数。 JavaScript 是一门优秀的编程语言。几年前,这句话可

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境