首頁  >  文章  >  web前端  >  用函數模板,寫一個簡單高效的 JSON 查詢器的方法介紹_javascript技巧

用函數模板,寫一個簡單高效的 JSON 查詢器的方法介紹_javascript技巧

WBOY
WBOY原創
2016-05-16 17:36:40887瀏覽

JSON可謂是JavaScript的亮點,它能用優雅簡練的程式碼實作Object和Array的初始化。同樣是基於文字的資料定義,它比符號分隔更有語義,比XML更簡潔。因此在越來越多的JS開發中,使用它作為資料的傳輸和儲存。

JS陣列內建了不少有用的方法,方便我們對資料的查詢和篩選。例如我們有一堆資料:

複製程式碼 程式碼如下:

var heros = [
var heros = [
        // 名============攻=====防=======力量====敏捷=====智力====
        { name:'冰室女巫', DP:38, AP:1.3, Str:16, Agi:16, Int:21},
        {name:'沉默術士', DP:39, AP:1.1, StrStr: 17, Agi:16, Int:21},
        {name:'娜迦海妖', DP:51, AP:6.0, Str:21, Agi:21, Int:18},
 :'賞金獵人', 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},
 '煉金術士', DP:49, AP:0.6, Str:25, Agi:11, Int:25}
        //...
    ];
...

    ];

複製程式碼


程式碼如下:

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

回傳得到一個陣列,包括符合條件的2個結果。 比起手工去寫循環判斷,filter方法為我們提供了很大的方便。但它是基於函數回呼的,所以每次使用必須寫一個function,對於簡單的查詢很是累贅,而且使用回呼效率也大大降低。但這是也沒辦法的,想簡單必然要犧牲一定性能。 如果能使用比這更簡單的語句,並且完全擁有程式碼展開時效率,該有是多麼完美的事。

先來想像下,要是能將上面的程式碼寫成這樣,並且查詢速度和手寫的遍歷判斷一樣
複製程式碼


