31

null and undefined don't have a toString or valueOf method. Afaik using String calls the toString method of its parameter (e.g. String({}) => [object Object]).

Why do String(null) or String(undefined work then? It doesn't implicitly do Object.prototype.toString.call(null). because that evaluates to [object Null].

[edit]: from the spec ECMA-262/5th edition (page 48). This doesn't add to clarification, I'd say:

/*
Table 13 — ToString Conversions  
-------------------------------------------------------------------------
Argument Type  | Result  
-------------------------------------------------------------------------
Undefined      | "undefined"
Null           | "null"  
Boolean        | If the argument is true, then the result is "true".
...            | ...
*/
0

4 Answers 4

41

After reviewing my previous answer, it seems a complete overhaul of my previous answer is necessary. I was way over complicating it, as the short answer is that these are standards-specified special cases.

The specification for String() (String used as a function):

15.5.1.1 String ( [ value ] )

Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty String "" is returned.

The ToString function (that exists internally, not in userland) is defined as follows (9.8):

"The abstract operation ToString converts its argument to a value of type String according to Table 13"

Argument Type | Result
Null | "null"
Undefined | "undefined"

This means that String(null) and String(undefined) go into this special table of types and just return the string values valued "null" and "undefined".

A user-land pseudo-implementation looks something like this:

function MyString(val) {
    if (arguments.length === 0) {
        return "";
    } else if (typeof val === "undefined") {
        return "undefined";
    } else if (val === null) {
        return "null";
    } else if (typeof val === "boolean") {
        return val ? "true" : "false";
    } else if (typeof val === "number") {
        // super complex rules
    } else if (typeof val === "string") {
        return val;
    } else {
        // return MyString(ToPrimitive(val, prefer string))
    }
}

(Note that this example ignores the constructor case (new MyString()) and that it uses user-land concepts rather than engine-land.)


I got a bit carried away and found an example implementation (V8 to be specific):

string.js:

// Set the String function and constructor.
%SetCode($String, function(x) {
  var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
  if (%_IsConstructCall()) {
    %_SetValueOf(this, value);
  } else {
    return value;
  }
});

macros.py:

macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));

runtime.js:

function NonStringToString(x) {
  if (IS_NUMBER(x)) return %_NumberToString(x);
  if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
  if (IS_UNDEFINED(x)) return 'undefined';
  return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
}

The NonStringToString (which is essentially what is of interest), is luckily defined in psuedo-JS-land. As you can see, there is indeed a special case for null/true/false/undefined.

Sign up to request clarification or add additional context in comments.

4 Comments

String() !== String(undefined) is because in JavaScript two objects are equal only if they are the same object in memory. When calling String() you are creating a wrapper object around the string primitive so essentially the comparison is of two different objects wrapping the same string primitive.
@Buzzy Yes, for objects that is correct, and thus for things like new String(x) === new String(y), that would make sense. String(x) is specified to be equivalent to a a string value though rather than a string object. According to the standard with regards to the === operator (11.9.6): "true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false."
@Buzzy I'm glad you commented. After rereading my old answer, I realized it was way overcomplicating what is a very clear cut functionality in the specification! :)
makes this work, which is weird: x={null:1}; x["null"];//1 x[null]; //1
2

There is probably just some extra checks and handling for special cases like null and undefined.

MDN says:

It's possible to use String as a "safer" toString alternative, as although it still normally calls the underlying toString, it also works for null and undefined.

Comments

1

String(null) creates a string object and passes it a default value of null.

Comments

1

You might be interested in seeing the Annotated ES5 (which is much more readable than the ECMAScript 5 PDF) which states that: new String([ value ]) http://es5.github.com/#x15.5.2.1 calls [ToString] http://es5.github.com/#x9.8 (there is a table of the special convertion cases) to convert the value passed to it to a string.

1 Comment

It's the exact same table as I linked to, except for colored green in spots. (And a direct link)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.