用函数模板,写一个简单高效的 JSON 查询器的方法

JSON可谓是JavaScript的亮点,它能用优雅简练的代码实现Object和Array的初始化。同样是基于文本的数据定义,它比符号分隔更有语义,比XML更简洁。因此越来越多的JS开发中,使用它作为数据的传输和储存。

JS数组内置了不少有用的方法,方便我们对数据的查询和筛选。例如我们有一堆数据:

复制代码 代码如下:


var heros = [
        // 名============攻=====防=======力量====敏捷=====智力====
        {name:'冰室女巫', DP:38, AP:1.3, Str:16, Agi:16, Int:21},
        {name:'沉默术士', DP:39, AP:1.1, Str:17, Agi:16, Int:21},
        {name:'娜迦海妖', DP:51, AP:6.0, Str:21, Agi:21, Int:18},
        {name:'赏金猎人', DP:39, AP:4.0, Str:17, Agi:21, Int:16},
        {name:'剧毒术士', DP:45, AP:3.1, Str:18, Agi:22, Int:15},
        {name:'光之守卫', DP:38, AP:1.1, Str:16, Agi:15, Int:22},
        {name:'炼金术士', DP:49, AP:0.6, Str:25, Agi:11, Int:25}
        //...
    ];


要查询攻击大于40并且防御小于4的英雄,我们可以用Array的filter方法:

复制代码 代码如下:


 var match = heros.filter(function(e) {
        return e.DP > 40 && e.AP < 4;
    });


返回得到一个数组,包括符合条件的2个结果。

相比手工去写循环判断,filter方法为我们提供了很大的方便。但它是基于函数回调的,所以每次使用必须写一个function,对于简单的查询很是累赘,而且使用回调效率也大大降低。但这是也没有办法的,想简单必然要牺牲一定性能。 如果能使用比这更简单的语句,并且完全拥有代码展开时效率,该有是多么完美的事。

先来想象下,要是能将上面的代码写成这样,并且查询速度和手写的遍历判断一样:

复制代码 代码如下:


    var match = heros.select('@DP>40 AND @AP<4');


看上去有点像SQL,连语法都换了?这样岂不是要写一个词法分析,语义解释等等等等一大堆的脚本引擎的功能了,没个几千上万行代码都搞不定,而且效率肯定更糟了。。。如果想到那么复杂,那么你还没深刻的理解脚本的精髓。但凡是脚本语言,都有运行时动态解释代码的接口,例如vbs的execute();js的eval(),new Function(),甚至创建一个<script>动态写入代码。

显然,要是能将另一种语言,翻译成js代码,那么就可直接交给宿主来执行了!

例如上面select中的字符,我们简单的将"@"替换成"e.", "AND"替换成"&&",于是就成了一个合法的js表达式,完全可以交给eval来执行。

所以我们要做的,就是将原始语句翻译成js语句来执行。并且为了提高效率,将翻译好的js表达式内联到一个上下文环境,生成一个可执行的函数体,而不是每次遍历中都依靠回调来判断。

于是,函数模版就要派上用场了。

函数模版简介

在C++里面,有宏和类模版这么个东西,可以让一些计算在编译阶段就完成了,大幅提升了运行时代码的性能。脚本虽然没有严格意义上的编译,但在第一次执行的时候会解析并充分的优化,这是目前主流浏览器相互竞争点。所以,我们要将重复eval的代码,镶嵌到事先提供的样板函数里:一个准备就绪,就差表达式计算的函数:

复制代码 代码如下:


 /**
     * 模版: tmplCount
     * 功能: 统计arr数组中符合$express表达式的数量
     */
    function tmplCount(arr) {
        var count = 0;

for(var i = 0; i < arr.length; i++) {
            var e = arr[i];

if($express) {
                count++;
            }
        }
        return count;
    }


上面就是一个模板函数,遍历参数arr[]并统计符合$express的数量。除了if(...)内的表达式外,其他都已经准备就绪了。字符$express也可以换成其他标识,只要不和函数内其他字符冲突即可。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdgxjx.html