程式碼如下:

    var match = heros.select('@DP>40 AND @AP看起來有點像SQL,連文法都換了?這樣豈不是要寫一個詞法分析,語意解釋等等等等一大堆的腳本引擎的功能了,沒個幾千上萬行程式碼都搞不定,而且效率肯定更糟了。 。 。如果想到那麼複雜,那麼你還沒深刻的理解腳本的精髓。但凡是腳本語言,都有運行時動態解釋代碼的接口,例如vbs的execute();js的eval(),new Function(),甚至創建一個<script>動態寫入代碼。 <P>顯然,如果能將另一種語言,翻譯成js程式碼,那麼就可直接交給宿主來執行了! <P> <P>例如上面select中的字符,我們簡單的將"@"替換成"e.", "AND"替換成"&&",於是就成了一個合法的js表達​​式,完全可以交給eval來執行。 <P>所以我們要做的,就是將原始語句翻譯成js語句來執行。並且為了提高效率,將翻譯好的js表達​​式內聯到一個上下文環境,產生一個可執行的函數體,而不是每次遍歷中都依靠回調來判斷。 <STRONG> <br>於是,函數模版就要派上用場了。 <br> <BR>函數模版簡介<div class="codetitle"><span><a style="CURSOR: pointer" data="84492" class="copybut" id="copybut84492" onclick="doCopy('code84492')">在C 裡面,有宏和類模版這麼東西,可以讓一些計算在編譯階段就完成了,大幅提升了運行時代碼的性能。雖然腳本沒有嚴格意義上的編譯,但在第一次執行的時候會解析並充分的最佳化,這是目前主流瀏覽器相互競爭點。所以,我們要將重複eval的程式碼,鑲嵌到事先提供的樣板函數裡:一個準備就緒,就差表達式計算的函數:<U>複製程式碼<🎜 ><🎜><🎜> 程式碼如下:<🎜><div class="codebody" id="code84492"><BR> /**<BR>     * 模版: tmplCount<BR>     * 功能: 統計arr陣列中符合$express表達式的數量<BR>    */<BR>    function tmplCount(arr) {<BR>        var count = 0; <P>        for(var i = 0; i < arr.length; i ) {<BR>            var e = arr[i]; <P>            if($express) {<BR>                 }<BR>        return count;<BR>    }<BR><BR><BR>上面就是一個範本函數,遍歷參數arr []並統計符合$express的數量。除了if(...)內的表達式外,其他都已經準備就緒了。字元$express也可以換成其他標識,只要不和函數內其他字元衝突即可。 <BR>當我們需要實例化時,首先透過tmplCount.toString()將函數轉成字串格式,然後將其中的$express替換成我們想要的表達式,最後eval這串字符,得到一個Function類型的變量,一個模板函數的實例就產生了! <P>我們簡單的示範下:<P><STRONG><BR><div class="codetitle"><span>複製程式碼<a style="CURSOR: pointer" data="65929" class="copybut" id="copybut65929" onclick="doCopy('code65929')"><U> 程式碼如下:<div class="codebody" id="code65929"> 程式碼如下:<BR><BR> <BR> /**<BR>     * 函數: createInstance<BR>     * 參數: exp<BR>     *          回傳一個Function,模版tmplCount的例<BR>    */<BR>    function createInstance(exp)<BR>    {<BR>                           .replace ('$express', exp);<BR><BR>        // 防止匿名函數直接eval封包錯誤<br>        var fn = eval('0,' code);<br> <BR>        // 回傳範本實例        return fn;<P>    }<BR> <BR>    // 測試參數<P>    var student = [<BR>        {name: 'Jane' {name: 'Adam', age: 18}<BR>    ];<BR> <BR>    // demo1<BR>    var f1 = createInstance('e.age<16');<BR>    alert(f1(student));   11個 <P>    // demo2<BR>    var f2 = createInstance('e.name!="Jack" && e.age>=14');<BR>    alert(f2(student)); ><P>注意createInstance()的參數中,有個叫e的對象,它是在tmplCount模版中定義的,指代遍歷時的具體元素。傳回的f1,f2就是tmplCount模板的兩個實例。在最終調用的f1,f2函數中,已經內嵌了我們的表達式語句,就像我們事先寫了兩個同樣函數的函數一樣,所以在遍歷的時候直接運行表達式,而不用回調什麼的,效率大幅提升。 <BR> <BR><BR> <BR>其實說穿了,tmplCount的存在只是為了提供這個函數的字串而已,本身從來就不會被呼叫。事實上用字串的形式定義也一樣,只不過用函數書寫比較直觀,方便測試。 <P>值得注意的是,如果腳本後期需要壓縮優化,那麼tmplCount模板絕對不能參與,否則對應的"e."和"$express"都有可能改變。 <IMG alt="" src="http://files.jb51.net/file_images/article/201304/201304171146157.png"> <P>JSON基本查詢功能<P> 函數範本的用處和實作介紹完了,再來回頭看之前的JSON查詢語言。我們只要將類似sql的語句,翻譯成js表達式,並且產生一個函數模板實例。對於相同的語句,我們可以進行緩存,避免每次都翻譯。 <P> <STRONG>首先我們實作查詢器的範本:<P><P><STRONG><BR>複製程式碼<div class="codetitle"><span><a style="CURSOR: pointer" data="48175" class="copybut" id="copybut48175" onclick="doCopy('code48175')">複製程式碼<U> 程式碼<div class="codebody" id="code48175"><BR> var __proto = Object.prototype; <P>    //<BR>    // 範本: __tmpl<BR>    // 參數: $C<BR>    // 說明: 記錄並傳回_list物件中符合$C的元素集合< var __tmpl = function(_list) {<BR>        var _ret = [];<BR>        var _i = -1;<BR> <BR>        for(var _k in _list) {            var _e = _list[_k]< proto[_k]) {<P>                if($C)<BR>                    _ret[ _i] = _e;<br>         return _ret;<br> <BR>    }.toString();<BR><BR><BR><BR>然後開始寫Object的select方法:<P><BR><BR><STRONG><BR><div class="codetitle"><> 🎜><span><a style="CURSOR: pointer" data="74878" class="copybut" id="copybut74878" onclick="doCopy('code74878')"> 程式碼如下:<U> //    // select方法實作    //<div class="codebody" id="code74878">  // select方法實作<BR>    //<BR> __proto.select = function(exp) {<BR>        if(!exp)<BR>            return [];<br> <br>        var fn = __cache[exp];<BR> <BR>        try {            if(!fn) {<P>           //解釋表達式                code = __tmpl.replace('$C', code);   到模版<P> <BR>                fn = __cache[exp] = __compile(code);    //實例化函數<BR>   <BR>            return fn(this);                          catch(e) {            return [];<P>       其中__cache表實作了查詢語句的快取。對於重複的查詢,效能可以極大的提升。 <BR><P><BR><BR>複製碼<BR><BR><BR> 代碼如下:<BR><BR>    function 🎜><BR><div class="codetitle">    function 🎜><span><a style="CURSOR: pointer" data="94768" class="copybut" id="copybut94768" onclick="doCopy('code94768')">  0,' arguments[0]);<U>    } __compile之所以單獨寫在一個空函數裡,就是為了eval的時候有個盡可能乾淨的上下文環境。 <div class="codebody" id="code94768">__interpret是整個系統的重中之重,負責將查詢語句翻譯成js語句。它的實現見智見仁,但盡可能簡單,不要過度分析語法。 <BR> <BR>具體程式碼檢視:<BR>jsonselect.rar<BR>出於演示,目前只實現部分基本功能。以後還可以再加上 LIKE,BETWEEN,ORDER BY 等等常用的功能。 <BR> <P>Demo<P><A href="http://xiazai.jb51.net/201304/yuanma/jsonselect.rar" target=_blank><BR>複製程式碼<P><STRONG><BR> 程式碼如下:<div class="codebody" id="code75283"><BR>var heros = [<BR>        // 名============攻=====防=======力量====敏捷== ===智力====<BR>        {name:'冰室女巫', DP:38, AP:1.3, Str:16, Agi:16, Int:21},<BR>     ', DP:39, AP:1.1, Str:17, Agi:16, Int:21},<BR>        {name:'娜迦海妖', DP:51, AP:6.0, Str:21, Agigi: 21, Int:18},<BR>        {name:'賞金獵人', DP:39, AP:4.0, Str:17, Agi:21, Int:16},< ', DP:45, AP:3.1, Str:18, Agi:22, Int:15},<BR>        {name:'光之守衛', DP:38, AP:1.1, Str:16, Agi:15 , Int:22},<BR>        {name:'煉金術士', DP:49, AP:0.6, Str:25, Agi:11,Int:25}<>   <BR><BR><BR><BR><BR><div class="codetitle">複製程式碼<span><a style="CURSOR: pointer" data="60993" class="copybut" id="copybut60993" onclick="doCopy('code60993')"><U> 程式碼如下: 程式碼如下:<div class="codebody" id="code60993"><BR><BR>敏捷都超過20的<BR>    // 結果:娜迦海妖    var match = heros.select('@Str>20 AND @Agi>20');<P> <BR>    // 查詢:「士」結尾的<BR>    // 結果:沉默術士,劇毒術士,煉金術士    var match = heros.select('right(@name,1)="士" ');<P> <BR>    // 查詢:生命值超過500的<BR>    // 結果:煉金術士<BR>    var match = heros.select('100 @Str*19 > 500');<🜎 ><BR></script>
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn