构造 x = x || y 的意思是什么?

308

我正在调试一些JavaScript代码,但不确定 || 的作用:

function (title, msg) {
  var title = title || 'Error';
  var msg   = msg || 'Error on Request';
}

为什么这个人要使用 var title = title || 'ERROR'?有时我也看到它没有 var 声明。


52
人们已经回答了这个问题,但需要非常注意的是,如果第一个值为“假值”(falsy),而不仅仅是“未定义”(undefined),则将选择第二个值。我已经看到过很多像“doWeDoIt = doWeDoIt || true”的代码,这让我感到非常难过。(也就是说,doWeDoIt现在永远不会是false) - Matt
6
对于有C#经验的人来说,双竖线运算符相当于空值合并运算符 ?? 。JavaScript 对非空对象的求值类似于 true(或更好地将空对象评估为 false)。 - usr-local-ΕΨΗΕΛΩΝ
4
不要难过 - JS是唯一允许这种可怕编码的愚蠢语言...还有教导将每个函数嵌套到代码行中并将它们丢弃使其不可重复使用。我从事编码已经30年了,直到不久前我才开始接触JS,我感同身受。我的建议是:准备一个“毫无意义,只有在JS中才有”的提示表,这是我解决问题的唯一途径! :) - Collin Chaffin
2
请考虑将接受的答案更改为我的答案 - Michał Perłakowski
1
@usr-local-ΕΨΗΕΛΩΝ - 我看到了那个日期,并且我涵盖了那个时间范围:在添加之前,与null-coelesce的正确等效是value!= null?value:YOUR_DEFAULT。使用||作为null coalesce从来不是一个好习惯。如果您遇到任何“falsey”值,但认为它是“非空”而不是“非falsey”,则会出现微妙的错误。也许称||为“truthy coalesce operator”? :) - ToolmakerSteve
显示剩余9条评论
12个回答

258

双竖线运算符 (||) 是什么?

双竖线运算符 (||) 是逻辑 OR 运算符。在大多数编程语言中,它的工作方式如下:

  • 如果第一个值是false,则检查第二个值。如果第二个值为true,则返回true,如果第二个值为false,则返回false
  • 如果第一个值为true,则无论第二个值是什么,它始终返回true

因此,基本上它的作用就像这个函数:

function or(x, y) {
  if (x) {
    return true;
  } else if (y) {
    return true;
  } else {
    return false;
  }
}

如果您仍然不理解,可以查看这个表格:

      | true   false  
------+---------------
true  | true   true   
false | true   false  

换句话说,只有当两个值都为假时它才是假的。

JavaScript中有什么不同呢?

JavaScript有些不同,因为它是一种弱类型语言。在这种情况下,这意味着您可以使用||运算符来处理非布尔值。尽管这没有意义,您可以将此运算符与例如函数和对象一起使用:

(function(){}) || {}

那里会发生什么?

如果值不是布尔值,JavaScript 会进行隐式转换为布尔值。这意味着,如果该值是假值(例如:0""nullundefined(另请参见:JavaScript 中所有的假值)),它将被视为 false;否则,它被视为 true

所以上面的例子应该返回 true,因为空函数是真值。嗯,实际上并不是。它返回了空函数。这是因为 JavaScript 的 || 运算符并不像我一开始写的那样工作。它的工作方式如下:

  • 如果第一个值是假值,它将返回第二个值
  • 如果第一个值是真值,它将返回第一个值

惊讶吗?实际上,它与传统的 || 运算符是“兼容”的。它可以被写成以下函数:

function or(x, y) {
  if (x) {
    return x;
  } else {
    return y;
  }
}

如果你传递一个真值作为 x,它会返回 x,也就是一个真值。所以如果你在后面的 if 语句中使用它:

(function(x, y) {
  var eitherXorY = x || y;
  if (eitherXorY) {
    console.log("Either x or y is truthy.");
  } else {
    console.log("Neither x nor y is truthy");
  }
}(true/*, undefined*/));

你会得到"Either x or y is truthy."

如果x是假值,eitherXorY将是y。在这种情况下,如果y是真值,你会得到"Either x or y is truthy.";否则,你会得到"Neither x nor y is truthy"

实际问题

现在,当你知道||运算符的工作原理后,你可以自己理解x = x || y的含义。如果x是真值,x就会被赋值为x,实际上什么也不会发生;否则y会被赋值给x。它通常用于在函数中定义默认参数。然而,它经常被认为是一种糟糕的编程实践,因为它阻止了你将一个假值(不一定是undefinednull)作为参数传递。考虑以下例子:

function badFunction(/* boolean */flagA) {
  flagA = flagA || true;
  console.log("flagA is set to " + (flagA ? "true" : "false"));
}

