我应该啥时候使用?? (无效合并)vs || (逻辑或)?

Posted

技术标签:

【中文标题】我应该啥时候使用?? (无效合并)vs || (逻辑或)?【英文标题】:When should I use ?? (nullish coalescing) vs || (logical OR)?我应该什么时候使用?? (无效合并)vs || (逻辑或)? 【发布时间】:2020-08-12 07:31:12 【问题描述】:

与Is there a "null coalescing" operator in javascript? 相关 - JavaScript 现在有一个?? 运算符,我看到它的使用频率更高。以前大多数 JavaScript 代码使用||

let userAge = null

// These values will be the same. 
let age1 = userAge || 21
let age2 = userAge ?? 21

在什么情况下??|| 的行为会有所不同?

【问题讨论】:

Overview and motivation @ASDFGerte 这将是一个很好的答案。 可能值得一提的是,在 Angular (2+) 模板中,您不能使用 ??(会导致一些奇怪的错误),但 || 可以正常工作 【参考方案1】:

如MDN中所述:

与逻辑或 (||) 运算符相反,如果左操作数不是nullundefined,则返回左操作数。换句话说,如果您使用|| 为另一个变量foo 提供一些默认值,如果您认为某些虚假值可用(例如''0),您可能会遇到意外行为。有关更多示例,请参见下文。

还有the answer to the question you linked:

无论第一个操作数的类型如何,如果将其转换为布尔值导致 false,则赋值将使用第二个操作数。请注意以下所有情况:

alert(Boolean(null)); // false
alert(Boolean(undefined)); // false
alert(Boolean(0)); // false
alert(Boolean("")); // false
alert(Boolean("false")); // true -- gotcha! :)

【讨论】:

我确实在那个问题的答案中看到了该描述,但这似乎是对众所周知的 JS 布尔逻辑的重复,而不是一个具体的例子。【参考方案2】:

|| 被用作布尔条件时,该条件变为假。例如:

let userAge = false

// These values will be the same. 
let age1 = userAge || 21 // 21
let age2 = userAge ?? 21 // false

逻辑 OR 仍然会为不计算为 true 的任何内容提供下一个值。所以在这种情况下它给出了值21。其中?? 只处理nullundefined

【讨论】:

【参考方案3】:

function f(input) 
  const val = input || 1;

  return 41 + val;


function g(input) 
  const val = input ?? 1;

  return 41 + val;


console.log("using ||:", f(0));
console.log("using ??:", g(0));

null(ish) 合并运算符适用于nullundefined。因此,当您不想要这些值但您会接受其他虚假值时,请使用 null(ish) 合并运算符

console.log(null      ?? "nullish");
console.log(undefined ?? "nullish");
console.log(""        ?? "nullish");
console.log(0         ?? "nullish");
console.log(false     ?? "nullish");

逻辑 OR 将跳过任何虚假值并为您提供其他内容。 逻辑 OR 将跳过任何虚假值并为您提供另一个参数。 This does work 现在是惯用的,但它并不总是你想要的:

console.log(null      || "falsy");
console.log(undefined || "falsy");
console.log(""        || "falsy");
console.log(0         || "falsy");
console.log(false     || "falsy");

这里有一些规则可以帮助您确定您需要哪一个。最简单的测试:

您是否只想防止null(和undefined - 通常是同一件事)?然后使用??。如果您不确定,默认使用 nullish 合并运算符可能是个好主意。 你知道你也不想0""吗?然后使用||

第二个是它实际上变得棘手的地方。你怎么知道你需要丢弃虚假值?好吧,第一个 sn-p 显示了如果你这样做会发生什么:f(0) 将丢弃零,从而产生不同的结果。这是一个比较常见的错误来源。由于构造 a = b || c 通常会引入回退值(在本例中为 c),因此它可能会在您无意中意外地回退到它。

function updateAge(years) 
  var yearsToAdd = years || 1;
  return this.age + yearsToAdd

如果您想为调用 updateAge()(无参数)提供回退,则此方法有效,但如果您调用 updateAge(0)(无更新)则失败。同样,您可以:

function sayMyName(greeting) 
  var prefix = greeting || "Hello, my name is ";
  return prefix + this.firstName;

同样,这适用于 sayMyName()(无参数),但不适用于 sayMyName("")(明确无问候)。

概括地说,如果您提供的后备值与虚假值不同,那么您可能会遇到问题。但是,如果您的后备虚假值 - num || 0str || "",那么这并不重要。

很少(或应该)您可能期望混合类型(数字、字符串或对象等)并为其提供回退:input || fallback 是有效的,但会拒绝空字符串和零。除非您明确不想要其中任何一个,否则通常会出错。

简单地说,您应该始终使用无效合并运算符??。弄清楚它是否是潜在的错误将减少认知负担。也没有太多理由避免它。它比布尔 OR 更新,因此它不适用于旧环境,这是真的,但是现在您可能应该为这些环境编译代码。如果您不能或不想这样做,那么您别无选择,应该使用布尔 OR。

【讨论】:

感谢@vlaz,这是最明确的答案(我最大胆的说最好的部分)——如何判断何时使用?或 || 以及一些示例。 @mikemaccana 我正在处理这些问题 :D 简短的回答 - 如果你想防止null,请使用??。如果要丢弃零和空字符串,请使用||。老实说,如果您支持较旧的环境并且不转译您的代码,我想不出您现在想要|| 的许多原因除了。但这无论如何都是小众市场。【参考方案4】:

