185

I know a lot of ways to create JS objects but I didn't know the Object.create(null)'s one.

Question:

is it exactly the same as:

var p = {}

vs

var p2 = Object.create(null);

?

Liam
  • 27,717
  • 28
  • 128
  • 190
Royi Namir
  • 144,742
  • 138
  • 468
  • 792

6 Answers6

252

They are not equivalent. {}.constructor.prototype == Object.prototype while Object.create(null) doesn't inherit from anything and thus has no properties at all.

In other words: A javascript object inherits from Object by default, unless you explicitly create it with null as its prototype, like: Object.create(null).

{} would instead be equivalent to Object.create(Object.prototype).


In Chrome Devtool you can see that Object.create(null) has no __proto__ property, while {} does.

enter image description here

Levi Roberts
  • 1,277
  • 3
  • 22
  • 44
Peter Herdenborg
  • 5,736
  • 1
  • 20
  • 21
112

They are definitely not equivalent. I'm writing this answer to more fully explain why it makes a difference.

  1. var p = {};

    Creates an object that inherits the properties and methods from Object.

  2. var p2 = Object.create(null);

    Creates an object that doesn't inherit anything.

If you are using an object as a map, and you create an object using method 1 above, then you have to be extra careful when doing lookups in the map. Because the properties and methods from Object are inherited, your code may run into a case where there are keys in the map that you never inserted. For example, if you did a lookup on toString, you would find a function, even though you never put that value there. You can work around that like this:

if (Object.prototype.hasOwnProperty.call(p, 'toString')) {
    // we actually inserted a 'toString' key into p
}

Note that it is fine to assign something to p.toString, it will simply override the inherited toString function on p.

Note that you can't just do p.hasOwnProperty('toString') because you may have inserted a key "hasOwnProperty" into p, so we force it to use the implementation in Object.

On the other hand, if you use method 2 above, then you won't have to worry about things from Object appearing in the map.

You can't check for the existence of a property with a simple if like this:

// Unreliable:
if (p[someKey]) {
    // ...
}

The value might be an empty string, might be false, or null, or undefined, or 0, or NaN, etc. To check whether a property exists at all, you would still need to use Object.prototype.hasOwnProperty.call(p, someKey).

Community
  • 1
  • 1
doug65536
  • 6,562
  • 3
  • 43
  • 53
  • 7
    A simpler alternative for checking for the existence of a property is: `if (someKey in p) {` – mrcrowl Nov 04 '16 at 07:51
  • 3
    @mrcrowl Only if they used `Object.create(null)`. I prefer not to make assumptions like that, even if you were absolutely right that it used `Object.create(null)`, the code could change, the object could be replaced with one that inherits `Object` at some point. `hasOwnProperty` always works. – doug65536 Nov 04 '16 at 08:13
  • 2
    I feel being careful about something like this should be unnecessary. I appreciate your answer but documentation should give you the api necessary to work with whatever code you're working with. If you're grabbing some random code from github, then you can fork it and be safe from less documented updates. Not to mention `{}` is so much more prevalent than `Object.create(null)`, that if your code accidentally grabs an inherited property at this point, you likely have way bigger bugs to worry about. I can only see people using Object.create(null) as a minor optimization. – aaaaaa Sep 13 '17 at 21:16
  • Double negation `!!p[key]` works good with `Object.create(null)`. But `hasKey = (key, input) => Object.prototype.hasOwnProperty.call(input, key)` is not bad either – andreid May 02 '18 at 10:03
  • > Note that you can't just do p.hasOwnProperty('toString') because you may have inserted a key "hasOwnProperty" into p, so we force it to use the implementation in Object. That's is unnecessary. In this case, you can't use any methods in `p` because every method could be inserted and so become not safe. – xianshenglu Dec 06 '18 at 02:13
  • @xianshenglu That is not correct. Using `Object.prototype.hasOwnProperty.call `will work regardless of what keys are in the object. – doug65536 Dec 06 '18 at 03:58
  • @doug65536, I mean if you are worried about `p.hasOwnProperty` was overwritten, you can't use `p.getOwnPropertyNames`, and also you can't use `p.otherMethods` because they can be overwritten either. – xianshenglu Dec 06 '18 at 07:42
1

Creating objects by using {} will create an object whose prototype is Object.prototype which inherits the basic functions from Object prototype while creating objects by using Object.create(null) will create an empty object whose prototype is null.

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Praveen Kishor
  • 2,413
  • 1
  • 23
  • 28
