JavaScript中三元运算符的操作符优先级

117

我似乎无法理解这段代码的第一部分 (+=) 与 三元运算符 的结合。

h.className += h.className ? ' error' : 'error'

我认为这段代码的工作方式如下:
h.className = h.className + h.className ? ' error' : 'error'

但那是不正确的,因为它在我的控制台中产生了一个错误。

我应该如何正确解释这段代码?

7个回答

141

使用:

h.className = h.className + (h.className ? ' error' : 'error')

你希望操作符适用于h.className。最好明确指定。

当然,h.className += ' error'不会有任何损害,但这是另一回事。

此外,请注意+优先于三元运算符:JavaScript 运算符优先级


3
我认为应该注意到,虽然 h.className += ' error' 不会造成任何伤害,但如果初始为空,则会在字符串开头留下一个空格。我相信三元操作的目的是产生一个干净漂亮的字符串。 - JMTyler
@JMTyler - 这正是我所指的 - 如果只是为了保留开头的空格而做所有这些工作,那就不值得了(边缘情况包括精确的jQuery或XPath选择器)。无论如何,谢谢。 - Kobi
@Kobi 单单因为操作符优先级的警告,就给你加一分! - Ed Chapel

129

可以这样理解:

<variable> = <expression> ? <true clause> : <false clause>

这个语句的执行方式如下:

  1. <expression> 的计算结果是 true 还是 false?
  2. 如果 <expression> 计算结果是 true,则将 <true clause> 的值分配给 <variable>,忽略 <false clause>,然后继续执行下一条语句。
  3. 如果 <expression> 计算结果是 false,则忽略 <true clause>,并将 <false clause> 的值分配给 <variable>

需要注意的是,在该语言和其他语言中,三元运算符中 <expression> 中的代码应在计算时产生一个布尔结果,即 true 或 false。

对于你的示例,如果使用简写算术运算符,则应将我解释中的 "assigned to" 替换为 "added to" 或类似的内容。


不确定是否适合“完美”的注释 :) 它跳过了任何关于为什么左侧表达式首先“分组”(即因为+比条件/三元运算符具有更高的优先级(实际上,条件运算符在任何表达式中几乎总是最后一个被评估的)。)的解释。 - iCollect.it Ltd

10
+=的作用是添加字符串,但在它右边的三元语句中,它检查h.className是否为假值,如果为未定义,则它将是假值。如果它是真值(即如果已指定类名),则添加一个带空格的错误(即添加一个类),否则添加不带空格的错误。
代码可以按照您建议进行重写,但您需要明确指定h.className用于真值比较,而不是在三元运算符中使用其实际值,所以请确保您不要同时处理值的连接和三元操作。
h.className = h.className + (h.className ? ' error' : 'error');

14
没错,undefined 不是 false,只是被视为是。 - David Hedlund

4
< p > 等号操作符的右侧按从左到右的顺序进行评估。因此,

g.className = h.className + h.className ? ' error' : 'error';`

等同于

h.className = (h.className + h.className) ? ' error' : 'error';

等同于

h.className += h.className ? ' error' : 'error';

您需要将三元运算符括在括号中:

h.className = h.className + (h.className ? ' error' : 'error');

3
if (h.className) {
    h.className = h.className + ' error';
} else {
    h.className = h.className + 'error';
}

应该等同于:
h.className += h.className ? ' error' : 'error';

1

我想选择韦恩的解释

<variable> = <expression> ? <true clause> : <false clause>

让我们考虑两种情况:

情况1

h.className += h.className ? 'true' : 'false'
  • 赋值运算符正常工作,值被添加到末尾
  • 第一次运行时,输出为false
  • 第二次运行。输出:false true -- 值继续追加

案例2:

h.className = h.className + h.className ? 'true' : 'false'
  • 结果与第1个案例不同
  • 第一次运行时,输出为false
  • 第二次输出:false -- 值不会继续追加

说明

在上面的代码中,案例1运行正常

然而

案例2,

h.className = h.className + h.className ? 'true' : 'false'

被执行为

 h.className = (h.className + h.className) ? 'true' : 'false'

h.className + h.className => 被视为三元运算符的表达式,因为三元运算符具有更高的优先级。因此,始终只分配三元表达式的结果。

h.className = h.className + (h.className ? ' error' : 'error')

1
这里的术语不太正确。优先级是语言固有的,你不需要“定义”它。相反,通过引入括号(其优先级高于所有其他运算符),您正在定义评估顺序 - iCollect.it Ltd
@TrueBlueAussie 我接受了。我感谢你的阅读意图 +1 - Angelin Nadar

1

但是我对所有答案都不完全满意,因为它们似乎都不完整。所以我们再次从基本原理开始:

用户的总体目标:

总结代码:“我希望将一个error类名添加到一个字符串中,如果字符串中已经有类名,则可以选择在前面加上一个空格。”

最简单的解决方案

正如Kobi指出,五年前,在类名中加入前导空格不会导致任何已知浏览器的问题,因此最短的正确解决方案实际上是:

h.className += ' error';

那应该是实际问题的实际答案。

尽管如此,所提出的问题是...

1. 为什么这个工作?

h.className += h.className ? ' error' : 'error'

条件/三元运算符的工作原理类似于if语句,将其truefalse路径的结果分配给变量。
因此,该代码之所以有效,是因为它被简单地评估为:
if (h.className IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className += ' error'
else
    h.className += 'error'

2. 这是为什么出错了?

h.className = h.className + h.className ? ' error' : 'error'

这个问题说“在我的控制台中出现错误”,这可能会让你误以为代码无法正常运行。实际上,以下代码确实可以运行,没有错误,但如果字符串不是空的,则返回 ' error',如果字符串空的,则返回 'error',因此不符合要求
该代码始终生成一个仅包含' error''error'的字符串,因为它计算为以下伪代码:
if ((h.className + h.className) IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className = ' error'
else
    h.className = 'error'

这是因为加法运算符(对于一般人来说就是+)的“优先级”(6)比条件/三元运算符(15)要高。我知道这些数字看起来是相反的 优先级意味着语言中每种类型的运算符都按照特定的预定义顺序进行评估(而不仅仅是从左到右)。
参考:JavaScript Operator Precedence">JavaScript运算符优先级

如何更改评估顺序:

现在我们知道了为什么它失败了,你需要知道如何使其工作。
其他一些答案提到了更改优先级,但是你不能这样做。优先级已被硬编码到语言中。那只是一组固定的规则...然而,你可以更改评估顺序...
我们工具箱中可以更改评估顺序的工具是分组运算符(也称为括号)。通过确保括号内的表达式在括号外的操作之前进行评估,它能够实现这一点。这就是它们所做的全部,但这已经足够了。
括号之所以有效,是因为它们(分组运算符)比所有其他运算符都具有更高的优先级(“现在有一个级别0”)。
只需简单地添加括号,您就可以更改评估顺序,以确保先执行条件测试,然后再进行简单的字符串连接。
h.className = h.className + (h.className ? ' error' : 'error')

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