JSON is the highlight of JavaScript, which enables the initialization of object and array with elegant and concise code. The same is text-based data definition, it is more semantic than symbolic separation, more concise than XML. Therefore, more and more JS development, using it as data transmission and storage.
JS Array built-in a number of useful methods to facilitate our data query and screening. For example, we have a bunch of data:
Copy Code code as follows:
var heros = [
Name ============ attack ===== ======= Strength = = Agile ===== Intelligence = =
{name: ' Ice room witch ', dp:38, ap:1.3, Str:16, Agi:16, int:21},
{name: ' Silent Warlock ', dp:39, ap:1.1, Str:17, Agi:16, int:21},
{name: ' Naga Hai Demon ', dp:51, ap:6.0, str:21, agi:21, int:18},
{Name: ' bounty hunter ', dp:39, ap:4.0, Str:17, agi:21, int:16},
{name: ' Toxic Warlock ', dp:45, ap:3.1, str:18, agi:22, int:15},
{name: ' Guard of Light ', dp:38, ap:1.1, Str:16, agi:15, int:22},
{name: ' Alchemist ', dp:49, ap:0.6, str:25, Agi:11, int:25}
//...
];
To query for Heroes with attacks greater than 40 and defending less than 4, we can use the array filter method:
Copy Code code as follows:
var match = Heros.filter (function (e) {
return E.DP > && E.ap < 4;
});
Returns an array that includes 2 results that match the criteria.
The filter method provides us with great convenience compared to the manual to write cycle judgment. But it is based on a function callback, so each use must write a function, is cumbersome for simple queries, and the use of callback efficiency is greatly reduced. But this is also no way, want simple must sacrifice certain performance. How perfect it would be if you could use a simpler statement and have full efficiency when the code is expanded.
First imagine, if you can write the above code like this, and query speed and handwritten traversal judgment like :
Copy Code code as follows:
var match = Heros.select (' @DP >40 and @AP <4 ');
Look a bit like SQL, even grammar changed? This is not to write a lexical analysis, semantic interpretation, and so on a lot of script engine functions, not a thousands of of thousands of lines of code are uncertain, and efficiency must be worse ... If you think it's so complicated, you don't have a deep understanding of the essence of the script. But in any script language, there is an interface for Run-time dynamic interpretation code, such as execute () of the VBS, the eval () of JS, the new Function (), and even the creation of a <script> dynamic write code.
Obviously, if you can translate another language into the JS code, then you can directly to the host to execute!
For example, the characters in the select above, we simply replace "@" with "E.", "and" replaced with "&&", and then become a legitimate JS expression, can be completely handed to eval to execute.
So what we want to do is to translate the original statement into a JS statement to execute. And in order to improve efficiency, the translation of JS expression inline to a context, to generate an executable function body, rather than every traversal depends on the callback to judge.
As a result, functional templates will come in handy.
Introduction to Functional templates
In C + +, there is a macro and a class template, which allows some computations to be completed in the compile phase, dramatically improving the performance of Run-time code. Although the script is not strictly compiled, it will be parsed and optimized at the first execution, which is the current dominant browser competing points. So, we're going to set up the code that repeats the eval into a previously provided boilerplate function: a function that is ready to be evaluated on the difference expression:
Copy Code code as follows:
/**
* Template: Tmplcount
* Function: Statistics arr The number of $express expressions in the array
*/
function Tmplcount (arr) {
var count = 0;
for (var i = 0; i < arr.length; i++) {
var e = arr[i];
if ($express) {
count++;
}
}
return count;
}
The above is a template function that traverses the parameter arr[] and counts the number of matches $express. except if (...) All the other expressions are ready. Character $express can also be replaced with other identities, as long as they do not conflict with other characters within the function.
When we need to instantiate, we first turn the function into a string format by Tmplcount.tostring (), and then replace the $express with the expression we want, finally eval the string of characters, and get a function type variable, An instance of a template function is generated!
We have a simple demo:
Copy Code code as follows:
/**
* Function: CreateInstance
* Parameter: EXP
* A section of JS expression string, used to replace the Tmplcount template $express
Returns
* Returns an instance of a function, template Tmplcount
*/
function CreateInstance (exp)
{
Replace an expression within a template
var code = tmplcount.tostring ()
. replace (' $express ', exp);
Prevent anonymous functions from directly eval an error
var fn = eval (' 0, ' + code);
Return template Instance
return FN;
}
Test parameters
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 A
Demo2
var F2 = CreateInstance (' e.name!= "Jack" && e.age>=14 ');
Alert (F2 (student)); 2 A
Note that in the parameter of CreateInstance (), there is an object called E, which is defined in the Tmplcount template and refers to the concrete elements of the traversal. The F1,F2 returned is the two instances of the Tmplcount template. In the final invocation of the F1,F2 function, we have embedded our expression statements, just as we have written two functions of the same function in advance, so we run the expression directly in the traversal, without having to recall anything, and the efficiency is greatly increased.
In fact, the existence of Tmplcount is simply to provide the string of this function, which is never invoked. In fact, the form of a string to define the same, but the use of functional writing is more intuitive, easy to test.
It is worth noting that if the script needs to compress optimizations later on, then the Tmplcount template will never participate, otherwise the corresponding "E." and "$express" are likely to change.
JSON basic query functionality
The usefulness and implementation of the function template is finished, and then look back at the JSON query language before. We simply translate SQL-like statements into JS expressions and generate a function template instance. For the same statement, we can cache, to avoid translation every time.
First we implement the template for the query:
Copy Code code as follows:
var __proto = Object.prototype;
//
Template: __tmpl
Parameters: $C
Description: Logs and returns the collection of elements matching $c in the _list object
//
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 ();
and then start writing the Select method of object:
Copy Code code as follows:
//
The Select method implements
//
var __cache = {};
__proto.select = function (exp) {
if (!EXP)
return [];
var fn = __cache[exp];
try {
if (!FN) {
var code = __INTERPRET (exp); Interpreting an expression
Code = __tmpl.replace (' $C ', code); Apply to Template
fn = __cache[exp] = __compile (code); Instantiating functions
}
RETURN FN (this); Querying the current object
}
catch (e) {
return [];
}
}
The __cache table implements the caching of query statements. For repetitive queries, performance can be greatly improved.
Copy Code code as follows:
function __compile () {
Return eval (' 0, ' + arguments[0]);
}
The reason why __compile is written in an empty function alone is to have as clean a context as possible for Eval.
__interpret is the most important of the whole system and is responsible for translating the query statements into JS statements. Its implementation see CHI, but as simple as possible, do not overdo parsing syntax.
Specific code view: Jsonselect.rar
For demonstration purposes, only some of the basic functionality is currently implemented. In the future, you can add like,between,order by and other common functions.
Demo
Copy Code code as follows:
var heros = [
Name ============ attack ===== ======= Strength = = Agile ===== Intelligence = =
{name: ' Ice room witch ', dp:38, ap:1.3, Str:16, Agi:16, int:21},
{name: ' Silent Warlock ', dp:39, ap:1.1, Str:17, Agi:16, int:21},
{name: ' Naga Hai Demon ', dp:51, ap:6.0, str:21, agi:21, int:18},
{Name: ' bounty hunter ', dp:39, ap:4.0, Str:17, agi:21, int:16},
{name: ' Toxic Warlock ', dp:45, ap:3.1, str:18, agi:22, int:15},
{name: ' Guard of Light ', dp:38, ap:1.1, Str:16, agi:15, int:22},
{name: ' Alchemist ', dp:49, ap:0.6, str:25, Agi:11, int:25}
//...
];
Copy Code code as follows:
Query: Strength, agility are more than 20 of
Result: Naga Hai Demon
var match = Heros.select (' @Str >20 and @Agi >20 ');
Inquiry: The end of "Shi"
Result: Silent Warlock, Poisonous warlock, alchemist
var match = Heros.select (' Right (@name, 1) = "scholar");
Query: More than 500 of the life value
Result: Alchemist
var match = Heros.select (' + @Str *19 > 500 ');