原文:Negating Predicate Functions in JavaScript
翻译:涂鸦码龙
如果你从没听过这个概念,那么至少用过谓语函数。谓语本质上是一个函数,根据它的参数,可以判断返回结果是 true
或者 false
。一般都以“isX”命名,比如 isEven
或者 isNumber
。
假如我们有个程序,需要处理漫画书里的英雄和坏蛋,以下面的简单对象为例:
1 2 3 4 5
| var superman = { name: "Superman", strength: "Super", heroism: true };
|
程序中难免会用到一些谓语,像这些:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function isSuperStrong (character) { return character.strength === "Super"; } function isNotSuperStrong (character) { return character.strength !== "Super"; } function isHeroic (character) { return character.heroism === true; } function isNotHeroic (character) { return character.heroism !== true; } console.log(isNotSuperStrong(superman)); console.log(isNotHeroic(superman));
|
如你所见,代码有些冗余。问题不是代码太长,而是核心逻辑定义了两次(每一对谓语,“is”和“isNot”)。逻辑重复意味着逻辑改变时,可能只更新了其中一处谓语,导致错误发生。
如何解决呢?我们首先想到的是这样改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function isSuperStrong (character) { return character.strength === "Super"; } function isNotSuperStrong (character) { return !isSuperStrong(character); } function isHeroic (character) { return character.heroism === true; } function isNotHeroic (character) { return !isHeroic(character); } console.log(isNotSuperStrong(superman)); console.log(isNotHeroic(superman));
|
的确有进步,但是仍有冗余。“isNot”谓语只不过颠倒了“is”谓语的功能。
我们何不抽象成一个更清晰,更易维护的函数呢?
1 2 3 4 5
| function negate (predicateFunc) { return function () { return !predicateFunc.apply(this, arguments); }; }
|
negate
函数接收一个谓语函数作为参数,返回一个函数用来对之前谓语的功能取反。
(如果对 apply
不了解,可以读读这篇《Invoking JavaScript Functions With ‘call’ and ‘apply’》)
使用 negate
函数解决我们先前的问题吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function isSuperStrong (character) { return character.strength === "Super"; } var isNotSuperStrong = negate(isSuperStrong); function isHeroic (character) { return character.heroism === true; } var isNotHeroic = negate(isHeroic); console.log(isNotSuperStrong(superman)); console.log(isNotHeroic(superman));
|
程序如期运行,但是我们把核心逻辑放到了一处,从“is”谓语也很容易推导出“isNot”谓语。
这点小小的重构看起来微不足道,但是应用于复杂系统的许多分散函数时,negate
函数可以使程序更易维护。
Underscore 和 Lo-Dash 都已经填加了 negate
函数。