Why does String(null) work?

javascript, null, string, tostring

Solution

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.

Problem

`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". ... | ... */ ```

Original source