【问题标题】:What does the construct x = x || y mean?构造 x = x || 是什么?你的意思是?
【发布时间】:2011-02-17 15:01:28
【问题描述】:

我正在调试一些 JavaScript,但无法解释 || 的作用:

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

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

【问题讨论】:

  • 人们已经回答了这个问题......但要特别注意如果第一个值是falsy,而不是undefined,则选择第二个值。我看到doWeDoIt = doWeDoIt || true 的次数足以让我哭泣。 (即 doWeDoIt 现在永远不会是 false
  • 对于那些有 C# 经验的人来说,双管道运算符相当于 null-coalesce 运算符??。 Javascript 评估非空对象 like true(或更好地将空对象评估为假)
  • 不要难过 - JS 是唯一一种允许这种可怕编码的愚蠢语言......并教导将每个函数嵌套到代码行中并将它们扔掉使它们一次性使用是正确的第二次无法使用。 :) 我从事编码工作已经 30 年了,直到不久前我自己才接触 JS,我能感觉到你的痛苦,我只能说,保持“没有任何意义,它只在 JS 中”的便条纸方便,这是我唯一的方法已经过去了! :)
  • 请考虑将接受的答案更改为my answer
  • @usr-local-ΕΨΗΕΛΩΝ - 我看到了那个日期,并且涵盖了那个时间范围:在添加之前,与 null-coelesce 等价的正确值是 value != null ? value : YOUR_DEFAULT|| 当作null coalesce 使用从来都不是一个好习惯。如果您遇到任何“虚假”值,但将其视为“非空”而不是“非虚假”,则会出现微妙的错误。也许打电话给||“真正的合并操作员”? :)

标签: javascript parameters optional-parameters or-operator


【解决方案1】:

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

这是写作的简写:

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

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

a OR b

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

var title = title || "Error";

基本上检查title 的计算结果是否为false。如果是,则“返回”"Error",否则返回title

【讨论】:

  • 不好意思挑剔,但参数不是可选的,参数是检查的
  • 这不是答案,我同意最后一条评论,它甚至不是可选的。这个答案的任何部分都不是正确的,即使是 Perl 参考,因为 Perl 语句实际上使 SENSE 并以完全不同的方式进行评估。 JS 在一个更“转换”的布尔逻辑方法中进行评估,我也发现读/写更令人困惑。下面标题为“什么是双管运算符”的答案实际上是一个正确的答案。
【解决方案2】:

什么是双管道运算符 (||)?

双管道运算符 (||) 是 逻辑 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(另见All falsey values in JavaScript)),它将被视为false;否则被视为true

所以上面的例子应该给true,因为空函数是真实的。好吧,它没有。它返回空函数。那是因为 JavaScript 的 || 运算符不像我在开始时写的那样工作。它的工作方式如下:

  • 如果第一个值为falsey,则返回第二个值
  • 如果第一个值是truthy,则返回第一个值

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

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"));
}

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


您也可以使用the ES6 syntax for default function parameters,但请注意它不适用于旧版浏览器(如 IE)。如果您想支持这些浏览器,您应该使用Babel 转译您的代码。

另见Logical Operators on MDN

【讨论】:

  • +1 - 迄今为止最正确和最完整的答案。而且,对于那些有这个问题的人(包括我们中一些对 JS 不熟悉的资深编码人员)当然应该将整个答案中的大部分注意力集中在这一行上:“虽然它没有意义”,因为这种“loosley typed”根本没有意义对于我们这些在没有它的情况下长大的人。对我们来说,布尔运算符就是这样,而且只有那样......无论谁认为在读/写代码时不得不停下来思考一些古怪的非布尔值到布尔值的转换是个好主意,那天忘记吃药了! :)
  • +1,简而言之:title = title || 'Error' 表示if (title) { title = title; } else { title = 'Error'; }
  • 我实际上不同意“虽然没有意义”这句话。例如,我知道该行无法在 C 中编译,但如果您来自 Ruby,甚至是 Groovy,则很好理解。在 Ruby 中,您可以将其表达得更短,如 title ||= 'Error'
  • FWIW,我会把代码 sn-p 写成function or(x, y) { if (x) return x; else return y; }。这基本上是在 所有 语言 AFAIK 中所做的,包括严格类型和松散类型,包括 JavaScript。 [忽略本次讨论的短路] 不同语言之间的唯一区别是if (x) 在什么条件下为真。 [在严格类型化的语言中,if (x) 仅适用于 true,因此 return xreturn true 相同。]
  • 2021 年重访:使用无效合并运算符 (??)
【解决方案3】:

如果没有设置标题,使用'ERROR'作为默认值。

更通用:

var foobar = foo || default;

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

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

【讨论】:

  • 我发现它令人困惑,因为变量具有相同的名称。当他们不这样做时会容易得多。
