87

I don't understand why JavaScript works this way.

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

The first line prints 11, and the second prints 0. Why does JavaScript handle the first as a String and the second as a number?

Community
  • 1
  • 1
Nirgn
  • 1,879
  • 4
  • 23
  • 28
  • 12
    +1 - although the answer *why* is obvious for anybody accustomed with JS, the reason *why the obvious answer is true* is still beyond my comprehension - and I suppose I'm not the only one... JS fails POLA in many ways *sigh* http://stackoverflow.com/questions/9032856/what-is-the-explanation-for-these-bizarre-javascript-behaviours-mentioned-in-the?rq=1 –  Jun 24 '14 at 15:02
  • 5
    I feel like this link should be posted along with any javascript typing weirdness: https://www.destroyallsoftware.com/talks/wat – DLeh Jun 24 '14 at 17:38
  • @DLeh: I was just about to post a link to that video :D – gen_Eric Jun 24 '14 at 18:33
  • 2
    Also related: [Your Language Sucks](http://wiki.theory.org/YourLanguageSucks) – Simon Forsberg Jun 24 '14 at 20:14

7 Answers7

121

String concatenation is done with + so Javascript will convert the first numeric 1 to a string and concatenate "1" and "1" making "11".

You cannot perform subtraction on strings, so Javascript converts the second "1" to a number and subtracts 1 from 1, resulting in zero.

Bernhard Hofmann
  • 10,321
  • 12
  • 59
  • 78
  • 2
    Now try to apply this logic to `[] + {}` and `{} + []` :) – Yury Tarabanko Jun 24 '14 at 10:25
  • 8
    @YuryTarabanko Okay. Concatenation (so not addition) *always* puts together 2 strings. So, if you try to do `[] + {}`, you basically do `[].toString() + ({}).toString()` (because JavaScript converts the involved array and object to a string before concatenating them). And, because `[].toString === ''` and `({}).toString() === '[object Object]'`, your final result for `[] + {} === '[object Object]'`. It's perfectly logical. – Joeytje50 Jun 24 '14 at 10:58
  • 4
    @Joeytje50 Right. What about `{} + []`? :) Go ahead apply the same logic :) – Yury Tarabanko Jun 24 '14 at 11:01
  • 3
    @YuryTarabanko Because objects and arrays can neither be concatenated nor added up, putting these 2 together in this order causes the array to be converted to a number instead of a string, because the `+` sign is in front of it (like how `+new Date` returns the numerical value of the `Date` object (the UNIX timestamp), or `+true` returns the numerical value of `true`, which is `1`). Because of that, the addition becomes `{} + 0`. Because the object doesn't have a numerical value, this becomes `+0`, which JavaScript outputs as `0`. – Joeytje50 Jun 24 '14 at 11:15
  • 1
    @YuryTarabanko either way, it's not important if a certain bit of logic can be applied *everywhere*, as long as it answers the original question, it is already enough. Nobody will *ever* use `+` for adding together objects or arrays, so this is not even a problem in practical situations, whereas adding together / subtracting a string and a number *is*. – Joeytje50 Jun 24 '14 at 11:17
  • @Joeytje50 OK. No point to continue the conversation when it comes to "practical situations" and common sense :) – Yury Tarabanko Jun 24 '14 at 11:33
  • 4
    @Joeytje50 Haha, well that's not really the *same* logic. If "objects and arrays can neither be concatenated nor added up", then why would `[] + {}` perform concatenation whereas `{} + []` doesn't? Plus, your statement that "object doesn't have a numerical value" is false: `+{}` returns `NaN`. And `NaN + 0` is `NaN`, not `0`. Like @Yury said though, it's pointless to discuss JavaScript type coercion from the standpoint of practical situations or common sense. – Ajedi32 Jun 24 '14 at 14:11
  • 11
    Just for the record, an opening brace at the beginning of a line in JavaScript is a block, not an object literal; so `[] + {}` and `{} + []` are actually two completely different statements – James Long Jun 24 '14 at 14:21
  • 1
    @Ajedi32 Force them all to numbers first and you'll get what you expect: `+{} + +[] === NaN` - confusion typically comes from Javascript's "try this first and if it works, yay! If not, try something else". It just so happens that strings are tried first – Izkata Jun 24 '14 at 14:44
  • 2
    @Izkata Exactly! Don't rely on JavaScript's built-in type coercion. If you want to add the contents of two variables together, first make sure that they're the same type. Because if you don't, JavaScript will. – Ajedi32 Jun 24 '14 at 14:47
  • 2
    @YuryTarabanko: Of course you can apply the very same logic to `({} + [])` (with parenthesis to remove the ambiguity). If you mean `{}; +[]`, then that's obviously a unary plus operator. – Bergi Jun 25 '14 at 03:01
34

+ is ambiguous. It can mean "concatenate" or "add". Since one side is a string, it is taken to mean "concatenate", hence the result is 11 (which, by the way, was one of my favourite jokes as a young child. That and "1 + 1 = window", as shown visually: │┼│ ニ ⊞)

- however has only one meaning: subtract. So it subtracts.

