26

In JavaScript, is there any circumstance where there is a semantic difference between these two options?

foo.bar + ''

...and...

'' + foo.bar

I would've expected the latter to more reliably coerce the result to a string, but I can't find any discussion of this (after much Googling) nor any example where it seems to matter.

VoteyDisciple
  • 37,319
  • 5
  • 97
  • 97
  • Related: https://stackoverflow.com/questions/31845895/whats-the-best-way-to-do-string-building-concatenation-in-javascript – Callat Jul 27 '18 at 19:38
  • Isn't the most readable way to do string coercion `String(foo.bar)`? – Neil Jul 27 '18 at 20:38
  • 1
    @Neil Yes, it is, there is however a semantic difference in that the global `String` variable could be overwritten. – Bergi Jul 28 '18 at 13:26

5 Answers5

16

Both are the same.

There is only a difference if there are other + (on the left or the right). In other words:

1 + 1 + ''                          // results in '2'

Is not the same as:

'' + 1 + 1                          // results in '11'
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73
  • 2
    To be fair, this does not match the OP's `foo.bar + ''` versus `'' + foo.bar` comparison. – trincot Jul 27 '18 at 19:54
  • 6
    @trincot That is already answered by _"Both are the same"_. I just wanted to include this related example that could produce a difference. After all, we don't know how OP is using this exactly. – ibrahim mahrir Jul 27 '18 at 19:56
  • @trincot ... It might as well be `something + foo.bar + ''` or `'' + foo.bar + something` – ibrahim mahrir Jul 27 '18 at 19:58
  • Yes, I agree. Precedence rules (left to right) will make the left side not `foo.bar` at the moment of the addition the OP is looking at. But I can see that it would be interesting if the OP is not actually looking for that precise case only. – trincot Jul 27 '18 at 20:01
13

They are the same as others have already mentioned, with additional considerations listed by @Zohaib Ijaz:

var values = [
  undefined,
  null,
  false,
  true,
  0,
  -1,
  1,
  NaN,
  Infinity,
  '',
  '123',
  'abc123',
  '123abc',
  [],
  [0],
  [1],
  {},
  {a: 1},
  function(){}   
];
var foo = {bar: null};

values.forEach(function(value) {
  foo.bar = value;
  
  console.log("foo.bar + '':", foo.bar + '');
  console.log("'' + foo.bar:", '' + foo.bar);
});

However there are significant differences, when you have more than 2 operands as mentioned by @ibrahim mahrir:

var values = [
  undefined,
  null,
  false,
  true,
  0,
  -1,
  1,
  NaN,
  Infinity,
  '',
  '123',
  'abc123',
  '123abc',
  [],
  [0],
  [1],
  {},
  {a: 1},
  function(){}   
];
var foo = {bar: null};

values.forEach(function(value) {
  foo.bar = value;
  
  console.log("foo.bar + '' + foo.bar:", foo.bar + '' + foo.bar);
  console.log("'' + foo.bar + '':", '' + foo.bar + '');
});
Rick
  • 4,030
  • 9
  • 24
  • 35
  • 9
    Well of course there's a difference between `foo.bar + '' + foo.bar` and `'' + foo.bar + ''`. Expecting those to be the same would be like expecting `1 + 2 + 1` and `2 + 1 + 2` to be the same. ibrahim's example is a bit subtler. – user2357112 Jul 28 '18 at 05:13
5

If one of the operands is a string, valueOf() or toString() will be called on the other one, so yes they are exactly the same.

12.8.3 of the spec:

 AdditiveExpression + MultiplicativeExpression
  1. Let lref be the result of evaluating AdditiveExpression.

  2. Let lval be ? GetValue(lref).

  3. Let rref be the result of evaluating MultiplicativeExpression.

  4. Let rval be ? GetValue(rref).

  5. Let lprim be ? ToPrimitive(lval).

  6. Let rprim be ? ToPrimitive(rval).

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

    a. Let lstr be ? ToString(lprim).

    b. Let rstr be ? ToString(rprim).

    c. Return the String that is the result of concatenating lstr and rstr

Community
  • 1
  • 1
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • This is not entirely true: before `toString` is called on the non-string argument, the `ToPrimitive` step is executed which may involve a call to `valueOf`, and if that result is a primitive, `toString` is never called (not to be confused with `ToString` in the reference which is about converting one primitive type to another, which does not involve a call to `toString`). – trincot Jul 28 '18 at 07:16
  • @trincot yup, clarified that – Jonas Wilms Jul 28 '18 at 08:15
3

Here are few rules taken from here

  • If at least one operand is an object, it is converted to a primitive value (string, number or boolean);
  • After conversion, if at least one operand is string type, the second operand is converted to string and the concatenation is executed;
  • In other case both operands are converted to numbers and arithmetic addition is executed.
Zohaib Ijaz
  • 21,926
  • 7
  • 38
  • 60
3

No, there is no difference.

If foo.bar is not a primitive value then first valueOf is called on it. If that still does not return a primitive value, toString is called on it. If that still does not return a primitive value, an error is triggered.

In all this, the foo.bar object is not aware in which context valueOf or toString are called. There is no way the object can differentiate between the two cases you mention. It does not even have to be one of those two.

Finally, there is no way to set a trap on the + operator, so you could know its operands.

Here is a demo of how valueOf and toString are called in that order:

const foo = { 
    bar: {
        valueOf() {
            console.log('valueOf');
            return this; // not a primitive value, so now toString will be called.
        },
        toString() {
            console.log('toString');
            return "1";
        }
    }
}

console.log(foo.bar + '');
console.log("====");
console.log('' + foo.bar);
trincot
  • 317,000
  • 35
  • 244
  • 286