Search code examples
javascriptecmascript-3

Was there a way to create an object without a prototype prior to ES5?


Was there a way to create an object without a prototype prior to ES5?

i.e. something like Object.create(null) (ES5)

I thought something like this might work, but the final statement unexpectedly returns true:

function withPrototype(p) {
  function temp(){}
  temp.prototype = p;
  return new temp();
}

Object.getPrototypeOf(withPrototype(null)) === Object.prototype; // true

Object.getPrototypeOf is ES5. I use it here for exposition.


Solution

  • As @Oriol has shown, there was no "official" (spec-compliant) way to do this.

    However, there was indeed an object that had no prototype - Object.prototype itself.

    15.2.4 Properties of the Object Prototype Object

    The value of the internal [[Prototype]] property of the Object prototype object is null and the value of the internal [[Class]] property is "Object".

    You could potentially "create" such an object by instantiating a new environment ("realm" in ES6 terms), e.g. via an <iframe>, capturing its Object.prototype, stripping it of its properties and voilá - you've got a new empty object.

    function getNoProtoObject(callback) {
      var iframe = document.createElement('iframe');
      iframe.onload = function() {
        var obj = iframe.contentWindow.Object.prototype;
        document.body.removeChild(iframe);
    
        // Remove all built-in enumerable properties.
        for (var name in obj) {
          delete obj[name];
        }
    
        // Remove known built-in non-enumerable properties, which may vary.
        delete obj['constructor'];
        delete obj['hasOwnProperty'];
        delete obj['isPrototypeOf'];
        delete obj['propertyIsEnumerable'];
        delete obj['toLocaleString'];
        delete obj['toString'];
        delete obj['toSource'];
        delete obj['valueOf'];
        delete obj['watch'];
        delete obj['unwatch'];
        delete obj['__defineGetter__'];
        delete obj['__defineSetter__'];
        delete obj['__lookupGetter__'];
        delete obj['__lookupSetter__'];
        delete obj['__proto__'];
    
        callback(obj);
      };
      iframe.src = 'about:blank';
      iframe.style.display = 'none';
      document.body.appendChild(iframe);
    }
    
    getNoProtoObject(function(o) {
      console.log(o);                        // Object {  }
      console.log(Object.getPrototypeOf(o)); // null
      console.log(o.__proto__);              // undefined
    });