This kind of problem is not present in other languages such as PHP, where "concatenate" is . instead of +, making no ambiguity. Still other languages like MySQL don't even have a concatenation operator, instead using CONCAT(a,b,c...).

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 9
    Another solution to avoid this problem (and many other problems that also arise in JavaScript) is to not allow implicit conversions. Python for example will just throw an error when you try something like the above which avoids all these unintuitive problems in the first place. Implicit conversions in a dynamically typed language is a horrible idea. – Voo Jun 24 '14 at 11:36
26

Because the spec explicitly tells to do so. Page 75. Note the difference between 11.6.1 steps 5-8 and 11.6.2 steps 5-7.

11.6.1 - describes how addition operator works

1-4. ...

5. Let lprim be ToPrimitive(lval).

6. Let rprim be ToPrimitive(rval).

7. If Type(lprim) is String or Type(rprim) is String, then

7a. Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)

8. Return the result of applying the addition operation to ToNumber(lprim) and ToNumber(rprim)

11.6.2 - describes how subtraction operator works

1-4. ...

5. Let lnum be ToNumber(lval).

6. Let rnum be ToNumber(rval).

7. Return the result of applying the subtraction operation to lnum and rnum

Summary In case of addition if any of the operands when converted to primitive value without any hints suddenly becomes a string the second one is converted to a string too. In case of subtraction both operands are converted to a number.

Bort
  • 7,398
  • 3
  • 33
  • 48
Yury Tarabanko
  • 44,270
  • 9
  • 84
  • 98
  • 1
    @Joeytje50 For example, go ahead and try to fantasize why `[] + [] === ""` :) Is it due to ambiguity about concatenation vs. addition? LOL – Yury Tarabanko Jun 24 '14 at 10:20
  • 4
    +1 because this is the only authoritative answer. All the rest may be useful mnemonics, but the ultimate answer is "because the spec says so", and it says so because Brendan Eich thought it was a good idea in those infamous 10 days. – Matteo Italia Jun 24 '14 at 21:00
12

There is no dedicated string concatenation operator in JavaScript**. The addition operator + performs either string concatenation or addition, depending on the type of operands:

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

There is no opposite of concatenation (I think) and the subtraction operator - only performs subtraction regardless of the type of operands:

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

** The . operator in PHP and & operator in VB are dedicated string concatenation operators.

Salman A
  • 262,204
  • 82
  • 430
  • 521
8

+ is both an addition operator for numeric variables, and a concatenation operator for strings.

Whenever there's a string after a +, Javascript will choose to use the + as a concatenation operator and convert (typed) as many terms as possible around the string so it can concatenate them. That's just the behaviour of Javascript. (If you tried console.log(23 + 2 + "." + 1 + 5 + "02" + 02);, you'll get the result 25.15022. The number 02 was typed into the string 2 before being concatenated.

- can only be a subtraction operator, so when given a string, it will implicitly change the type of the string "1" into a numeric 1; if it didn't do that, there's no way "1" - 1 would make sense. If you tried console.log(23 + 2 + 1 + 5 - "02" + 03); you'll get 32 - the string 02 gets converted into the number 2. The term after the - must be able to be converted into a number; if you tried console.log(23 - 2 - "." - 1 - 5 - 02 - "02"); you'll get NaN returned.

More importantly, if you tried console.log(23 + 2 + "." + 1 + 5 - "02" + 03);, it will output 26.15, where everything before - was treated as a string (because it contains a string ".", and then the term after the - is treated as a number.

dayuloli
  • 16,205
  • 16
  • 71
  • 126
  • In your last example it seems instead that JS is considering the elements going from left to right: it seems that it adds numbers `23 + 2 = 25` then it finds the string `"."` and converts type number `25` into type string `"25"` and concatenate the 1 and the 5 as well to have type string `"25.15"`. At this point it finds the `-` so it coerces the strings before and after into numbers and since both can be converted into numbers, than the subtractions is performed to get the number `23.15`. Finally it is adding `3` for the final result of type number `26.15`. – Ferie Jan 11 '23 at 15:41
5

According to the standard EcmaScript 262. The + and - operators behave differently when strings are involved. The first converts every value to a string. The second converts every value to a number.

From the standard:

If Type(lprim) is String or Type(rprim) is String, then Return the String that is the result of concatenating ToString(lprim) followed by ToString(rprim)

This rules implies that if in the expression there is a string value, all values involved in the + operation are converted to a string. In JavaScript when the + operator is used with strings, it concatenates them. This is why console.log("5"+1) returns "51". 1 is converted to a string and then, "5" + "1" are concatenated together.

Nevertheless, the above rule doesn't apply for the - operator. When you are using a - all values are converted to numbers according to the Standard (see below). Therefore, in this case, "5" is converted to 5 and then 1 is subtracted.

From the standard:

5 Let lnum be ToNumber(lval).

6 Let rnum be ToNumber(rval).


Operator definition from the standard EcmaScript 262.

Operator + : http://www.ecma-international.org/ecma-262/5.1/#sec-11.6.1 Operator + definition

Operator - : http://www.ecma-international.org/ecma-262/5.1/#sec-11.6.2 Operator - definition

Giuseppe Pes
  • 7,772
  • 3
  • 52
  • 90
  • Like it when people read and quote specifications and manuals. Thanks. –  Apr 21 '16 at 07:00
0

Using plus and a string "" you basically return a string because you are performing a concatenation:

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

When using - instead you convert to a number as string concatenation is possible:

typeof ("" - 1 + 0) // number
GibboK
  • 71,848
  • 143
  • 435
  • 658