JavaScript: || 替代 IF 语句 - 这合法且跨浏览器有效吗?

33

看起来:

if (typeof a == 'undefined') {
    a = 0;
}

(typeof a != 'undefined') || (a = 0)

在JavaScript中具有相同的效果。

我很喜欢第二种方法,因为它是一行短小的代码,但这是否合法并且跨浏览器有效呢? 我的意思是,jslint说它有错误。我应该毫不担心地使用它吗?


5
一个问题:复杂的代码有什么作用? - Subir Kumar Sao
34
稍微更好的写法是 a = (typeof a !== 'undefined') ? a : 0。该语句的意思是,如果变量a已经定义,则将其值赋给a,否则将a的值设置为0。 - Blender
8
"typeof a != 'undefined')||(a = 0)"非常适合JavaScript的“加密”。建议终身,不要只为自己编写代码。 - Jashwant
2
@Blender:不,这会浪费一个赋值,对于 ES5 和赋值式属性设置器来说可能很重要。无论如何,a || a=default 是在函数/构造函数中设置默认值的常见习语。 - Phil H
2
喜欢过于“聪明”的代码只是一个阶段,经验可以治愈它。 :) - simon
显示剩余7条评论
10个回答

76

在我看来,|| (a = 0)|| (a == 0) 非常相似,因此容易让人混淆。有一天,过于热心的开发人员会仅仅"修复"它,改变你代码的意思。这样其他开发人员就需要花费一些时间来弄清楚这是否是你的意图还是一个简单的错误。

实际上,这正是JSLint试图传达的信息:

期望条件表达式,而看到的是一个赋值表达式。

我避免使用令人困惑的结构,因为它们会影响代码可读性。a = a || 0; 更易于识别,并且意义相似。


1
即使我认为开发人员可能会将其误认为是打字错误。 - Subir Kumar Sao
4
顺便提一下,在变量a初始值为0的情况下,执行a = a || 0这条语句有点无意义,因为0为假值,会将a重新赋值为0。 - Phil H
1
一个快速的jsperf显示(typeof a == 'undefined')和(!a)一样快,因此即使避免明显的字符串比较也没有速度提升。 - Phil H
7
a = a || 0不完全相同。对于falsenull,你会得到0,这可能不是问答者想要的。 - Izkata
1
我可能错了,但我的假设是OP真正想要的是var a=0,而不是a=0。如果是这样,(var a=0)显然是一个变量赋值,不能与(a==0)混淆。 - Christophe
显示剩余3条评论

31

为什么不使用更简单的方式,比如:

a = a || 0;
或者
a = a ? a : 0;
在这两种情况下,你也可以清楚地看到在行的开头就将某些东西赋值给了 a,而不需要阅读整个代码行,并弄清左侧或右侧是否发生了任何具有重大影响的函数调用... 或者弄清双方的功能,以决定可能存在多少潜在的程序范围变化。如果需要包含整个类型检查,它仍然不会非常庞大。
a = (typeof a !== "undefined") ? a : 0;  // [parentheses are there for clarity]

三元赋值(a = cond ? a : default)在 a 通过测试时浪费了一次赋值。写成 cond || (a=default) 只有在 a 未通过测试时才执行赋值。这在 ES5(Javascript 的新版本)和属性到来时更为重要,因为属性可以具有类似赋值的 setter。 - Phil H
@PhilH,你在各种评论中一直这么说,但我没有看到它。a = cond ? a : 0是一个赋值语句,当然,a = a || 0也是。你提到的额外赋值在哪里? - kojiro
2
简短的版本不是a = a || 0,而是cond || a = 0。它的行为就像if(cond){a = 0};只有在条件为真时才执行赋值操作。另一个版本会在条件为假时执行a = a的赋值操作,这是一种浪费。 - Phil H
顺便提一下,@PhilH,您可以通过用反引号括起文本来在注释中获得引用高亮显示。 - Izkata
1
@PhilH -- 你在谈论微秒级别的“浪费”...这不是额外的函数调用等。事实上,我可以告诉你,你的方法浪费了一个完美的测试机会。默认情况下,a将被赋值为它本身,除非返回值为falsey,然后才会将其赋值为给定的默认值。同样,你的方法将鼓励开发人员修复OR语句(a === 0),或者重载它(a != 1) || (doStuff();doOtherStuff().id();a=16)。就像那些已经使用三元运算符进行分支执行的人一样...这对于维护你的代码的人来说并不是一个好的结局。 - Norguard
显示剩余2条评论

9

这是合法的,而且在所有EcmaScript引擎中都有效。然而,滥用短路求值作为if语句非常不常见。

我的意思是,jslint说它有错误。我能毫无顾虑地使用它吗?

不,JsLint是正确的。这种方法很不寻常和令人困惑,至少对其他开发者来说是这样。它看起来太像一个OR条件 - 然而它没有“主体”。如果你做赋值,变量应该在语句的开始,而不是在某个表达式内部。

我真的很喜欢第二个,因为它是简短的一行代码

那就使用它:

