关于javascript:构造x = x ||是什么

What does the construct x = x || y mean?

我正在调试一些javascript,无法解释这个||是做什么的?

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

有人能给我个提示吗,为什么这家伙要用var title = title || 'ERROR'?我有时也会在没有var声明的情况下看到它。


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

这是书写的简写:

1
2
3
if (!title) {
  title ="Error";
}

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

1
a OR b

如果abtrue,则评估为true。所以如果a是真的,你根本不需要检查b。这被称为短路布尔计算,因此:

1
var title = title ||"Error";

基本上检查title是否评估为false。如果返回,则返回"Error",否则返回title


什么是双管操作员(||

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

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

所以基本上它是这样工作的:

1
2
3
4
5
6
7
8
9
function or(x, y) {
  if (x) {
    return true;
  } else if (y) {
    return true;
  } else {
    return false;
  }
}

如果你还是不明白,看看这张表:

1
2
3
4
      | true   false  
------+---------------
true  | true   true  
false | true   false

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

它在javascript中有什么不同?

JavaScript有点不同,因为它是一种松散类型的语言。在这种情况下,它意味着可以使用值不是布尔值的||运算符。尽管它没有意义,但您可以将此运算符用于例如函数和对象:

1
(function(){}) || {}

那里发生了什么?

如果值不是布尔值,则javascript会隐式地与布尔值进行对话。即如果值为假(如0""nullundefined(另见javascript中的所有假值),则按false处理,否则按true处理。

所以上面的例子应该给出true,因为空函数是真实的。嗯,它没有。它返回空函数。这是因为javascript的||操作符不能像我在开头写的那样工作。其工作方式如下:

  • 如果第一个值为假,则返回第二个值。
  • 如果第一个值为truthy,则返回第一个值。

惊讶?实际上,它与传统的||操作符"兼容"。它可以写成以下函数:

1
2
3
4
5
6
7
function or(x, y) {
  if (x) {
    return x;
  } else {
    return y;
  }
}

如果您将一个真实值作为x传递,它将返回x,即真实值。因此,如果您稍后在if条款中使用它:

1
2
3
4
5
6
7
8
(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作为参数传递。请考虑以下示例:

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

乍一看是有效的。但是,如果您将false作为flagA参数传递(因为它是布尔值,即可以是truefalse),会发生什么情况?它将变成true。在本例中,无法将flagA设置为false

最好明确检查flagA是否为undefined,如下:

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

虽然它更长,但它总是有效的,更容易理解。

您也可以使用ES6语法作为默认函数参数,但请注意,它在旧的浏览器(如IE)中不起作用。如果你想支持这些浏览器,你应该使用babel开发你的代码。

另请参见MDN上的逻辑运算符。


如果未设置标题,请使用"错误"作为默认值。

更通用:

1
var foobar = foo || default;

读取:将foobar设置为foodefault。你甚至可以把这个链接很多次:

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


再解释一下……

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

1
2
3
4
5
6
7
8
9
10
 X | Y | X || Y
---+---+--------
 F | F |   F    
---+---+--------
 F | T |   T    
---+---+--------
 T | F |   T    
---+---+--------
 T | T |   T    
---+---+--------

注意到了吗?如果X为真,则结果始终为真。所以如果我们知道X是真的,我们就不必检查Y。因此,许多语言为逻辑-or和逻辑-and实现"短路"评价。他们检查第一个元素,如果这是真的,他们根本不需要检查第二个元素。结果(逻辑上)是相同的,但在执行方面,如果第二个元素的计算成本很高,则可能存在巨大的差异。

那么这和你的例子有什么关系呢?

1
var title   = title || 'Error';

让我们看看这个。title元素被传递给您的函数。在javascript中,如果不传递参数,则默认为空值。同样,在javascript中,如果变量是空值,则逻辑运算符认为它是假的。因此,如果用给定的标题调用这个函数,它是一个非假值,因此被分配给局部变量。但是,如果没有给定值,则为空值,因此为假。然后,logical-or运算符计算第二个表达式并返回'error'。所以现在局部变量的值为'error'。

这是因为在JavaScript中实现了逻辑表达式。它不返回正确的布尔值(truefalse,而是返回根据某些规则给出的值,即被认为等同于true的值和被认为等同于false的值。查找您的javascript引用,了解在布尔上下文中javascript认为是对的还是错的。


双管代表逻辑"或"。当"参数未设置"时,情况并非如此,因为如果您有这样的代码,则严格在JavaScript中:

1
2
function foo(par) {
}

然后调用

1
2
3
4
5
foo()
foo("")
foo(null)
foo(undefined)
foo(0)

不是等效的。

double pipe()将第一个参数强制转换为boolean,如果结果boolean为true,则执行赋值,否则它将指定正确的部分。

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

例如,我们有一个函数setsalary,它有一个可选参数。如果用户不提供参数,则应使用默认值10。

如果您这样做:

1
2
3
function setSalary(dollars) {
    salary = dollars || 10
}

这将在调用时产生意外结果,如

1
setSalary(0)

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


基本上,它检查计算值之前的值是否为真,如果为真,则取该值;如果不是,则取计算值之后的值。

它将取之后的值(据我所知):

  • 未定义
  • ""(空或空字符串)


为了给我之前所说的一切增加一些解释,我应该给你一些例子来理解逻辑概念。

1
2
var name = false ||"Mohsen"; # name equals to Mohsen
var family = true ||"Alizadeh" # family equals to true

它意味着,如果左侧被评估为一个真语句,它将完成,左侧将被返回并分配给变量。在其他情况下,将返回并分配右侧。

和运算符的结构相反,如下所示。

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

虽然Cletus的答案是正确的,但我觉得应该在javascript中添加更多关于"评估为假"的细节。

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

不仅要检查是否提供了标题/msg,而且还要检查其中一个是否有问题。即以下其中一种:

  • false.
  • 0 (zero)
  • "" (empty string)
  • null.
  • undefined.
  • NaN (a special Number value meaning Not-a-Number!)

所以在这条线上

1
var title = title || 'Error';

如果title是truthy(即,不是falsy,所以title="titleMessage"等),那么布尔或()运算符找到一个"true"值,这意味着它的计算结果为true,因此它短路并返回真值(title)。

如果标题不正确(即上面列表中的一个),则布尔或()运算符找到了一个"false"值,现在需要计算运算符"error"的另一部分,该部分的计算结果为true,因此返回。

如果操作员的两边都评估为假,那么(经过一些快速的Firebug控制台实验之后),它会返回第二个"falsy"操作员。

1
return ("" || undefined)

返回未定义,这可能允许您在尝试将标题/消息默认为"时使用此问题中询问的行为"。即跑步后

1
2
var foo = undefined
foo = foo ||""

foo将设置为"


双管操作工

这个例子有用吗?

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

也可以

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

引述:"构造x=x y是什么意思?"

指定默认值。

这意味着提供默认值y到x,如果X仍在等待它的值,但还没有收到它,或者故意忽略它,以便返回到默认值。


我还得再加一件事:这一点点的速记是可憎的。它错误地使用了一个意外的解释器优化(如果第一个操作是正确的,则不必担心第二个操作)来控制一个分配。这种用法与操作员的目的无关。我认为它不应该被使用。

我更喜欢使用三元运算符进行初始化,例如,

1
var title = title?title:'Error';

它使用一行条件操作来实现正确的目的。它仍然用Truthinness玩难看的游戏,但这是你的javascript。