为什么JavaScript在字符串和数字之间对加号和减号运算符的处理方式不同?


73

我不明白为什么JavaScript会这样工作。

console.log("1" + 1);
console.log("1" - 1);

第一行显示11,第二行显示0。为什么JavaScript将第一行作为字符串处理而第二行作为数字处理?


11
+1 -虽然答案为什么是任何人都习惯用JS明显,原因为何明显的答案是真的还是超出了我的理解-我想我不是唯一一个... JS在许多方面失败POLA感叹 计算器.com / questions / 9032856 /…

5
我觉得这个环节应该有任何JavaScript类型的怪事被张贴沿:destroyallsoftware.com/talks/wat
DLeh

@DLeh:我正要发布指向该视频的链接:D
Rocket Hazmat 2014年

Answers:


106

字符串连接完成 +Java因此Javascript将第一个数字1转换为字符串,然后将“ 1”和“ 1”连接为“ 11”。

您无法对字符串执行减法运算,因此Javascript将第二个“ 1”转换为数字,并从1中减去1,从而得出零。


7
@YuryTarabanko好的。串联(不是加法)总是将2个字符串放在一起。因此,如果您尝试做[] + {},基本上就可以做到[].toString() + ({}).toString()(因为JavaScript在连接它们之前将涉及的数组和对象转换为字符串)。并且,因为[].toString === ''({}).toString() === '[object Object]',您的最终结果为[] + {} === '[object Object]'。这是完全合乎逻辑的。
Joeytje50 2014年

4
@ Joeytje50对。那{} + []呢 :)继续使用相同的逻辑:)
Yury Tarabanko 2014年

3
@YuryTarabanko因为对象和数组既不能串联也不能加在一起,所以将这2个对象按此顺序放在一起会导致数组转换为数字而不是字符串,因为+符号在其前面(例如如何+new Date返回数值)所述的Date对象(UNIX时间戳),或者+true返回的数值true,这是1)。因此,加法变为{} + 0。由于对象没有数值,因此变成+0,JavaScript输出为0
Joeytje50 2014年

4
@ Joeytje50哈哈,好,这不是真的一样的逻辑。如果“对象和数组既不能串联也不能相加”,那么为什么要[] + {}执行串联而{} + []不能呢?另外,您关于“对象没有数值”的说法是错误的:+{}return NaN。而且NaN + 0NaN,不0。就像@Yury所说的那样,从实际情况或常识的角度讨论JavaScript类型强制是毫无意义的。
Ajedi32 2014年

9
仅作记录,JavaScript中一行开头的左括号是一个块,而不是对象文字。所以[] + {}{} + []实际上是两个完全不同的语句
詹姆斯龙

33

+是模棱两可的。它可以表示“连接”“添加”。由于一侧是一个字符串,因此它的意思是“连接”,因此结果为11(顺便说一句,这是我小时候最喜欢的笑话之一。“ 1 + 1 =窗口”直观显示:│┼│ ニ ⊞

-但是只有一个意思:减。因此它减去。

在其他语言(例如PHP)中,“ concatenate”.代替,不会出现这种问题+,因此不会产生歧义。像MySQL这样的其他语言甚至都没有连接运算符,而是使用CONCAT(a,b,c...)


7
避免此问题(以及JavaScript中也出现许多其他问题)的另一种解决方案是不允许隐式转换。例如,当您尝试上述操作时,Python只会抛出错误,从而首先避免了所有这些不直观的问题。动态类型语言中的隐式转换是一个可怕的想法。
Voo

23

因为规范明确指示要这样做。Page75。请注意11.6.1步骤5-8和11.6.2步骤5-7之间的区别。

11.6.1-描述加法运算符的工作方式

1-4。...

5。令lprim为ToPrimitive(lval)。

6。令rprim为ToPrimitive(rval)。

7。如果Type(lprim)是String或Type(rprim)是String,则

7a。返回由ToString(lprim)和ToString(rprim)串联而成的字符串

8。返回对ToNumber(lprim)和ToNumber(rprim)应用加法运算的结果

11.6.2-描述减法运算符的工作方式

1-4。...

5。令lnum为ToNumber(lval)。

6。令rnum为ToNumber(rval)。

7。返回对lnum和rnum应用减法运算的结果

总结 在加法的情况下,如果任何操作数在转换为没有任何提示的原始值时突然变成字符串,则第二个操作数也将转换为字符串。在减法的情况下,两个操作数都将转换为数字。


1
@ Joeytje50例如,继续尝试幻想为什么[] + [] === "":)是由于串联与加法之间的模棱两可吗?大声笑
Yury Tarabanko

