等式中操作数的顺序是否重要?(例如,1 == x与x == 1相比)

12

我正在尝试使用Google Closure编译器,并注意到它会将所有相等参数切换,以便变量始终位于比较的右侧。

现在,例如,typeof XMLHttpRequest=="undefined"就变成了"undefined"==typeof XMLHttpRequest,而if(null!==a)则变成了if(a!==null)

我知道它们都能实现同样的功能,但这不是我习惯的风格。有没有什么好处可以让它们交换位置呢?我无法看出有什么好处。

有人能解释一下 Closure编译器为什么要这样做吗?这只是Closure的某个部分编写者的喜好吗?

编辑: 为澄清起见,有人告诉我为什么这可能是良好的编码实践。没错,但这是编译后发生的事情。这种切换是否有性能优势,还是Closure编译器只是想证明一点?


1
在SO上以不同形式多次提问。 - Mitch Wheat
2
啊,好老的 Yoda 表达式 - zzzzBov
1
实际上,这与更一般的尤达条件问题不同,请在回答/评论/投票之前仔细阅读问题。 - Joachim Sauer
6个回答

12

常见的做法是在像C/C++这样的语言中进行,以避免您不小心执行

if (a = null) {
    // sets a to null and everyone is happy.
    // but probably meant to check if a is null or not.
    // via (a == null)
}

if (null = a) {
    // won't compile
}

它也被用于Java中,以防止在这种情况下抛出NullPointerException:"foo".equals(someString); - jahroy
9
值得一提的是,任何一种体面的编译器(或脚本语言的类 lin 工具)在使用if(a = null)时都会显示警告,除非你使用if((a = null))明确表达你的意图。 - ThiefMaster
嗨John,我想知道你是否可以回复一下我对Alex答案的跟进评论。谢谢你的回答,但我想知道为什么Closure会这样做。如果在我的原始代码中有一个错误,比如(a=1),我相信Closure可能会将其保留,因为(a=1)仍然是有效的代码。为什么编译后的代码要按这种方式排序操作数呢?我不认为一个理智的程序员会想再次处理已经编译好的代码,而不是参考源代码。 - Nathan
1
不了解 Closure,无法给出可靠的答案,但是 if (1=a) 不是有效的 JS 代码,因此编译后的代码将无法运行。如果使用 if (a=1),则更容易找到错误,尽管它能够运行,但不会产生正确的结果。 - John3136
@John3136 谢谢,感谢你回复我。看起来最合理的解释就是gzip在操作数交换后可以更好地压缩。 - Nathan
@Nathan - 我也不是gzip专家,但这对我来说毫无意义!你有任何信息来源的链接吗?- 我很想了解更多! - John3136

11

编译器改变代码书写顺序是为了更好地使用gzip进行压缩。编译器并不关心提高代码可读性或方便编辑的问题。通过改变顺序,常见比较语句如 "if (x == null) ... if (y == null) ..." 变成了 "if (null == x) ... if (null == y) ..." Gzip能够找到 "if (null ==" 并替换为一个单一的标记。这不是一个很大的改进,但在大型代码库中它是累积增加的。


那很有道理。我后来读到,在高级优化器模式下,void 0 可以纯粹为了大小/压缩目的替换 undefined。我会将其标记为答案,但如果您能够实际展示操作数交换后 gzip 压缩效果更好,那就更好了! - Nathan
@John,你能提供一个链接或更多细节吗?听起来很奇怪,但我很想了解更多! - John3136
2
这里是问题所在:http://code.google.com/p/closure-compiler/issues/detail?id=461。像这样的增强功能都会在谷歌的代码库上进行测试(几乎每个主要产品,如地图和Gmail)。 - Chad Killingsworth
@John3136 我对gzip的了解不多,但仔细想想,如果你在整个代码库中多次重复使用同一代码,那么压缩效果会更好是有道理的。比如字符串 typeof objectA=="undefined"typeof objectB=="undefined",我们需要压缩2个非唯一的字符串 typeof object=="undefined"。如果你重新排列顺序,可以将其压缩成一个非唯一的字符串 "undefined"==typeof object - Nathan
@Nathan 所以从 gzip 的角度来看,更多的是关于一致性而不是实际顺序?这确实有道理。 - John3136
确切地说,它是试图构建更长的公共字符串。 - John

3

是的,你不能给常量赋值,而且==很容易打错(有时你可能会忘记一个,使用了=)。

例如,以下两种表达方式有什么差异...

if (a == 1) { }

...and...

if (a = 1) { }

? 第二个表达式的结果将始终为true,无论a的值是什么。

如果你交换左右两侧的表达式,你会立即看到好处...

if (1 == a) { }

...将按预期工作并...

if (1 = a) { }

如果您试图将值分配给常量,则会失败。


谢谢您的回答,但我想知道为什么Closure会这样做。如果在我的原始代码中有一个错误,比如(a=1),Closure会将代码编译成这种格式,我相信Closure可能只会将其保留,因为(a=1)仍然是有效的代码。为什么编译后的代码要以这种方式排序操作数呢?我认为一个理智的程序员不会想再次处理已经编译好的代码,而是会参考源代码。 - Nathan

3
我知道这是为了防止...
if (x = 5) { }

如果你将它反转
if (5 = x) { }

您将会得到一个编译错误。
但是,如果您将其写成:
if (5 == x) { }

它将会编译成功。


1

我的脑袋解析

if( x < y )

略快于

if( y > x )

可能是因为实数轴总是从左到右定向,因此使条件更容易可视化。

然而,在Java中编写更加实用。

if( "string".equals(x) ) {...

与“更自然”相反

if( x.equals("string") ) {...

为了消除任何可能导致NPE的机会。


说“可能”没有任何好处。做一些基准测试并找出答案。 - alex
3
@alex在谈论人脑的基准测试吗?我认为这就是Tegiri所指的。同样地,我的大脑理解"x == 1"比"1 == x"更快。 - Nathan
1
程序员的思维才是最重要的,因为编译器可以修复。 - Tegiri Nenashi
1
@Nathan 对,我没有像我想的那样仔细阅读答案。 - alex

1

只是一种廉价的替代方法,用于静态分析常见错误的特定情况。


所以你的意思是编译器首先部分地去除代码中的错误,即使这对最终结果没有任何作用?我想这是有道理的,毕竟lint工具可能是在编译器之前完成的。 - Nathan
我认为是这样的,因为通常情况下,在低级别中,值出现在处理器(或可能是某种解释器替代方案)寄存器中,并通过操作码进行比较。 - Yuriy Vikulov
例如,在C#中你不能犯这样的错误,但在C++或C中可以。 - Yuriy Vikulov

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