摘要:本文嘗試編寫一種參數(shù)檢查工具,期待能緩解類似問題。為了實現(xiàn)鏈式調用,返回的是一個特殊的包裝對象。如果要打印出檢查失敗的參數(shù)名,需要寫成。由于德摩根定律的存在,后的參數(shù)表實際上在表達與的關系,比如表示的是參數(shù)既不為也不為。
綜述
javascript 屬于弱類型語言,參數(shù)的類型錯誤只能在運行期發(fā)現(xiàn)。當你需要 expose “非常健壯”的接口給外部,或者在調試較大項目的時候,你可能會懷念強類型語言的類型約束,或者 assert 一類東西。
正因為 js 沒有類型約束,也沒有 assert 這樣的“契約型”斷言工具,所以同一個人寫出的 js 代碼,健壯性常常是不穩(wěn)定的,有時約束多,有時約束少,有時候返回 null,有時候拋異常,并且約束代碼也常常不統(tǒng)一放在函數(shù)入口處。
本文嘗試編寫一種參數(shù)檢查工具,期待能緩解類似問題。
參數(shù)檢查假設,我們需要給所有接口統(tǒng)一添加穩(wěn)定的約束,以及約束破壞后統(tǒng)一的反饋行為(比如崩潰),除了語言原生支持(聽說 Eiffel 有這個能力,有興趣的可以 google 下),最直接的方法就是設計一個類似 assert 的參數(shù)檢查函數(shù) check,在每個函數(shù)入口處調用 check 檢查參數(shù),如果檢查失敗則執(zhí)行既定的失敗反饋。
如果所有的函數(shù)都這樣編寫,就可以保證所有函數(shù)嚴格執(zhí)行約束,約束破壞后立刻停止運行,并打印相應的信息。
接口我們很容易大致設想一個 check 接口的模樣——
check.setCheckFailedCallback(function (e) {}); function test(a) { check(a).檢查1(條件1).檢查2(條件2)…… }
有幾個細節(jié)需要討論一下:
上面的代碼使用了鏈式調用,鏈式調用的必要性是很顯然的——我們需要一種組合檢查步驟的方式。為了實現(xiàn)鏈式調用,check 返回的是一個特殊的包裝對象 Checker。
當參數(shù) a 通過所有檢查后,代碼向下執(zhí)行。如果有一個檢查沒有通過,此時需要執(zhí)行一個反饋。由于外層代碼可能存在 try 塊,所以這里拋異常是不可靠的,或者說我們要想一個辦法拋出一個“不可 catch”的異常。這里采用的最簡單的辦法,上層設置回調函數(shù) checkFailedCallback,檢查失敗后自行處理結果,同時拋出一個異常。
check(a) 這種寫法,實際上是做不到的。js 里沒有宏,所以沒有辦法接受一個變量同時拿到變量的名稱。如果要打印出檢查失敗的參數(shù)名,需要寫成 check(a, "a")。這種寫法有點累贅,可能有更好的方案,我還在思考。
邏輯組合剛才說到鏈式調用可以用來組合檢查步驟,但是只有一種組合方式顯然是不行的。因為檢查步驟之間的關系可能有三種:與、或、非。我們要想辦法使用同一的規(guī)則把三種關系表達清楚。
具體就不解釋了,分享一下我的規(guī)則:
鏈式調用實現(xiàn)“與”:
// a 是 number 型,并且大于 1 小于 3 check(a, "a").is("number").gt(1).lt(3);
參數(shù)表實現(xiàn)“或”:
// a 是 number 型,并且位于 [0, 1) || (1, 2] 區(qū)間上 check(a, "a").is("number").within("[0, 1)", "(1, 2]");
注:由于參數(shù)表實現(xiàn)“或”,所以這里“或”的優(yōu)先級永遠比“與”高,如果需要“與”比“或”高,則需要一點技巧,具體見我這篇文章。
not 屬性實現(xiàn)“非”:
// a 是字符串并且不符合正則表達式 /^[w][wd]+$/ check(a, "a").is("string").not.match(/^[w][wd]+$/); // a 是字符串并且不符合正則表達式 /^[w][wd]+$/, 并且長度等于 10 check(a, "a").is("string").not.match(/^[w][wd]+$/).length().eq(10);
注:
not 是一個特殊屬性,會返回一個特殊對象 NotChecker,這個對象使用 try 執(zhí)行原對象的檢查方法,catch 到異常則認為檢查通過。并且 NotChecker 的檢查方法返回的是原對象而不是自己,所以 not.match 之后連接 length 時,已經(jīng)不再 not 的作用范圍。
由于德摩根定律的存在,not 后的參數(shù)表實際上在表達"與"的關系,比如:
check(a, "a").not.is("string", "number").
表示的是參數(shù) a 既不為 string 也不為 number。
其他另外,為了方便使用,還需要實現(xiàn)一些另外的接口,比如:
// a 包含屬性 foo,大于 1 小于 3; 同時包含屬性 bar, 大于 2 小于 4 check(a, "a").has("foo").gt(1).lt(3).owner.has("bar").gt(2).lt(4);
注:
上面的代碼中,has 是一個特殊方法,它檢驗參數(shù)中是否包含指定的屬性(own property),如果包含,就返回一個包裝該屬性的 Checker,否則拋檢查失敗的異常。
owner 是一個特殊屬性,它返回包裝上一層對象的 Checker 對象。所以我們可以在調用 has 檢查屬性之后,調用 owner“跳回去”繼續(xù)檢查上層對象。
代碼為了檢驗上面的想法,我實現(xiàn)了一個 js 庫 param-check,代碼位于:
https://github.com/yusangeng/param-check
因為只是一個語言切換是產(chǎn)生的 idea,所以目前這個庫還不完善,實際能有多大意義還不好說,對性能和編程范式的影響還需要評估。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/87798.html
摘要:上一個版本的問題接這篇文章,聊聊參數(shù)檢查工具的完善。最終實現(xiàn)了這樣的效果檢查是否在區(qū)間與的交集內檢查是否在區(qū)間與的并集內檢查是否是數(shù)組并且長度大于檢查是否不是之間的偶數(shù)即 上一個版本的問題 接這篇文章,聊聊參數(shù)檢查工具 param-check 的完善。 按照之前的接口設計,鏈式調用表示與,參數(shù)表表示或,自然產(chǎn)生了一個問題——如果我要表達(A與B)或(C與D)這樣的邏輯組合應該怎么辦? ...
摘要:介紹這是一篇短文,旨在展示多種在中安全地訪問深層嵌套值的方式。所以每次我們想要訪問深度嵌套的數(shù)據(jù)時,都必須明確地進行手動檢查。我們還觸及了,可以更新深度嵌套數(shù)據(jù)而不會改變對象。 介紹 這是一篇短文,旨在展示多種在javascript中安全地訪問深層嵌套值的方式。下面的例子通過不同的方式來解決這一問題。 開始之前,讓我們看下實際遇到這種狀況時.. 假設有一個props對象(如下),當我們...
摘要:檢驗是用來檢驗序列是否平穩(wěn)的方式一般來說是時間序列中的一種檢驗方法中可使用現(xiàn)成的工具來實現(xiàn)檢驗最參數(shù)和返回結果的理解還不夠深刻后頭再把參數(shù)和返回結果都加上參數(shù)項序列,一維數(shù)組差分次數(shù)只有常量,有常量項和趨勢項,有常量項線性和二次趨勢 adf檢驗是用來檢驗序列是否平穩(wěn)的方式一般來說是時間序列中的一種檢驗方法python中可使用現(xiàn)成的工具statsmodels來實現(xiàn)adf檢驗 import...
閱讀 4033·2021-11-22 13:53
閱讀 1735·2021-09-23 11:52
閱讀 2454·2021-09-06 15:02
閱讀 975·2019-08-30 15:54
閱讀 915·2019-08-30 14:15
閱讀 2398·2019-08-29 18:39
閱讀 670·2019-08-29 16:07
閱讀 433·2019-08-29 13:13