0

If someone is looking for implementing Object.create(null), just to know how it works. It is written using __proto__ which is non-standard and hence, I do not recommend it.

function objectCreateMimic()
{
  /*optional parameters: prototype_object, own_properties*/
  var P = arguments.length>0?arguments[0]:-1;
  var Q = arguments.length>1?arguments[1]:null;
  var o = {};
  if(P!==null && typeof P === "object")
  {
    o.__proto__ = P;
  }
  else if(P===null)
  {
    o.__proto__ = null;
  }
  if(Q!==null && typeof Q === "object")
  {
   for(var key in Q)
   {
     o[key] = Q[key];
   }
  }
  return o;
}

Note: I wrote this, out of curiosity, and it is only written in simple terms, for instance, I am not transferring the property descriptors from the second object to the return object.

Yves M.
  • 29,855
  • 23
  • 108
  • 144
Aravind
  • 3,169
  • 3
  • 23
  • 37
  • 1
    Note that as of ECMAScript's release in summer, `__proto__` will now [officially](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-__proto__-property-names-in-object-initializers) be part of the language. – Chiru Mar 20 '15 at 00:55
  • 1
    Why `-1` in `arguments.length>0?arguments[0]:-1;`? – happy_marmoset Dec 03 '15 at 09:39
  • @happy_marmoset late response, but it looks like it's just a non-null placeholder so that the `Object` prototype is retained if the first argument isn't given. Variable names could be a lot better here. – Mike Hill Jul 31 '17 at 00:11
  • Also, the second parameter should describe the property *descriptors* rather than the actual properties themselves. See reference here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create – Mike Hill Jul 31 '17 at 00:16
0

When you create an Object with Object.create(null) that means you are creating an Object with no prototype.null here means end of prototype chain. Nevertheless when you create an object like {} Object prototype will be added. Hence these are two different objects, one with prototype another without prototype.Hope this helps

0

Somewhat old question and seems like all valid answers, but what @Royi Namir may have been also getting at, and what might be good to understand here is SHOULD I use it? Rather than just ask the question, I'll take a gander.

  • {} is simple, and straightforward and well-known, ubiquitous code.
  • Object.create(null) is less well-known, and less often used, as we can surmise from the sheer fact of the question being asked.

Why would you use Object.create(null) over {}? The other answers here answer that question by pointing out the difference between the two approaches, which is simply that Object.create(null) does not inherit Object.prototype prototype while {} does, and that may be of value in your particular circumstance.

Aside from that, the only reason you might want to use one over the other would be performance, memory use, or ease of use.

If we run some simple tests to measure the time it takes to create objects both ways, we can see that there are indeed both performance and memory differences. Note: for the sake of simplifying my comment, I'm only showing the tests that include assigning attributes but I've done the tests both with and without attribute assignments and the metrics are similar.

console.time("create curly");
let objects = [];
for (let i = 0; i < 100000; i++) {
    objects.push({ attr1: i, attr2: i*2, attr3: i*5});
}
console.log(objects.length);
console.timeEnd("create curly");
console.log("Memory", process.memoryUsage().heapUsed);

Output (several runs, best time, not precise):

create curly: 24.152ms
Memory 11280760
console.time("create object");
let objects = [];
for (let i = 0; i < 100000; i++) {
    const obj = Object.create(null)
    obj.attr1 = i;
    obj.attr2 = i*2;
    obj.attr3 = i*5;
    objects.push(obj);
}
console.log(objects.length);
console.timeEnd("create object");
console.log("Memory", process.memoryUsage().heapUsed);

Output (several runs, best time, not precise):

create object: 41.106ms
Memory 23766400

Performance While the time output is not a fully scientific, precise metric, I think it's pretty safe to say that Object.create(null) is SLOWER than {}. Depending on the number of objects you need to create, however, it may be a relatively insignificant difference.

Memory From the tests results shown, creating objects with Object.create(null) and assigning a few attributes to the objects takes about twice as much memory as creating the objects with {} with attributes inline. About 10MB more for 100,000 objects. That's significant!

Ease of Use It is readily apparent that {} with inline attributes is significantly easier to use and allows for attribute assignment that is streamlined.

Bottom line, use {} for better performance, memory use, and to ease finger stress, unless you have a real reason to create an object that does not inherit the Object.prototype prototype.

meem
  • 211
  • 3
  • 5