摘要:簡單說,我想實(shí)現(xiàn)這么一個(gè)功能假設(shè)有一個(gè)對象,他的方法支持鏈?zhǔn)秸{(diào)用。本來是立即執(zhí)行的代碼,通過的包裝,成了一個(gè)函數(shù),我可以把存儲(chǔ)起來在任意時(shí)刻調(diào)用,相當(dāng)于執(zhí)行檢查。
我相信讀到本文標(biāo)題的人基本上是懵 B 的,我實(shí)在想不出更好的表述方法。簡單說,我想實(shí)現(xiàn)這么一個(gè)功能:
假設(shè)有一個(gè)對象 foobar,他的方法支持鏈?zhǔn)秸{(diào)用。比如這樣:
var foobar = new Foobar(); foobar.segment(1).fault(2);
注意 segment 和 fault 并不一定返回 this,但是要返回一個(gè)同類型對象,否則 foobar.segment(1).fault(2).segment(3).fault(4) 這樣的代碼就可能不合法。這是我特別添加的約束,滿足這一條下面的文章和才有意義。
現(xiàn)在我想實(shí)現(xiàn)一個(gè)包裝函數(shù) makePolicy,實(shí)現(xiàn)這樣的語法(假設(shè) Foobar 所有方法都支持鏈?zhǔn)秸{(diào)用):
var policy = makePolicy(Foobar).segment(1).fault(2); var foobar = new Foobar(); policy(foobar); // 等效于調(diào)用 foobar.segment(1).fault(2)
這里比較難實(shí)現(xiàn)的,就是 makePolicy(Foobar).segment(1).fault(2)這句代碼。如果沒有這個(gè)需求,那直接這樣寫好了:
var policy = function (that) { var newThat = Foobar.prototype.segment.call(that, 1); Foobar.prototype.fault.call(newThat , 2); }; var foobar = new Foobar(); policy(foobar); // 等效于調(diào)用 foobar.segment(1).fault(2)
之所以有這樣的需求,主要是為了完善 js 參數(shù)檢查器(這篇文章)中的邏輯連接的功能。為了方便使用,我想寫出這樣的接口:
// 檢查 param 是否在區(qū)間(1,3) 與 (2,4) 的交集內(nèi) check(param, "param").and(check.policy.gt(1).lt(3), check.policy.gt(2).lt(4)); // 檢查 param 是否在區(qū)間(1,2) 與 (3,4) 的并集內(nèi) check(param, "param").or(check.policy.gt(1).lt(2), check.policy.gt(3).lt(4)); function myCheck(obj) { return obj.length > 4; } // 檢查 param 是否是數(shù)組并且長度大于 4 check(param, "param").and(check.policy.is("array"), myCheck); // 檢查 param 是否*不是*[1,3]之間的偶數(shù)(即2) check(param, "param").not.and( check.policy.is("number").not.lt(1).not.gt(3), function (obj) { return obj % 2 === 0; });
上面的代碼中,check.policy.gt(1).lt(3) 就是我想實(shí)現(xiàn)的語法功能。本來 check(a).gt(1).lt(3) 是立即執(zhí)行的代碼,通過 policy 的包裝,var fn = check.policy.gt(1).lt(3) 成了一個(gè)函數(shù),我可以把 fn 存儲(chǔ)起來在任意時(shí)刻調(diào)用,相當(dāng)于執(zhí)行 gt(1).lt(3) 檢查。
需求講清楚了,剩下的就是開腦洞了。對照下面的代碼梳理一下思路:
var policy = makePolicy(Foobar).segment(1).fault(2); var foobar = new Foobar(); policy(foobar); // 等效于調(diào)用 foobar.segment(1).fault(2)
首先,我實(shí)驗(yàn)了一下,policy 的語法設(shè)想實(shí)際上很難實(shí)現(xiàn),因?yàn)?js 中沒有方便的語法表達(dá)“函數(shù)類”、“函數(shù)實(shí)例”這樣的概念,所以 policy 不適合設(shè)計(jì)為一個(gè)函數(shù),妥協(xié)一下,把 policy 設(shè)計(jì)為一個(gè) 包含 exec 方法的對象,調(diào)用 policy.exec(...) 即可執(zhí)行相應(yīng)功能。
第二,將 policy 設(shè)計(jì)為一個(gè) Policy 類的實(shí)例,因?yàn)?policy 可能會(huì)有很多方法,這些方法是在 makePolicy 函數(shù)中從輸入類原型上按照名字一個(gè)一個(gè)扒下來的,比較適合放在 prototype 中。以及,根據(jù)輸入類的不同,Policy 應(yīng)該在 makePolicy 中動(dòng)態(tài)生成,然后立即 new 一個(gè)實(shí)例返回,這樣我們可以隨時(shí)生成任意類的 policy 包裝。
綜合以上思考,我們要實(shí)現(xiàn)的接口改為這樣:
var policy = makePolicy(Foobar); var functor = policy.segment(1).fault(2); // fn 存儲(chǔ)了鏈?zhǔn)秸{(diào)用的路徑和參數(shù) var foobar = new Foobar(); functor.exec(foobar); // 等效于調(diào)用 foobar.segment(1).fault(2)
下面是簡化的實(shí)現(xiàn)代碼:
/** * 生成 policy * @param proto 要生成 policy 的類原型 * @return 生成的 policy 實(shí)例 */ function makePolicy(proto) { function Policy(fn, prev) { this.fn_ = fn; this.prev_ = prev; } Policy.prototype.exec = function (that) { var myThat = that; var prev = this.prev_; var fn = this.fn_; if (prev) { myThat = prev.exec(that); } return fn(myThat); }; for (var key in proto) { if (proto.hasOwnProperty(key)) { Policy.prototype[key] = (function (fnName) { return function () { var self = this; var args = Array.prototype.slice.call(arguments, 0); return new Policy(function (that) { return proto[fnName].apply(that, args); }, self); } })(key); } } return new Policy(); }
由上面的代碼可知,當(dāng)我們在鏈?zhǔn)秸{(diào)用函數(shù)時(shí),順序是從左到右。而 policy 在運(yùn)行時(shí),是先調(diào)用最右邊的 policy 然后通過 prev_ 指針一路回溯到最左邊,然后再從左到右執(zhí)行下來。
有了上面的實(shí)現(xiàn),js 參數(shù)檢查器(這篇文章)的功能差不多就完整了,有興趣的同學(xué)可以在這里看到具體實(shí)現(xiàn)。不過目前的實(shí)現(xiàn)仍然有許多限制,在目前的基礎(chǔ)上,我們其實(shí)可以實(shí)現(xiàn)更加通用,無所謂是不是鏈?zhǔn)秸{(diào)用的 policy 語法,等我做出來在寫文章匯報(bào)。
最后請教一個(gè)問題:policy 這個(gè)名字是我瞎起的,這樣的功能應(yīng)該叫什么名字呢?roadmap?blueprint?想不出來。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87805.html
摘要:函數(shù)會(huì)在之后的某個(gè)時(shí)刻觸發(fā)事件定時(shí)器。事件循環(huán)中的這樣一次遍歷被稱為一個(gè)。執(zhí)行完畢并出棧。當(dāng)定時(shí)器過期,宿主環(huán)境會(huì)把回調(diào)函數(shù)添加至事件循環(huán)隊(duì)列中,然后,在未來的某個(gè)取出并執(zhí)行該事件。 原文請查閱這里,略有改動(dòng)。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第四章。 現(xiàn)在,我們將會(huì)通過回顧單線程環(huán)境下編程的弊端及如何克服這些困難以創(chuàng)建令人驚嘆...
摘要:例如通過哈希表映射需要一個(gè)操作來檢查值是否相等,另一個(gè)操作用于創(chuàng)建哈希碼。如果使用哈希碼,則對象應(yīng)該是不可變的。模式匹配提案目前處于第階段。在本文,我們研究其中的智能管道另一個(gè)提議被稱為。更強(qiáng)大,更重量級(jí),并附帶自己的數(shù)據(jù)結(jié)構(gòu)。 翻譯:瘋狂的技術(shù)宅原文:http://2ality.com/2019/01/fut... 本文首發(fā)微信公眾號(hào):jingchengyideng歡迎關(guān)注,每天...
摘要:回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對該事件或條件進(jìn)行響應(yīng)。若是使用回調(diào)函數(shù)進(jìn)行處理,代碼就可以繼續(xù)進(jìn)行其他任務(wù),而無需空等。參考理解回調(diào)函數(shù)理解與使用中的回調(diào)函數(shù)這篇相當(dāng)不錯(cuò)回調(diào)函數(shù) 為什么寫回調(diào)函數(shù) 對于javascript中回調(diào)函數(shù) 一直處于理解,但是應(yīng)用不好的階段,總是在別人家的代碼中看到很巧妙的回調(diào),那時(shí)候會(huì)有wow c...
閱讀 3108·2021-11-19 09:40
閱讀 1569·2021-11-15 11:39
閱讀 686·2021-10-08 10:05
閱讀 2281·2021-09-03 10:29
閱讀 3414·2021-08-12 13:22
閱讀 2180·2019-08-30 15:54
閱讀 3721·2019-08-30 14:03
閱讀 2663·2019-08-30 13:45