如果左边是falsy,OR 运算符|| 使用右值,而如果左边是nullundefined,则空值合并运算符?? 使用右值。

如果缺少第一个运算符,这些运算符通常用于提供默认值。

但如果您的左值可能包含 ""0false(因为这些是 falsy values),则 OR 运算符 || 可能会出现问题

console.log(12 || "not found") // 12
console.log(0  || "not found") // "not found"

console.log("jane" || "not found") // "jane"
console.log(""     || "not found") // "not found"

console.log(true  || "not found") // true
console.log(false || "not found") // "not found"

console.log(undefined || "not found") // "not found"
console.log(null      || "not found") // "not found"

在许多情况下,如果 left 是 nullundefined,您可能只需要正确的值。这就是 nullish 合并运算符 ?? 的用途:

console.log(12 ?? "not found") // 12
console.log(0  ?? "not found") // 0

console.log("jane" ?? "not found") // "jane"
console.log(""     ?? "not found") // ""

console.log(true  ?? "not found") // true
console.log(false ?? "not found") // false

console.log(undefined ?? "not found") // "not found"
console.log(null      ?? "not found") // "not found"

虽然 ?? 运算符在 current LTS versions of Node(v10 和 v12)中不可用,但您可以将它与某些版本的 TypeScript 或 Node 一起使用:

?? 运算符已于 2019 年 11 月添加到 TypeScript 3.7。

最近,?? 运算符是 included in ES2020,它受 Node 14(2020 年 4 月发布)支持。

当支持无效合并运算符 ?? 时,我通常使用它而不是 OR 运算符 ||(除非有充分的理由不这样做)。

【讨论】:

【参考方案5】:

基于MDN docs:

与the logical OR (||) operator相反,如果左操作数是falsy值不是nullundefined,则返回。

概念示例:

当使用||falsyNOT undefined 或`null:

false || 'name'; // ==> the 'name' is returned

但是在上述情况下使用?? 时:

false ?? 'name'; // ==> the false is returned

真实示例: 假设,我们有一个phone 变量在我们的表单中不是强制性的,并且空字符串('')对我们有效,如果phone 变量是nullundefined,我们想要doSomething(),现在猜猜看:

当将|| 用于falsyNOT undefined 或`null:

const phone = ''; // assume it became empty string from some action

phone || doSomething(); // ==> damn, the doSomething is run but we want to run it if it's `undefined` or `null` the empty string is valid for us

但是在上述情况下使用?? 时:

const phone = ''; // same assumption like above

phone || doSomething(); // ==> yeah, that's right, the empty string is valid for us and the doSomething is not run

注意:实际上是一个例子,在实际项目中你可以更好地了解这个可爱的操作用法。

注意:对于undefinednull,它们的行为是一样的。

【讨论】:

【参考方案6】:

简而言之:

Nullish Coalescing Operator 区分 nullishnullundefined)和 虚假但已定义值(false0'' 等)。有关详细信息,请参见下图。

对于||(逻辑或)nullish 和 falsey 值是相同的。

let x, y

x = 0
y = x || 'default'   // y = 'default'
y = x ?? 'default'   // y = 0

如上所述,运算符??|| 之间的区别在于一个是检查nullish 值,一个是检查falsey 值。但是,在许多情况下它们的行为相同。这是因为在 JavaScript 中,每个 nullish 值也是 falsey(但不是每个 falsey 值都是 nullish)。

我创建了一个简单的图形来说明 JavaScript 中 nullishfalsey 值的关系:

使用我们在上面学到的知识,我们可以为不同的行为创建一些示例:

let y

y = false || 'default'       // y = 'default'
y = false ?? 'default'       // y = false

y = 0n || 'default'          // y = 'default'
y = 0n ?? 'default'          // y = 0n

y = NaN || 'default'         // y = 'default'
y = NaN ?? 'default'         // y = NaN

y = '' || 'default'          // y = 'default'
y = '' ?? 'default'          // y = ''

由于新的Nullish Coalescing Operator 可以区分无值和虚假值,如果您需要检查是否没有字符串或空字符串,它可能会很有用。通常,您可能大部分时间都想使用?? 而不是||

最后也是最不重要的是它们行为相同的两个实例:

let y

y = null || 'default'        // y = 'default'
y = null ?? 'default'        // y = 'default'

y = undefined || 'default'   // y = 'default'
y = undefined ?? 'default'   // y = 'default'

【讨论】:

【参考方案7】:

作为一个非常简短的规则,您可以以相反的方式看待它:

||(或)returns the first "truthy" value(如果不存在“真实”值,则为最后一个值) ??(无效合并)returns the first "defined" value(如果不存在“已定义”值,则为最后一个值)

例子

x = false || true; // -->  true   (the first 'truthy' value - parameter 2)
x = false ?? true; // -->  false  (the first 'defined' value - parameter 1)

【讨论】:

这个答案比公认的答案和其他答案要简洁明了得多。不错。

以上是关于我应该啥时候使用?? (无效合并)vs || (逻辑或)?的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该在 JavaScript 中使用 delete vs 将元素设置为 null? [复制]

Long vs Integer,long vs int,啥时候使用?

我啥时候应该使用 Tracing vs Logger.NET、Enterprise Library、log4net 或 Ukadc.Diagnostics?

当两张表非常相似时,啥时候应该合并?

理解 Java 中的正则表达式:split("\t") vs split("\\t") - 它们啥时候都可以工作,啥时候应该使用它们

.Net vs SSIS:SSIS 应该用于啥?