3
+1,因为这是唯一的权威性答案。其余所有可能是有用的助记符,但最终答案是“因为规范如此表示”,它之所以如此,是因为Brendan Eich认为在那臭名昭著的10天内是个好主意。
Matteo Italia 2014年

8

+既是数字变量的加法运算符,也是串联运算符,也是字符串。

每当在后面有一个字符串时+,JavaScript都会选择使用+as作为串联运算符,并在该字符串周围尽可能多地转换(键入)术语,以便可以将它们串联起来。那只是Javascript的行为。(如果尝试过console.log(23 + 2 + "." + 1 + 5 + "02" + 02);,则会得到结果25.15022。该数字在连接前已02输入到字符串2中。

-只能是减法运算符,因此当给定字符串时,它将隐式地将字符串的类型更改"1"为数字1;如果它没有做到这一点,那就没有"1" - 1道理了。如果您尝试过console.log(23 + 2 + 1 + 5 - "02" + 03);,将得到32-字符串02将转换为数字2。后面的术语-必须能够转换为数字;如果你尝试过,console.log(23 - 2 - "." - 1 - 5 - 02 - "02");你会得到NaN回报。

更重要的是,如果您尝试过console.log(23 + 2 + "." + 1 + 5 - "02" + 03);,它将输出26.15,之前的所有内容都-被视为字符串(因为它包含string ".",然后在之后的术语-被视为数字)。


8

JavaScript **中没有专用的字符串连接运算符。加法运算符+执行字符串连接或加法,具体取决于操作数的类型:

"1" +  1  // "11"
 1  + "1" // "11"
 1  +  1  // 2

串联没有相反的作用(我认为),并且减法运算符-仅执行减法,而与操作数的类型无关:

"1" -  1  // 0
 1  - "1" // 0
 1  -  1  // 0
"a" -  1  // NaN

** .PHP中的&运算符和VB中的运算符是专用的字符串连接运算符。


3

根据标准EcmaScript262。+-涉及字符串时运算符的行为不同。第一个将每个值转换为字符串。第二个将每个值转换为数字。

从标准:

如果Type(lprim)为String或Type(rprim)为String,则返回将ToString(lprim)与ToString(rprim)串联后得到的字符串

该规则意味着,如果表达式中有一个字符串值,则该+操作中涉及的所有值都将转换为字符串。在JavaScript中,当+运算符与字符串一起使用时,它将它们连接起来。这就是为什么console.log("5"+1)返回“ 51”的原因。1转换为字符串,然后将“ 5” +“ 1”连接在一起。

但是,以上规则不适用于-运算符。使用a时,-所有值都将根据标准转换为数字(请参见下文)。因此,在这种情况下,"5"被转换为5然后1减去。

从标准:

5令lnum为ToNumber(lval)。

6令rnum为ToNumber(rval)。


标准EcmaScript 262中的运算符定义。

运营商+http : //www.ecma-international.org/ecma-262/5.1/#sec-11.6.1 运算符+定义

运营商-http : //www.ecma-international.org/ecma-262/5.1/#sec-11.6.2 运算符-定义


人们阅读并引用规格和手册时喜欢它。谢谢。

0

使用plus和一个字符串,""您基本上会返回一个字符串,因为您正在执行串联:

typeof ("" + 1 + 0)  // string
typeof (1 + 0)  // number

-改为使用时,您可以转换为数字,因为可以进行字符串连接:

typeof ("" - 1 + 0) // number
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.