乍一看似乎是有效的。但是,如果你将false作为flagA参数传递会发生什么呢(因为它是布尔类型,即可以是truefalse)?它将变为true在这个例子中,没有办法将flagA设置为false

更好的做法是明确检查flagA是否为undefined,如下所示:

function goodFunction(/* boolean */flagA) {
  flagA = typeof flagA !== "undefined" ? flagA : true;
  console.log("flagA is set to " + (flagA ? "true" : "false"));
}

虽然它更长,但它总是有效并且更易理解。


你也可以使用ES6默认函数参数语法,但请注意它在较旧的浏览器(如IE)中不起作用。如果你想支持这些浏览器,你应该使用Babel进行转译

另请参阅MDN上的逻辑运算符


19
这是需要翻译的内容:+1 - by far the most correct and complete answer. And, for those with this question (some of us veteran coders new to JS included) should certainly focus the most out of this entire answer on this line: "Though it makes no sense" because this "loosely typed" will simply never make sense to those of us that grew up without it. For us, a boolean operator is just that and ONLY that......and whomever ever thought it would be a good idea to have to stop and think through some wacky conversion of non-boolean values to boolean while reading/writing code, forgot to take their meds that day! :)这是翻译后的结果:+1 - 明显是最正确和最完整的答案。对于那些有此问题的人(包括我们这些新接触JS的老手程序员),一定要重点关注答案中的这句话:“虽然没有意义”,因为这种“弱类型”对于那些没有接触过它的人来说根本就没意义。对我们而言,布尔运算符仅仅是布尔运算符,没有别的……而且,谁会认为在阅读/编写代码时必须停下来思考一些奇怪的将非布尔值转换为布尔值的方式是个好主意,他们可能当天忘记吃药了! :) - Collin Chaffin
3
简述:title = title || 'Error' 的意思是,如果 title 存在,则将 title 赋值给自身;否则将其赋值为 'Error' - Andre Elrico
我其实不同意“虽然没有意义”的那句话。例如在C语言中,我理解这行代码无法编译通过,但是如果你来自Ruby或者Groovy的话,这行代码是很容易理解的。在Ruby中,你甚至可以更简洁地表达为title ||= 'Error' - Elliot Nelson
就我个人而言,我会将代码片段编写为 function or(x, y) { if (x) return x; else return y; }。据我所知,这基本上是所有语言(包括 JavaScript)中都采用的方法,无论是严格类型还是松散类型。[在本讨论中忽略短路]不同语言之间唯一的区别在于何时 if (x) 为真。[在严格类型的语言中,if (x) 只对 true 为真,因此 return x 等同于 return true]。 - ToolmakerSteve
1
2021年重温:使用空值合并运算符(??) - user4945014

244

这意味着title参数是可选的。因此,如果您在不带参数的情况下调用该方法,它将使用默认值"Error"

这是一种简写方式,等同于以下代码:

if (!title) {
  title = "Error";
}

这种使用布尔表达式的速记技巧在Perl中也很常见。使用以下表达式:

a OR b

如果a或者btrue,则它的值将评估为true。因此,如果a为真,则根本不需要检查b。这被称为短路布尔运算,所以:

var title = title || "Error";

这个函数基本上是检查 title 是否评估为 false。如果评估为 false,它会“返回”"Error",否则它会返回title


5
抱歉我要挑剔一下,但这里的"argument"不是可选的,而是需要进行检查的。 - themightybun
5
这不是答案,我同意上一条评论,这甚至不是可选项。这个答案的任何部分都不正确,即使是关于Perl的参考,因为Perl语句实际上是有意义的,并且以完全不同的方式进行评估。JS使用了一种更加“转化”的布尔逻辑方法来执行eval,我发现它更难以阅读/编写。下面标题为“什么是双管运算符”的答案才是正确的答案。 - Collin Chaffin

30

如果未设置标题,则使用“ERROR”作为默认值。

更通用的说法:

var foobar = foo || default;

读取:将 foobar 设置为 foodefault。 你甚至可以多次链接它:

var foobar = foo || bar || something || 42;

1
我觉得很困惑,因为变量名字一样。如果变量名不同会容易得多。 - Norbert Norbertson

14

进一步解释一下...

|| 运算符是逻辑或运算符。如果第一个部分为真,则结果为真;如果第二个部分为真,则结果为真;如果两个部分都为真,则结果为真。为了清晰起见,这里是一个表格:

 X | Y | X || Y 
---+---+--------
 F | F |   F    
---+---+--------
 F | T |   T    
---+---+--------
 T | F |   T    
---+---+--------
 T | T |   T    
---+---+--------