if (typeof a == 'undefined') a = 0;

2
这是一个广阔的世界。在Shell脚本和Perl程序员中,使用短路评估作为if语句并不罕见。我不确定是否同意Javascript应该像C一样。 - Random832

2

您可以使用:

a = typeof(a) !== "undefined" ? a : 0; 

三元赋值(a = cond ? a : default)在a通过测试时浪费了一次赋值。写成cond || (a=default)只有在a未通过测试时才执行赋值。这在ES5(Javascript的新版本)和属性到来时更为重要,因为属性可以具有类似赋值的setter。 - Phil H

1
(typeof a != 'undefined') || (a = 0)

是一个表达式。但其中一个子表达式

(a = 0)

这是一个任务。因此,本质上这是一个带有副作用的表达式。具有副作用的语句(特别是那些是表达式的语句)通常是你在入门编程课程中学习的首要事项之一,不要这样做。那么,仅仅因为它只需要一行代码,为什么要这样做呢?


1

我认为使用这样的结构很糟糕。虽然它可以工作,但代码不易读懂。如果你想要编写单行条件语句,可以开始使用CoffeeScript并编写:

a = 0 if (typeof a == 'undefined');

在您的情况下,当条件和赋值中只有一个变量时,请使用一行JavaScript三元运算符:

a = (typeof a == 'undefined') ? 0 : a;

1

请问您为什么偏爱一行代码呢?

作为人类,我更喜欢可读性强的代码。我的机器则更喜欢短小快速的代码(易于加载和执行)。

如今,像UglifyJS这样的代码压缩工具已经可以缩短代码,因此您可以同时拥有两者,而无需担心这个细节层面。我已将您的代码交给UglifyJS处理,以下是输出结果:

typeof a=="undefined"&&(a=0)

您可以在这里尝试一下:

http://marijnhaverbeke.nl/uglifyjs

【更新】考虑可读性,我的个人偏好是在有选择时使用 if,而在需要回退时使用 ||。你的具体示例似乎是一个回退(如果 a 不存在或未定义,则将值 0 分配给 a),因此我会使用 ||。正如我在评论中所说,在变量 a 尚未声明的情况下,(var a=0) 对我来说更有意义(我不知道您的上下文)。

1

从风格上讲,像 a || a=default 这样设置默认值是进入函数时的常见习语,因为 JavaScript 不强制参数数量。

如果在其他情况下使用此结构,而你真正想要的是 if/else,则可读性将会受到影响。

性能以前在不同的风格之间有所差异,但在今天的快速测试中,if/else 和逻辑运算符的速度相同,但三元操作较慢。


为什么你会在函数进入时给 a 赋值呢?这就是你提到的浪费。如果 a 是标量,那么你不会得到外部作用域的 a 的引用,而是标量值。如果 a 是对象,则要么你浪费时间赋值给 a,因为你将通过返回值进行赋值(在 a 参数上浪费分配),要么你将对 a 进行对象构造,这不应该在那里完成,因为那是一个可维护性代码实例化的可怕位置。var b = doIt(a||obj); 就足够了。 - Norguard
因为JavaScript不能保证函数参数以任何方式设置!如果我构造func(a,b,c),我可以只使用func()来调用它。除非我在进入时设置默认参数值,否则一旦使用参数,我仍然必须检查未定义的值!在强类型语言中,您保证有值,并且引用可以保证传递了对象等。JavaScript没有保证这些事情。 - Phil H
不,我的问题是,假设 function add(a, b) { return a + b; },那么 var c = add(a||a=0, b||b=0);var c = add(a||0, b||0); 之间有什么区别。答案是对于 c 的目的来说,两者完全没有任何区别。 - Norguard
此外,如果在程序中第一次使用 ab,并且您打算在函数调用后继续使用它们...或者这是您的程序中唯一进行类型检查或验证的地方,那就是一个糟糕的地方来执行它。 - Norguard
1
我想说的更多是像这样的 function add(a, b) { a || (a=0); b || (b=0); return a+b; }。我同意,在进入函数时不希望初始化全局变量,但我并没有建议这样做。在 JavaScript 中,传递的是值,类似于 Java;按值传递值,按指针值传递对象。因此,函数可以测试其参数中是否有对象,如果没有,则构造一个默认对象。要求调用者执行此操作对调用者要求过高,并且暗示您永远不希望具有可选参数。 - Phil H

0
此外,我认为这会影响性能,因为。
(typeof a != 'undefined') || (a = 0)

由两个测试组成(即使我们不关心第二个测试)。

然而

if (typeof a == 'undefined') {
    a = 0;
}

只包含一个测试


0

多年来,我一直讨厌“||代替IF”的写法。

现在我终于习惯了,并且喜欢上了这种写法。

我也喜欢用同样的方式使用“&&”。

如果你自己采用简洁的编码风格,阅读别人简洁的代码会更容易。

当然,我完全理解别人的想法。我也曾经历过这个阶段。


你能提供其他你采用的简洁实践的例子吗? - Andrew Kozak

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