What's the difference between String(value) vs value.toString()

Javascript has lot's of "tricks" around types and type conversions so I'm wondering if these 2 methods are the same or if there is some corner case that makes them different?


Solution 1:

They are not completely the same, and actually, the String constructor called as a function (your first example), will at the end, call the toString method of the object passed, for example:

var o = { toString: function () { return "foo"; } };
String(o); // "foo"

On the other hand, if an identifier refers to null or undefined, you can't use the toString method, it will give you a TypeError exception:

var value = null;
String(null);     // "null"
value.toString(); // TypeError

The String constructor called as a function would be roughly equivalent to:

value + '';

The type conversion rules from Object-to-Primitive are detailed described on the specification, the [[DefaultValue]] internal operation.

Briefly summarized, when converting from Object-to-String, the following steps are taken:

  1. If available, execute the toString method.
    • If the result is a primitive, return result, else go to Step 2.
  2. If available, execute the valueOf method.
    • If the result is a primitive, return result, else go to Step 3.
  3. Throw TypeError.

Given the above rules, we can make an example of the semantics involved:

var o = {
  toString: function () { return "foo"; },
  valueOf:  function () { return "bar"; }
};

String(o); // "foo"

// Make the toString method unavailable:
o.toString = null;

String(o); // "bar"

// Also make the valueOf method unavailable:
o.valueOf = null;

try { 
  String(o); 
} catch (e) {
  alert(e); // TypeError
}

If you want to know more about this mechanism I would recommend looking at the ToPrimitive and the ToString internal operations.

I also recommend reading this article:

  • Object-to-Primitive Conversions in JavaScript

Solution 2:

value.toString() will cause an error if value is null or undefined. String(value) should not.

For example:

var value = null;
alert(value.toString());

will fail because value == null.

var value = null;
alert(String(value));

should display a message reading "null" (or similar), but it will not crash.

Solution 3:

String(value) should have the same result as value.toString() in every case, except for values without properties like null or undefined. ''+value will produce the same result.

Solution 4:

String() [the constructor call] is basically calling the .toString()

.toString() and String() can be called on primitive values(number,boolean,string) and basically will do nothing special:

true => 'true'

false => 'false'

17 => '17'

'hello' => 'hello'

But calling these functions on objects is where things gets interesting:

if the object has it's own .toString() function it will be called when ever you need this object to be treated as a string(explicitly/implicitly)

let obj = {
           myName:"some object",
           toString:function(){ return this.myName; } 
          }

//implicitly treating this obj as a string
"hello " + obj; //"hello some object"

//OR (explicitly)
"hello " + String(obj) //calling the existent toString function

//OR
"hello " + obj.toString(); //calling toString directly

By the way if you want to treat this object as a number it should has a .valueOf() function defined in it.

what if we have both in one object?

if we want to treat this object as a string => use .toString()

if we want to treat this object as a number => use .valueOf()

what if we only have .valueOf() defined?

.valueOf() defined inside the object will be called whether we want to handle the object as a string or as a number