当我们需要实例化时,首先通过tmplCount.toString()将函数转成字符串格式,然后将其中的$express替换成我们想要的表达式,最后eval这串字符,得到一个Function类型的变量,一个模板函数的实例就产生了!
我们简单的演示下:
复制代码 代码如下:
/**
* 函数: createInstance
* 参数: exp
* 一段js表达式字符串,用来替换tmplCount模板的$express
* 返回:
* 返回一个Function,模版tmplCount的实例
*/
function createInstance(exp)
{
// 替换模板内的表达式
var code = tmplCount.toString()
.replace('$express', exp);
// 防止匿名函数直接eval报错
var fn = eval('0,' + code);
// 返回模板实例
return fn;
}
// 测试参数
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()的参数中,有个叫e的对象,它是在tmplCount模版中定义的,指代遍历时的具体元素。返回的f1,f2就是tmplCount模板的两个实例。最终调用的f1,f2函数中,已经内嵌了我们的表达式语句,就像我们事先写了两个同样功能的函数一样,所以在遍历的时候直接运行表达式,而不用回调什么的,效率大幅提升。
其实说白了,tmplCount的存在仅仅是为了提供这个函数的字符串而已,其本身从来不会被调用。事实上用字符串的形式定义也一样,只不过用函数书写比较直观,方便测试。
值得注意的是,如果脚本后期需要压缩优化,那么tmplCount模板绝对不能参与,否则对应的"e."和"$express"都有可能发生变化。
JSON基本查询功能
函数模板的用处和实现介绍完了,再来回头看之前的JSON查询语言。我们只需将类似sql的语句,翻译成js表达式,并且生成一个函数模板实例。对于相同的语句,我们可以进行缓存,避免每次都翻译。
首先我们实现查询器的模板:
复制代码 代码如下:
var __proto = Object.prototype;
//
// 模板: __tmpl
// 参数: $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方法实现
//
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); //应用到模版
fn = __cache[exp] = __compile(code); //实例化函数
}