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