JavaScript JSON 模式匹配实现:过滤器与运算符详解
本文深入解析 JavaScript 中 JSON 模式匹配的实现机制,重点介绍如何构建过滤器(Filter)与运算符(Operators)。通过定义比较、相等及逻辑运算符,并引入默认运算符以简化 JSON 匹配语法,最终实现从模式到过滤函数的转换,为构建健壮的 Dispatcher 组件奠定基础。
过滤器与捕获机制实现
实现 notify 和 capture 方法相对简单,核心思路是将 capture 接收到的 handler 进行存储,随后在 notify 执行时检索并调用匹配的 handler。
具体实现代码如下:
var filterHandlerBundles = [];
Dispatch.capture = function(pattern, handler) {
var filter = createFilter(pattern);
filterHandlerBundles.push({
"filter": filter,
"handler": handler
});
};
Dispatcher.notify = function(json) {
for (var i = 0; i < filterHandlerBundles.length; i++) {
if (filterHandlerBundles[i].filter.apply(this, arguments)) {
filterHandlerBundles[i].handler(json);
}
}
}; 上述代码逻辑直观,核心难点在于 createFilter 函数的实现。该函数负责将描述模式的 JSON 对象转换为一个用于判断 JSON 是否匹配的函数。
Operators
运算符设计与默认处理
我们设计了多种运算规则,如何实现它们?为了避免使用冗长的 switch-case 结构,这里采用关联数组来映射运算符名称与其对应的实现函数。
代码如下:
var operators = {};
operators["lt"] = function(testValue, value) {
return arguments.length == 2 && value < testValue;
};
operators["lte"] = function(testValue, value) {
return arguments.length == 2 && value <= testValue;
};
operators["gt"] = function(testValue, value) {
return arguments.length == 2 && value > testValue;
};
operators["gte"] = function(testValue, value) {
return arguments.length == 2 && value >= testValue;
}; 通过提取 "$" 后的运算符名称,即可直接定位到对应的判断函数。以上四个为比较运算符,因实现较为简单,故在此作为示例展示。
较为复杂的运算符是 eq,它需根据数据类型选择具体的判断逻辑。对于 String、Number 和 Boolean 类型,eq 等同于 ==;对于 Array 类型,要求内部每个元素均满足 eq 且顺序一致;对于 Object 类型,则需每个子条件均符合,因此需要提取每个子条件的运算符字符串并调用相应运算符。具体实现可参考完整代码。
其他运算符相对简单,此处仅提供思路,开发者可根据实际需求选取或扩展运算符子集:
in - 遍历数组,检查是否存在至少一个满足 eq 的元素。
all - 遍历数组,检查是否所有元素均满足 eq。
ex - 若传入值存在,则检查子元素是否存在。
re - 使用正则表达式判断字符串是否匹配。
ld - 直接调用函数进行判断。
代码编写完成后,如何确保其正确性?这将在下一篇文章中讨论。在此之前,我们先添加一个默认运算符。
代码如下:
operators[""] = function(testValue, value) {
if (testValue instanceof Array) {
return operators["in"].apply(this, arguments);
} else if (testValue instanceof RegExp) {
return operators["re"].apply(this, arguments);
} else if (testValue instanceof Function) {
return operators["ld"].apply(this, arguments);
} else {
return operators["eq"].apply(this, arguments);
}
}; 引入默认运算符主要作为一种快捷方式。在多数场景中,我们主要使用 eq 运算。若每次均需显式指定运算符,代码将变得复杂且不够美观。对比以下两个 JSON 对象,你认为哪一个更直观?
代码如下:
Dispatcher.capture({
"status": 200,
"command": "message"
}, function(json) { /* display message */ });
Dispatcher.capture({
"status$eq": 200,
"command$eq": "message"
}, function(json) { /* display message */ }); 显然,第一个示例更为直观。因此,我们需要默认运算符,当运算符字符串为空时,默认运算符会根据 testValue 的类型自动选择相应的运算符。
Pattern to Filter
模式到过滤器的转换
最后,需将 operators 与 createFilter 连接起来。这部分工作并不复杂,只需调用默认运算符即可。
代码如下:
var createFilter = function(condition) {
return function(json) {
if (arguments.length > 0) {
return operators[""](condition, json);
} else {
return operators[""](condition);
}
};
}; 为何需考虑 json 参数未传入的情况?这将在下篇文章中解释。若不这样做,虽不影响基本功能,但可能引发一些细微问题。
编写运算符时,严谨性至关重要。由于 Dispatcher 是一个封装好的组件,运算符中任何微小的不严谨都可能埋下难以排查的缺陷。因此,下一篇文章将讨论单元测试,通过测试提高 Dispatcher 的健壮性。
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
