综述
javascript 属于弱范例言语,参数的范例毛病只能在运转期发明。当你须要 expose “非常硬朗”的接口给外部,或许在调试较大项目的时刻,你能够会思念强范例言语的范例束缚,或许 assert
一类东西。
正由于 js 没有范例束缚,也没有 assert
如许的“契约型”断言东西,所以一致个人写出的 js 代码,硬朗性经常是不稳固的,偶然束缚多,偶然束缚少,偶然刻返回 null,偶然刻抛非常,而且束缚代码也经常不一致放在函数入口处。
本文尝试编写一种参数搜检东西,期待能减缓相似问题。
参数搜检
假定,我们须要给一切接口一致增加稳固的束缚,以及束缚损坏后一致的反应行动(比方崩溃),除了言语原生支撑(据说 Eiffel 有这个才能,有兴致的能够 google 下),最直接的要领就是想象一个相似 assert
的参数搜检函数 check
,在每一个函数入口处挪用 check
搜检参数,假如搜检失利则实行既定的失利反应。
假如一切的函数都如许编写,就能够保证一切函数严格实行束缚,束缚损坏后马上住手运转,并打印响应的信息。
接口
我们很轻易大抵想象一个 check 接口的样子容貌——
check.setCheckFailedCallback(function (e) {});
function test(a) {
check(a).搜检1(前提1).搜检2(前提2)……
}
有几个细节须要讨论一下:
上面的代码运用了链式挪用,链式挪用的必要性是很显然的——我们须要一种组合搜检步骤的体式格局。为了完成链式挪用,
check
返回的是一个特别的包装对象Checker
。当参数
a
经由过程一切搜检后,代码向下实行。假如有一个搜检没有经由过程,此时须要实行一个反应。由于外层代码能够存在try
块,所以这里抛非常是不可靠的,或许说我们要想一个方法抛出一个“不可 catch”的非常。这里采纳的最简朴的方法,上层设置回调函数checkFailedCallback
,搜检失利后自行处理结果,同时抛出一个非常。check(a)
这类写法,现实上是做不到的。js 里没有宏,所以没有方法接收一个变量同时拿到变量的称号。假如要打印出搜检失利的参数名,须要写成check(a, 'a')
。这类写法有点累坠,能够有更好的计划,我还在思索。
逻辑组合
适才说到链式挪用能够用来组合搜检步骤,然则只要一种组合体式格局显然是不可的。由于搜检步骤之间的关联能够有三种:与、或、非。我们要想方法运用一致的划定规矩把三种关联表达清晰。
详细就不诠释了,分享一下我的划定规矩:
链式挪用完成“与”:
// a 是 number 型,而且大于 1 小于 3
check(a, 'a').is('number').gt(1).lt(3);
参数表完成“或”:
// a 是 number 型,而且位于 [0, 1) || (1, 2] 区间上
check(a, 'a').is('number').within('[0, 1)', '(1, 2]');
注:由于参数表完成“或”,所以这里“或”的优先级永远比“与”高,假如须要“与”比“或”高,则须要一点技能,详细见我这篇文章。
not 属性完成“非”:
// a 是字符串而且不符合正则表达式 /^[\w][\w\d]+$/
check(a, 'a').is('string').not.match(/^[\w][\w\d]+$/);
// a 是字符串而且不符合正则表达式 /^[\w][\w\d]+$/, 而且长度即是 10
check(a, 'a').is('string').not.match(/^[\w][\w\d]+$/).length().eq(10);
注:
not 是一个特别属性,会返回一个特别对象
NotChecker
,这个对象运用try
实行原对象的搜检要领,catch
到非常则以为搜检经由过程。而且NotChecker
的搜检要领返回的是原对象而不是本身,所以not.match
以后衔接length
时,已不再not
的作用局限。由于德摩根定律的存在,not 后的参数表现实上在表达”与”的关联,比方:
check(a, 'a').not.is('string', 'number').
示意的是参数 a 既不为 string 也不为 number。
其他
别的,为了方便运用,还须要完成一些别的的接口,比方:
// a 包括属性 foo,大于 1 小于 3; 同时包括属性 bar, 大于 2 小于 4
check(a, 'a').has('foo').gt(1).lt(3).owner.has('bar').gt(2).lt(4);
注:
上面的代码中,
has
是一个特别要领,它磨练参数中是不是包括指定的属性(own property),假如包括,就返回一个包装该属性的 Checker,不然抛搜检失利的非常。owner
是一个特别属性,它返回包装上一层对象的 Checker 对象。所以我们能够在挪用has
搜检属性以后,挪用owner
“跳回去”继承搜检上层对象。
代码
为了磨练上面的主意,我完成了一个 js 库 param-check,代码位于:
https://github.com/yusangeng/param-check
由于只是一个言语切换是发生的 idea,所以现在这个库还不完美,现实能有多大意义还不好说,对机能和编程范式的影响还须要评价。