请不要写令人困惑的条件
#javascript #初学者 #编程 #tips

您是否曾经花了几秒钟以上盯着同一条代码?您多久发现有条件棘手的解析?在本文中,我们讨论了混乱的条件在我们的代码中如何表现出来。一路上,我们还探索了不同的重构技术,以提高条件控制流的可读性。

双重负面

当我们否定名称已经以非肯定风格的变量时,就会出现双重负面。考虑以下示例。

// Non-affirmative Naming Convention
const isNotReady = doSomething();
const isForbidden = doSomething();
const cannotJoinRoom = doSomething();
const hasNoPermission = doSomething();

// Negating once results in an awkward double negative situation.
console.log(!isNotReady);
console.log(!isForbidden);
console.log(!cannotJoinRoom);
console.log(!hasNoPermission);

这与 肯定样式 相反,这需要减少认知负载才能解析,因为一个人不需要跳过否定变量名称即可提取的圈其逻辑的本质。换句话说,可以读取肯定变量背后的意图,不需要读圈!考虑对上一个示例的以下修改。

// Affirmative Naming Convention
const isReady = doSomething();
const isAllowed = doSomething();
const canJoinRoom = doSomething();
const hasPermission = doSomething();

// Negation actually makes sense here!
console.log(!isReady);
console.log(!isAllowed);
console.log(!canJoinRoom);
console.log(!hasPermission);

不幸的是,由于标准和向后兼容,有时不可避免的命名约定是不可避免的。以HTML DOM APIskoude0属性为例。 <input>标签中disabled属性的存在告诉浏览器(在视觉和字面上)禁用元素的形式控制。否则,它的缺席会导致<input>表现出其默认行为,即接受用户输入。这是html的人体工程学的不幸副作用。

<!-- Normal Checkbox (Default) -->
<input type="checkbox" />

<!-- Disabled Checkbox -->
<input type="checkbox" disabled />

尽管如此,我们仍然应该尽可能地争取肯定的命名惯例。它们更容易阅读和解析,仅仅是因为无需始终在精神上否定变量名称。此规则适用于两个变量名称和函数名称。

非肯定控制流

下一种双重负面形式更加微妙。

// Suppose that we _do_ follow affirmative naming conventions.
const isAllowed = checkSomething();
if (!isAllowed) {
    // There is something funny about this...
    doError();
} else {
    // Notice how the `else` block is practically a double negation?
    doSuccess();
}

如上所述,非肯定的样式也可以弥补条件控制流。回想一下,else块实际上是对相应的if条件的否定。因此,我们必须在这里扩展肯定风格。修复实际上很简单。

// Just invert the logic!
const isAllowed = checkSomething();
if (isAllowed) {
    doSuccess();
} else {
    doError();
}

同一规则适用于平等和不平等检查。

// ❌ Don't do this!
if (value !== 0) {
    doError();
} else {
    doSuccess();
}

// ✅ Prefer this instead.
if (value === 0) {
    doSuccess();
} else {
    doError();
}

有些人甚至可能会让有条件的块是空白的,只是为了否定肯定风格的条件。尽管我并不是在提倡每个人都采取这么多的态度,但我可以理解为什么对于某些人来说这可能更可读。以koude6操作员为例,例如,如果没有括号,它就无法轻易否定。

if (obj instanceof Animal) {
    // Intentionally left blank.
} else {
    // Do actual work here (in the negation).
    doSomething();
}

早期回报的例外

快速旁边,有条件控制流的特殊例外。在这种情况下,否定是必要的。

if (!isAllowed) {
    // Return early here.
    doError();
    return;
}

// Otherwise, proceed with the success branch.
doSuccess();

,如果可能的情况下,我们仍然应该尝试将逻辑产生较小的嵌套,缩小水平和更可读的肯定风格。

// Prefer affirmative early returns.
if (isAllowed) {
    doSuccess();
    return;
}

// If we did not invert the logic, this would have been
// nested inside the `!isAllowed` conditional block.
if (!hasPermission) {
    doPermissionError();
    return;
}

// When all else fails, do something else.
doSomethingElse();
return;

以肯定样式表达相同控制流的另一种方法(无早期返回)如下。

// Hooray for the affirmative style!
if (isAllowed) {
    doSuccess();
} else if (hasPermission) {
    doSomethingElse();
} else {
    doPermissionError();
}
return;

当然,还有许多其他方法可以交换,倒转和重构代码,每个代码都是完全主观的。因此,保留肯定的惯例将成为某种艺术形式。无论如何,只要我们坚持肯定风格的一般准则,代码的可读性将始终提高。

复合条件

逻辑运营商(例如koude7koude8),这个故事变得更加复杂。例如,我们如何以更肯定的风格重构下面的代码?

// This is fine... but there has to be a better way,
// right? There are just too many negations here!
if (!isUser || !isGuest) {
    doSomething();
} else {
    doAnotherThing();
}

对于复合条件,我们介绍了布尔代数最被低估的法律:De Morgan's Laws

// Suppose that these are **any** two Boolean variables.
let a: boolean;
let b: boolean;

// The following assertions will **always** hold for any
// possible pairing of values for `a` and `b`.
!(a && b) === !a || !b;
!(a || b) === !a && !b;

感谢De Morgan的法律,我们现在有一种方法可以“分发”条件中的否定,然后“翻转”其操作员(从&&&&||,反之亦然)。

尽管以下示例仅具有二进制比较(即两个要素),但只要我们尊重operator precedence,de Morgan的定律就可以在任何数量的条件变量上推广。即,&&运算符始终在||操作员之前进行评估。

// By De Morgan's Laws, we can "factor out" the negation as follows.
if (!(isUser && isGuest)) {
    doSomething();
} else {
    doAnotherThing();
}
// Then we simply invert the logic as we did in the previous section.
if (isUser && isGuest) {
    doAnotherThing();
} else {
    doSomething();
}

现在,这不是更可读性吗?使用德·摩根(De Morgan)的法律,我们可以清理“否定太多”的条件。

结论

此时总体主题应该显而易见。在可能的情况下,我们应该避免编写代码,以迫使读者跳过(不必要的)需要额外的认知开销的篮球。在本文中,我们讨论了以下技术:

  1. 鼓励肯定的命名约定。
    • 避免使用否定术语/诸如nonotdis-mal-等的前缀
    • 更喜欢正等价。
  2. 将条件控制流(如有可能)倒转,以适应肯定风格。
    • 在交换,倒转和重构分支时随意玩。
    • 早期返回 May 需要否定。
  3. 使用布尔代数中的一些技巧来倒置通风管。
    • de Morgan的定律是重构的特别有力工具!

现在要出发,以更清洁的条件祝福世界!