现在注意一下这里,如果X为真,则结果总是为真。因此,如果我们知道X为真,就不必检查Y。因此,许多语言对逻辑或(从另一个方向来的逻辑与)实现“短路”计算器。它们检查第一个元素,如果那个元素为真,它们根本不会再检查第二个元素。结果(在逻辑术语中)相同,但是在执行方面,如果第二个元素很难计算,那么可能存在巨大的差异。
那么这与你的例子有什么关系?
var title   = title || 'Error';

让我们来看看这个。 title 元素被传递到您的函数中。在 JavaScript 中,如果您没有传递参数,则默认为 null 值。此外,在 JavaScript 中,如果您的变量是 null 值,则逻辑运算符将其视为 false。因此,如果使用给定的标题调用此函数,则它是非 false 值,因此分配给本地变量。但是,如果没有给出值,则它是 null 值,因此为 false。然后,逻辑或运算符评估第二个表达式,并返回“Error”。因此,现在本地变量被赋予“Error”值。
这是因为 JavaScript 中逻辑表达式的实现方式。它不返回正确的布尔值(truefalse),而是根据一些规则返回其所给定的值,以确定什么被视为等效于 true,什么被视为等效于 false。请查阅您的 JavaScript 参考资料,了解 JavaScript 在布尔上下文中认为什么是 true 或 false。

12

|| 是布尔“或”运算符。与JavaScript一样,undefined、null、0、false 被认为是falsy值。

它的意思很简单。

true || true = true
false || true = true
true || false = true
false || false = false

undefined || "value" = "value"
"value" || undefined = "value"
null || "value" = "value"
"value" || null = "value"
0 || "value" = "value"
"value" || 0 = "value"
false || "value" = "value"
"value" || false = "value"

11

基本上,它检查在||之前的值是否为真。如果是,则采用该值,如果不是,则采用||之后的值。

会采用||之后的值(根据我的记忆):

  • undefined
  • false
  • 0
  • ''(空或空字符串)

1
false || null || undefined || 0 || '' || '你忘了null' - Dziamid

8

虽然Cletus' answer是正确的,但我认为在JavaScript中关于“评估为false”的内容应该添加更多细节。

var title = title || 'Error';
var msg   = msg || 'Error on Request';

这不仅是检查标题/消息是否已提供,还要检查它们中的任何一个是否为falsy。即以下之一:

  • false。
  • 0(零)
  • ""(空字符串)
  • null。
  • undefined。
  • NaN(表示非数字的特殊数字值!)

因此,在该行中:

var title = title || 'Error';

如果标题为真值(即非假值,因此标题=“titleMessage”等),那么布尔OR(||)运算符已经找到一个“true”值,这意味着它求值为true,因此它短路并返回true值(标题)。
如果标题是假值(即上面列表中的一个),则布尔OR(||)运算符已经找到一个“false”值,现在需要评估运算符的另一部分“Error”,该部分求值为true,并因此返回。
还可以看出(经过一些快速的firebug控制台实验),如果运算符的两侧都求值为false,则返回第二个“falsy”运算符。
即。
return ("" || undefined)

返回未定义,这可能是为了让您在尝试将标题/消息默认设置为“”时使用本问题中询问的行为。即,在运行后。

var foo = undefined
foo = foo || ""

foo将被设置为空字符串


8

双竖线表示逻辑“或”。但是,在“参数未设置”时,这并不是真正的情况,因为在JavaScript中,如果您有以下代码:

function foo(par) {
}

然后调用。
foo()
foo("")
foo(null)
foo(undefined)
foo(0)

不等价。

双竖线(||)将把第一个参数转换为布尔值,如果结果为true,则执行赋值操作;否则,将赋值右侧的部分。

这很重要,如果您检查未设置的参数。

假设我们有一个函数setSalary,它有一个可选参数。如果用户没有提供参数,则应使用默认值10。

如果您像这样进行检查:

function setSalary(dollars) {
    salary = dollars || 10
}

这将导致一次意外的调用结果,例如:

setSalary(0)

它仍将按照上述流程设置10。

6

双管运算符

以下示例可能会有所帮助:

var section = document.getElementById('special');
if(!section){
     section = document.getElementById('main');
}

它也可以是:

var section = document.getElementById('special') || document.getElementById('main');

4
为了更好的解释之前说过的内容,我需要举几个例子来帮助你理解逻辑概念。
var name = false || "Mohsen"; # name equals to Mohsen
var family = true || "Alizadeh" # family equals to true

这意味着如果左侧被评估为真,则操作将结束并返回左侧,然后将其分配给变量。在其他情况下,右侧将被返回并分配。

And运算符具有相反的结构,如下所示。

var name = false && "Mohsen" # name equals to false
var family = true && "Alizadeh" # family equals to Alizadeh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接