用JavaScript對(duì)JSON進(jìn)行模式匹配 (Part 2 - 實(shí)現(xiàn))
要實(shí)現(xiàn) notify 和 capture 就太容易了,我們只需要把 capture 傳入的 handler 都保存下來(lái),然后在 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);
}
}
};
這段代碼的邏輯很清晰,關(guān)鍵就在于 createFilter 的部分。這個(gè)函數(shù)負(fù)責(zé)把一個(gè)描述模式的 JSON 轉(zhuǎn)換為一個(gè)判斷 JSON 是否匹配的函數(shù)。
Operators
我們?cè)O(shè)計(jì)了不少的運(yùn)算法,如何實(shí)現(xiàn)他們呢?記住,我們不要 switch case 。因此,我們使用一個(gè)關(guān)聯(lián)數(shù)組來(lái)保存運(yùn)算符與實(shí)現(xiàn)之間的映射關(guān)系好了 。
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;
};
這樣我們只要把 "$" 后面的運(yùn)算符抽取出來(lái),就可以立即找到對(duì)應(yīng)的判斷函數(shù)了。上面4個(gè)是比較運(yùn)算符,由于實(shí)現(xiàn)比較容易,所以放在這里做例子。
一個(gè)比較難的函數(shù)是 eq ,因?yàn)樗枰鶕?jù)數(shù)據(jù)類型來(lái)選擇具體的判斷方式。對(duì)于 String 、 Number 、 Boolean , eq 的含義就是 == ;對(duì)于 Array , eq 的含義就是里面的每一個(gè)元素都 eq ,而且順序一致;對(duì)于 Object , eq 的含義是每一個(gè)子條件都符合,因此我們需要將每一個(gè)子條件的運(yùn)算符字符串提取出來(lái),然后調(diào)用對(duì)應(yīng)的運(yùn)算符。具體可以參考完整代碼。
其他運(yùn)算符會(huì)簡(jiǎn)單一些,在此我僅僅給出提示,大家可以根據(jù)自己的實(shí)際需求這些運(yùn)算符的子集或超集:
in - 遍歷數(shù)組,看能否找到至少一個(gè) eq 的。
all - 遍歷數(shù)組,看是否每一個(gè)都存在 eq 的。
ex - 如果有傳入值,則子元素存在。
re - 用正則表達(dá)式判斷字符串是否匹配。
ld - 直接調(diào)用函數(shù)進(jìn)行判斷。
寫好了嗎?不太確信自己寫得是否正確?這是我們下一篇文章要討論的內(nèi)容,讓我們先加上一個(gè)默認(rèn)運(yùn)算符。
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);
}
};
為什么需要一個(gè)默認(rèn)運(yùn)算符?這其實(shí)只是一個(gè)快捷方式。在大多數(shù)時(shí)候,我們需要的都是 eq 運(yùn)算,如果每一處都要把運(yùn)算符寫上,代碼將變得很復(fù)雜,也不美觀。對(duì)比一下兩個(gè) JSON ,你覺得哪個(gè)更自然?
Dispatcher.capture({
"status": 200,
"command": "message"
}, function(json) { /* display message */ });
Dispatcher.capture({
"status$eq": 200,
"command$eq": "message"
}, function(json) { /* display message */ });
顯然,第一個(gè)更直觀一些。因此,我們需要一個(gè)默認(rèn)運(yùn)算符,當(dāng)運(yùn)算符字符串就是 "" 時(shí),就通過(guò)默認(rèn)運(yùn)算符選擇一個(gè)運(yùn)算符。
Pattern to Filter
最后,我們需要把 operators 和 createFilter 接上。這部分工作其實(shí)也不難,只要調(diào)用默認(rèn)運(yùn)算符就可以了。
var createFilter = function(condition) {
return function(json) {
if (arguments.length > 0) {
return operators[""](condition, json);
} else {
return operators[""](condition);
}
};
};
為什么需要考慮 json 參數(shù)沒有傳入的情況?下次文章再告訴你。不這樣做也可以,只是有些很細(xì)小的問(wèn)題而已。
寫運(yùn)算符,最需要的是嚴(yán)謹(jǐn)性。因?yàn)?Dispatcher 是一個(gè)封裝好的組件,運(yùn)算符一點(diǎn)點(diǎn)的不嚴(yán)謹(jǐn),都會(huì)把缺陷埋藏得很深,很難找出來(lái)。因此,下一篇文章我們要討論的是單元測(cè)試,通過(guò)單元測(cè)試我們可以大大提高 Dispatcher 的健壯性。
相關(guān)文章
JavaScript延遲加載之a(chǎn)sync與defer的應(yīng)用
這篇文章主要介紹了JavaScript延遲加載之a(chǎn)sync與defer的應(yīng)用場(chǎng)景與使用區(qū)別的介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09JS模擬實(shí)現(xiàn)Excel條件格式中的色階效果
這篇文章主要為大家詳細(xì)介紹了如何利用JavaScript模擬實(shí)現(xiàn)Excel條件格式中的色階效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2023-05-05指定區(qū)域的圖片自動(dòng)按比例縮小的js代碼(防止頁(yè)面被圖片撐破)
有時(shí)候我們更新的內(nèi)容,有很多的大圖片,就會(huì)導(dǎo)致頁(yè)面變形或看不到全圖。一般情況我們用css的max-width控制,但有些瀏覽器不支持,我們也可以用js做個(gè)補(bǔ)充2014-02-02HTML5附件拖拽上傳drop & google.gears實(shí)現(xiàn)代碼
從gmail 的附件拖拽上傳,到網(wǎng)易郵箱的拖拽上傳,我們看到了html 5 為我們帶來(lái)了新的web體驗(yàn)。2011-04-04公眾號(hào)SVG動(dòng)畫交互實(shí)戰(zhàn)代碼
這篇文章主要介紹了公眾號(hào)SVG動(dòng)畫交互實(shí)戰(zhàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05數(shù)據(jù)分析軟件之FineReport教程:[5]參數(shù)界面JS(全)
表格軟件FineReport在設(shè)計(jì)報(bào)表時(shí)經(jīng)常會(huì)用到,這篇文章主要介紹數(shù)據(jù)分析軟件之FineReport教程:[5]參數(shù)界面JS,需要的朋友可以參考下2015-08-08js動(dòng)態(tài)獲取子復(fù)選項(xiàng)并設(shè)計(jì)全選及提交的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇js動(dòng)態(tài)獲取子復(fù)選項(xiàng)并設(shè)計(jì)全選及提交的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的, 現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06Jupyter Notebook運(yùn)行JavaScript的方法
Jupyter Notebook是一塊所見即所得的畫布,通過(guò)在瀏覽器上編輯代碼,讓開發(fā)人員實(shí)現(xiàn)展示與快速迭代的利器,本文主要介紹了Jupyter Notebook運(yùn)行JavaScript的方法,感興趣的可以了解一下2021-05-05一個(gè)javascript參數(shù)的小問(wèn)題
2008-03-03