欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

用函數(shù)模板,寫一個簡單高效的 JSON 查詢器的方法介紹

 更新時間:2013年04月17日 12:18:19   作者:  
本篇文章小編將為大家介紹,用函數(shù)模板,寫一個簡單高效的 JSON 查詢器的方法介紹,需要的朋友可以參考一下

JSON可謂是JavaScript的亮點,它能用優(yōu)雅簡練的代碼實現(xiàn)Object和Array的初始化。同樣是基于文本的數(shù)據(jù)定義,它比符號分隔更有語義,比XML更簡潔。因此越來越多的JS開發(fā)中,使用它作為數(shù)據(jù)的傳輸和儲存。

JS數(shù)組內(nèi)置了不少有用的方法,方便我們對數(shù)據(jù)的查詢和篩選。例如我們有一堆數(shù)據(jù):

復制代碼 代碼如下:

var heros = [
        // 名============攻=====防=======力量====敏捷=====智力====
        {name:'冰室女巫', DP:38, AP:1.3, Str:16, Agi:16, Int:21},
        {name:'沉默術(shù)士', 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:'劇毒術(shù)士', DP:45, AP:3.1, Str:18, Agi:22, Int:15},
        {name:'光之守衛(wèi)', DP:38, AP:1.1, Str:16, Agi:15, Int:22},
        {name:'煉金術(shù)士', 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;
    });

返回得到一個數(shù)組,包括符合條件的2個結(jié)果。

相比手工去寫循環(huán)判斷,filter方法為我們提供了很大的方便。但它是基于函數(shù)回調(diào)的,所以每次使用必須寫一個function,對于簡單的查詢很是累贅,而且使用回調(diào)效率也大大降低。但這是也沒有辦法的,想簡單必然要犧牲一定性能。 如果能使用比這更簡單的語句,并且完全擁有代碼展開時效率,該有是多么完美的事。

先來想象下,要是能將上面的代碼寫成這樣,并且查詢速度和手寫的遍歷判斷一樣

復制代碼 代碼如下:

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

看上去有點像SQL,連語法都換了?這樣豈不是要寫一個詞法分析,語義解釋等等等等一大堆的腳本引擎的功能了,沒個幾千上萬行代碼都搞不定,而且效率肯定更糟了。。。如果想到那么復雜,那么你還沒深刻的理解腳本的精髓。但凡是腳本語言,都有運行時動態(tài)解釋代碼的接口,例如vbs的execute();js的eval(),new Function(),甚至創(chuàng)建一個<script>動態(tài)寫入代碼。

顯然,要是能將另一種語言,翻譯成js代碼,那么就可直接交給宿主來執(zhí)行了!

例如上面select中的字符,我們簡單的將"@"替換成"e.", "AND"替換成"&&",于是就成了一個合法的js表達式,完全可以交給eval來執(zhí)行。

所以我們要做的,就是將原始語句翻譯成js語句來執(zhí)行。并且為了提高效率,將翻譯好的js表達式內(nèi)聯(lián)到一個上下文環(huán)境,生成一個可執(zhí)行的函數(shù)體,而不是每次遍歷中都依靠回調(diào)來判斷。

于是,函數(shù)模版就要派上用場了。

函數(shù)模版簡介

在C++里面,有宏和類模版這么個東西,可以讓一些計算在編譯階段就完成了,大幅提升了運行時代碼的性能。腳本雖然沒有嚴格意義上的編譯,但在第一次執(zhí)行的時候會解析并充分的優(yōu)化,這是目前主流瀏覽器相互競爭點。所以,我們要將重復eval的代碼,鑲嵌到事先提供的樣板函數(shù)里:一個準備就緒,就差表達式計算的函數(shù):