【解决方案4】:

再解释一下……

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

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

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

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

var title   = title || 'Error';

让我们来看看。 title 元素被传递给您的函数。在 JavaScript 中,如果您不传入参数,则默认为空值。同样在 JavaScript 中,如果您的变量为空值,则逻辑运算符将其视为 false。因此,如果在给定标题的情况下调用此函数,则它是一个非假值,因此分配给局部变量。但是,如果没有给它一个值,它就是一个空值,因此是假的。然后逻辑-or 运算符计算第二个表达式并返回“错误”。所以现在局部变量被赋予了值'Error'。

这是因为在 JavaScript 中实现了逻辑表达式。它不返回正确的布尔值(truefalse),而是返回根据某些规则给出的值,即什么被认为等同于true,什么被认为等同于false。查看您的 JavaScript 参考,了解 JavaScript 在布尔上下文中认为是真还是假。

【讨论】:

    【解决方案5】:

    基本上,它会检查|| 之前的值是否为真。如果是,就取这个值,如果不是,就取||后面的值。

    它将采用|| 之后的值(据我所知):

    • 未定义
    • 0
    • ''(Null 或 Null 字符串)

    【讨论】:

    • 假 ||空 ||未定义 || 0 || '' || '你忘了 null'
    【解决方案6】:

    虽然Cletus' answer 是正确的,但我觉得应该添加更多关于 JavaScript 中“评估为假”的细节。

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

    不仅检查是否提供了 title/msg,还检查它们是否为falsy。即以下之一:

    • 错误。
    • 0(零)
    • “”(空字符串)
    • 空。
    • 未定义。
    • NaN(一个特殊的数字值,意思是非数字!)

    所以排队

    var title = title || 'Error';
    

    如果标题为真(即,不是假的,因此 title = "titleMessage" 等),则布尔 OR (||) 运算符找到了一个“真”值,这意味着它的计算结果为真,因此它是简短的 -电路并返回真值(标题)。

    如果标题是假的(即上面的列表之一),那么布尔或 (||) 运算符找到了一个“假”值,现在需要评估运算符的另一部分“错误”,这计算结果为真,因此返回。

    如果运算符的两边都评估为 false,它似乎也会(在一些快速的 firebug 控制台实验之后)返回第二个“falsy”运算符。

    return ("" || undefined)
    

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

    var foo = undefined
    foo = foo || ""
    

    foo 将被设置为 ""

    【讨论】:

      【解决方案7】:

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

      function foo(par) {
      }
      

      然后调用

      foo()
      foo("")
      foo(null)
      foo(undefined)
      foo(0)
      

      不等价。

      双竖线 (||) 会将第一个参数转换为布尔值,如果结果布尔值为真 - 进行赋值,否则将赋值正确的部分。

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

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

      如果你这样做检查:

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

      这样的调用会产生意想不到的结果:

      setSalary(0)
      

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

      【讨论】:

        【解决方案8】:

        双管运算符

        这个例子可能有用:

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

        也可以是:

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

        【讨论】:

          【解决方案9】:

          || 是布尔 OR 运算符。与 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"
          

          【讨论】:

            【解决方案10】:

            为了对我之前所说的所有内容进行一些解释,我应该给你一些例子来理解逻辑概念。

            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
            

            【讨论】:

              【解决方案11】:

              引用:“构造 x = x || y 是什么意思?”

              分配默认值。

              这意味着为 x 提供一个默认值 y, 以防 x 仍在等待其值但尚未收到它,或者为了回退到默认值而故意省略它。

              【讨论】:

              • 这就是构造的确切含义,也是它的唯一含义。它在很大程度上作为编写函数的子程序,可以作为原型、独立函数以及借用的方法应用于另一个元素。它的主要和唯一职责是修改目标的参考。示例:function getKeys(x) { x = x || this ; .... } 无需修改即可用作独立函数、原型中的属性方法以及元素的方法,该方法可以获取另一个元素作为其参数,如 ` [element].getKeys(anotherElement);`
              【解决方案12】:

              我还要补充一点:这种速记是可憎的。它滥用了意外的解释器优化(如果第一个操作是真实的,则不会打扰第二个操作)来控制分配。这种使用与运营商的目的无关。我不认为它应该被使用。

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

              var title = title?title:'Error';
              

              为了正确的目的,这使用了单行条件操作。它仍然在玩不雅的游戏,但那是你的 JavaScript。

              【讨论】:

              猜你喜欢
              • 2020-03-18
              • 2021-08-02
              • 2015-03-24
              • 2020-10-02
              • 2011-05-27
              • 2018-05-03
              • 1970-01-01
              • 2014-09-19
              • 2015-12-30
              相关资源
              最近更新 更多