Search code examples
javascriptlanguage-lawyerecmascript-5

`new Object` vs `Object` in the ECMAScript spec


So, I'm looking at the ES5 specification at the definition of what new Object and Object do. To my surprise:

  • new Object describes a whole algorithm of how the object constructor works - treating what happens with different kinds of values. Basically calls ToObject on non objects - identity on objects and builds on null and undefined.
  • Object has a special first step for null and undefined where it builds an object and then calls ToObject on primitives and identity on objects.

After reading the description a few times - they seem identical. However, clearly from the spec they do something different. For example in Array - calling new Array is specified as the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.`

So - what is the difference between new Object and Object? Why were they specified differently?

For ease - here's a link to the spec.


Solution

  • Object(window) will never clone window but new Object(window) might. All current -- potentially all known -- implementations just return the same reference, although the spec allows for implementation-defined behavior.

    The steps for 15.2.1.1 say:

    1. If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard built-in Object constructor had been called with the same arguments
    2. Return ToObject(value).

    The definition of ToObject (9.9) lists a few types that will be caught by step 1 (in table 14), but for Object has a very simple definition:

    The result is the input argument (no conversion).

    It explicitly states that the input argument will be returned as-is, so they should be equal references (===).

    The definition for new Object (15.2.2.1) has a similar chain of type-checks in step 1, but the step for objects (1.a) is:

    i. If the value is a native ECMAScript object, do not create a new object but simply return value.

    ii. If the value is a host object, then actions are taken and a result is returned in an implementation-dependent manner that may depend on the host object.

    That is, for any host object foo, the call Object(foo) must === foo but new Object(foo) may === foo.

    Host objects are defined in 4.3.8 to be

    object supplied by the host environment to complete the execution environment of ECMAScript.

    This answer lists a few host objects to include window, history, etc. Running those through new Object(foo) should (but doesn't have to) return a different object.

    In any case but passing a host object, new Object(foo) seems to be a more complicated chain that defers to ToObject in much the same way as Object(foo).

    Unfortunately, 15.2.2.1.1.a.ii states that the "result is returned in an implementation-dependent manner" and has no specifics as to the "actions [that] are taken" and it appears that Chrome will return the same object (equal references) for all of the listed "host objects."

    Using this script to check:

    var objects = [
      /* Native objects */
      'Object', 'Date', 'Math', 'parseInt', 'eval',
      /* Host objects */
      'window', 'document', 'location', 'history', 'XMLHttpRequest', 'setTimeout'
    ];
    
    function getDefinedReference(name) {
      if (eval('typeof ' + name) !== 'undefined') {
        return eval(name);
      } else {
        throw new Error('' + name + ' is not defined.');
      }
    }
    
    function checkIdentity(name) {
      try {
        var ref = getDefinedReference(name);
        var no = new Object(ref);
        var o = Object(ref);
    
        console.log(name, ref === no, ref === o, no === o);
    
        if (ref === o && no !== o) {
          // Make sure ref === Object(ref) but not new Object(ref)
          console.log(name, 'returns different references.');
        }
      } catch (e) {
        console.warn(e);
      }
    }
    
    objects.forEach(checkIdentity);
    
    if (typeof window !== 'undefined') {
      for (var f in window) {
        checkIdentity(f);
      }
    }

    doesn't find any objects where Object and new Object behave differently. @Xotic750 seems to be right that it can be implementation-dependent, but nobody is using it.