復制代碼 代碼如下:

 /**
     * 模版: tmplCount
     * 功能: 統(tǒng)計arr數(shù)組中符合$express表達式的數(shù)量
     */
    function tmplCount(arr) {
        var count = 0;

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

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


上面就是一個模板函數(shù),遍歷參數(shù)arr[]并統(tǒng)計符合$express的數(shù)量。除了if(...)內(nèi)的表達式外,其他都已經(jīng)準備就緒了。字符$express也可以換成其他標識,只要不和函數(shù)內(nèi)其他字符沖突即可。

當我們需要實例化時,首先通過tmplCount.toString()將函數(shù)轉(zhuǎn)成字符串格式,然后將其中的$express替換成我們想要的表達式,最后eval這串字符,得到一個Function類型的變量,一個模板函數(shù)的實例就產(chǎn)生了!

我們簡單的演示下:

復制代碼 代碼如下:

 /**
     * 函數(shù): createInstance
     * 參數(shù): exp
     *      一段js表達式字符串,用來替換tmplCount模板的$express
     * 返回:
     *      返回一個Function,模版tmplCount的實例
     */
    function createInstance(exp)
    {
        // 替換模板內(nèi)的表達式
        var code = tmplCount.toString()
                    .replace('$express', exp);

        // 防止匿名函數(shù)直接eval報錯
        var fn = eval('0,' + code);

        // 返回模板實例
        return fn;
    }


    // 測試參數(shù)
    var student = [
        {name: 'Jane', age: 14},
        {name: 'Jack', age: 20},
        {name: 'Adam', age: 18}
    ];

    // demo1
    var f1 = createInstance('e.age<16');
    alert(f1(student));    //1個

    // demo2
    var f2 = createInstance('e.name!="Jack" && e.age>=14');
    alert(f2(student));    //2個


注意createInstance()的參數(shù)中,有個叫e的對象,它是在tmplCount模版中定義的,指代遍歷時的具體元素。返回的f1,f2就是tmplCount模板的兩個實例。最終調(diào)用的f1,f2函數(shù)中,已經(jīng)內(nèi)嵌了我們的表達式語句,就像我們事先寫了兩個同樣功能的函數(shù)一樣,所以在遍歷的時候直接運行表達式,而不用回調(diào)什么的,效率大幅提升。

其實說白了,tmplCount的存在僅僅是為了提供這個函數(shù)的字符串而已,其本身從來不會被調(diào)用。事實上用字符串的形式定義也一樣,只不過用函數(shù)書寫比較直觀,方便測試。

值得注意的是,如果腳本后期需要壓縮優(yōu)化,那么tmplCount模板絕對不能參與,否則對應(yīng)的"e."和"$express"都有可能發(fā)生變化。

JSON基本查詢功能

函數(shù)模板的用處和實現(xiàn)介紹完了,再來回頭看之前的JSON查詢語言。我們只需將類似sql的語句,翻譯成js表達式,并且生成一個函數(shù)模板實例。對于相同的語句,我們可以進行緩存,避免每次都翻譯。

首先我們實現(xiàn)查詢器的模板:

復制代碼 代碼如下:

 var __proto = Object.prototype;

    //
    // 模板: __tmpl
    // 參數(shù): $C
    // 說明: 記錄并返回_list對象中匹配$C的元素集合
    //
    var __tmpl = function(_list) {
        var _ret = [];
        var _i = -1;

        for(var _k in _list) {
            var _e = _list[_k];

            if(_e && _e != __proto[_k]) {
                if($C)
                    _ret[++_i] = _e;
            }
        }
        return _ret;

    }.toString();


然后開始寫Object的select方法:
復制代碼 代碼如下:

 //
    // select方法實現(xiàn)
    //
    var __cache = {};

    __proto.select = function(exp) {
        if(!exp)
            return [];

        var fn = __cache[exp];

        try {
            if(!fn) {
                var code = __interpret(exp);            //解釋表達式
                code = __tmpl.replace('$C', code);      //應(yīng)用到模版

                fn = __cache[exp] = __compile(code);    //實例化函數(shù)
            }

            return fn(this);                            //查詢當前對象
        }
        catch(e) {
            return [];
        }
    }


其中__cache表實現(xiàn)了查詢語句的緩存。對于重復的查詢,性能可以極大的提升。
復制代碼 代碼如下:

    function __compile() {
        return eval('0,' + arguments[0]);
    }

 __compile之所以單獨寫在一個空函數(shù)里,就是為了eval的時候有個盡可能干凈的上下文環(huán)境。

__interpret是整個系統(tǒng)的重中之重,負責將查詢語句翻譯成js語句。它的實現(xiàn)見智見仁,但盡可能簡單,不要過度分析語法。

具體代碼查看:jsonselect.rar
出于演示,目前只實現(xiàn)部分基本功能。以后還可以加上 LIKE,BETWEEN,ORDER BY 等等常用的功能。

Demo

復制代碼 代碼如下:

var heros = [
        // 名============攻=====防=======力量====敏捷=====智力====
        {name:'冰室女巫', DP:38, AP:1.3, Str:16, Agi:16, Int:21},
        {name:'沉默術(shù)士', 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:'劇毒術(shù)士', DP:45, AP:3.1, Str:18, Agi:22, Int:15},
        {name:'光之守衛(wèi)', DP:38, AP:1.1, Str:16, Agi:15, Int:22},
        {name:'煉金術(shù)士', DP:49, AP:0.6, Str:25, Agi:11, Int:25}
        //...
    ];

復制代碼 代碼如下:

 // 查詢:力量,敏捷 都超過20的
    // 結(jié)果:娜迦海妖
    var match = heros.select('@Str>20 AND @Agi>20');

    // 查詢:“士”結(jié)尾的
    // 結(jié)果:沉默術(shù)士,劇毒術(shù)士,煉金術(shù)士
    var match = heros.select('right(@name,1)="士" ');

    // 查詢:生命值 超過500的
    // 結(jié)果:煉金術(shù)士
    var match = heros.select('100 + @Str*19 > 500');


相關(guān)文章

  • JS代碼簡潔方式之函數(shù)方法詳解

    JS代碼簡潔方式之函數(shù)方法詳解

    這篇文章主要介紹了JS代碼簡潔方式之函數(shù)方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • 微信小程序開發(fā)animation心跳動畫效果

    微信小程序開發(fā)animation心跳動畫效果

    這篇文章主要為大家詳細介紹了微信小程序開發(fā)animation心跳動畫效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • JS+DIV實現(xiàn)拖動效果

    JS+DIV實現(xiàn)拖動效果

    這篇文章主要為大家詳細介紹了JS+DIV實現(xiàn)拖動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • JavaScript中Array方法你該知道的正確打開方法

    JavaScript中Array方法你該知道的正確打開方法

    string和array作為javascript內(nèi)置對象,其中許多方法無論是在開發(fā)過程中,還是在面試的時候都有機會被面試官問到,下面這篇文章主要給大家介紹了關(guān)于JavaScript中Array方法你該知道的正確打開方法,需要的朋友可以參考下
    2018-09-09
  • JS數(shù)據(jù)雙向綁定原理與用法實例分析

    JS數(shù)據(jù)雙向綁定原理與用法實例分析

    這篇文章主要介紹了JS數(shù)據(jù)雙向綁定原理與用法,結(jié)合實例形式分析了JavaScript數(shù)據(jù)雙向綁定相關(guān)原理、實現(xiàn)技巧與操作注意事項,需要的朋友可以參考下
    2019-11-11
  • TypeScript類型聲明書寫詳解

    TypeScript類型聲明書寫詳解

    這篇文章主要介紹了TypeScript類型聲明書寫詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • javascript彈出層輸入框(示例代碼)

    javascript彈出層輸入框(示例代碼)

    這篇文章主要介紹了javascript彈出層輸入框(示例代碼)。需要的朋友可以過來參考下,希望對大家有所幫助
    2013-12-12
  • js函數(shù)setTimeout延遲執(zhí)行的簡單介紹

    js函數(shù)setTimeout延遲執(zhí)行的簡單介紹

    設(shè)置指定的JS函數(shù)在指定的時間后執(zhí)行,可以利用setTimeout()函數(shù)。
    2013-07-07
  • 圖形編輯器中JS實現(xiàn)防誤操作之拖拽阻塞

    圖形編輯器中JS實現(xiàn)防誤操作之拖拽阻塞

    這篇文章主要為大家介紹了圖形編輯器中JS實現(xiàn)防誤操作之拖拽阻塞實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • javascript按順序加載運行js方法

    javascript按順序加載運行js方法

    本篇文章主要教給大家如何在javascript中動態(tài)加載按順序加載運行js的方法以及實現(xiàn)代碼,需要的朋友參考學習下吧。
    2017-12-12

最新評論