37

I prefer to use Object.create(null), this way I can use the object as a simple dictionary to store basic information. If I need object functionality, then I'd create a normal object, e.g. var obj = {x:3, etc};

This is because {} isn't truly a blank object as it links to the Object prototype so in loops you have to do hasOwnProperty to stop it dancing around the prototype chain and doing this check on every loop hinders performance, albeit it's negligible.

Is it bad practice to use Object.create(null) instead of {}, so I don't have to write hasOwnProperty in every loop?

Edit: Possible duplicate, but I'm not asking about how or why they're different, instead I'm interested to know if others use Object.create(null)? Is is SOP in Javascript land?

Edit 2: I also prefer to do one-line checks, e.g. if(obj[someKey])... ht 'Tribute to APJ Kalam Sir'.

Community
  • 1
  • 1
Data
  • 1,337
  • 11
  • 17
  • 1
    possible duplicate of [Creating Js object with Object.create(null)?](http://stackoverflow.com/questions/15518328/creating-js-object-with-object-createnull) – Julius Musseau Aug 28 '15 at 03:24
  • 1
    @Julius Davies - I read that before posting; my question is not about the difference but enquiring about the hive-mind consent on which is the better approach. – Data Aug 28 '15 at 03:26
  • Why do you have to call `.hasOwnProperty()`? Are you using `for..in` or something? – gilly3 Aug 28 '15 at 03:27
  • Hive mind seems conflicted on this one. I say go for it! – Julius Musseau Aug 28 '15 at 03:30
  • @gilly3 - I add data to my `Object.create(null)` from a database result for example, then another function would process the results by looping over the object. – Data Aug 28 '15 at 03:30
  • You haven't defined any criteria for "better" other than the own property issue. None of the standard properties on *Object.protoype* are enumerable, so that's a Furphy. What other reason do you have? Of course there may be additional, enumerable properties on *Object.prototype* but there are other ways to avoid that than using *hasOwnProperty*. – RobG Aug 28 '15 at 03:32
  • @RobG - Perhaps I'm being too pedantic worrying that `hasOwnProperty` takes an extra CPU cycle every time you use a loop. I'd rather save the digital gremlins from doing any more work than they have to, hence I use the null method. I take on board your comment and will do some research about enumerable props, thanks. – Data Aug 28 '15 at 03:36
  • 1
    I understand your issue with *for..in*, however there is also `Object.keys(...).forEach(...)` and other Array methods that can be applied directly to an array of own properties. Going the other way, there's `if (o.hasOwnProperty(s)` rather than `if (s in o)`. I guess it boils down to seeing a specific case, as treating an object with `[[Prototype]]` of `null` as a normal object might lead to strange outcomes (e.g. no *valueOf*, *toString* or *constructor* properties). `var o = Object.create(null);console.log(o+'')` throws an error. – RobG Aug 28 '15 at 03:44
  • @RobG - I don't treat it as normal object, it's sole purpose is as a repository to hold/augment basic info. Your arguments are good though and made me consider my approach. – Data Aug 28 '15 at 03:48
  • @RobG I never like using `o.hasOwnProperty`, what if `o.hasOwnProperty('hasOwnProperty')`? I always end up referencing through `Object.prototype` for the method – Paul S. Aug 28 '15 at 03:57
  • @PaulS.—seems to me if you're using this in an environment that you have complete control over, then there's no issue as you know what properties have and haven't been added. Otherwise, there's always `Object.prototype.hasOwnProperty.call(obj, prop)`. ;-) I see the OPs point where the object will only ever be used like `if (prop in obj)`, in that case it makes sense. – RobG Aug 28 '15 at 04:20
  • Please consider using a `Map` for this – Benjamin Gruenbaum Aug 28 '15 at 10:50
  • JavaScript evolves, interestingly the new coming array method `groupBy` returns a new object without prototype (Object.create(null) thus). groupByToMap also exists. More than ever the dev has to know those details. – Michel Lamsoul Mar 15 '22 at 00:41

7 Answers7

37

By not inheriting from Object.prototype with Object.create(null), your object no longer has the methods on the object prototype. So the obvious question is, what are the methods on that prototype, and do you need them?

The methods are:

  • hasOwnProperty
  • isPrototypeOf
  • propertyIsEnumerable
  • toString/toLocaleString
  • valueOf

hasOwnProperty seems kind of useless because if there is no prototype, all properties on the created object are by definition own properties. However, it's conceivable that some library or other piece of code might be iterating over the object's properties with for...in and using the if (o.hasOwnProperty(key)) idiom. It is probably too much to hope for that it would use the more correct Object.prototype.hasOwnProperty.call(o, key). Such code would now fail. Or, you might be creating a sub-object with the created object as prototype, and be interested in knowing whether a property is an own property of the sub-object or comes from the prototype.

isPrototypeOf might or might not be useful if you plan to use the created object as the prototype for other objects, and for some reason want to check if the created object is or is not in the prototype chain of such other objects.

propertyIsEnumerable would not be used too much, but then again, we cannot predict what other code in the system is going to do. If it tries to call this method on the created object, or any objects created with it as a prototype, it will fail.

The same holds for toString. Who knows who might try to call it on your created object? For instance, some libraries might try to test a value's type by doing a toString and seeing if the result is "[object Object]". Hopefully they do a safe Object.prototype.toString.call(o), but...

valueOf is rarely used explicitly, but is called by the engine if it needs to coerce the object to a primitive. If it is missing, the code could break:

> o2 = Object.create(null)
> o2 + 1
Uncaught TypeError: Cannot convert object to primitive value

Of course, there is also the possibility that someone has added methods to the Object prototype, in which case Object.create({}) will pick them up. Probably you don't want or need them. On the other hand, they are most likely harmless.

Given all of this, it seems that Object.create(null) should be limited to specific cases where it is provable that none of the above issues could occur. For instance, where the object is never even passed outside its local context, or is never going to be used as a prototype for another object. Even in that case, the performance advantage will be very small, or even zero.

I'm interested to know if others use Object.create(null)? Is it SOP in JavaScript land?

I don't think it could be called SOP.

  • You may want to correct a syntax error: `s/Is is/Is it/`. – Vidul Aug 28 '15 at 06:15
  • What are peoples views when an object is created purely as a temporary instance object to do some transformation, but the object is an assignee of other functions calls, e.g: var tempObj = Object.create(null); for (var i=0; i – stephenwil Jul 05 '16 at 07:52
27

To the best of my knowledge,

{} == Object.create(Object.prototype);

Also,

To me (Only me) I feel like Object.create(null) gives you more clarity that your object is not going to inherit anything i.e its (purely) empty map.


Also,

In both cases you can use object as dictionary.


Also,

It depend upto you what you supposed to do with your object.


When,

  1. {} == Object.create(Object.prototype); // you inherit all object properties

  2. o = Object.create(null); // you do not inherit anything. this is completely blank object.

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(object, 'toString')) {
    // we actually inserted a 'toString' key into object
}

Note that you can't just do object.hasOwnProperty('toString') because you may have inserted a key "hasOwnProperty" into object, 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 (object[someKey]) {
    // ...
}

SO,

To use object as a Map most of the people on planet use

Object.create(null) // no overhead to worry about inherited properties.

In all other cases you simply use,

var myObject = {}

MDN - Object.create

MSDN - Object.create

syntagma
  • 23,346
  • 16
  • 78
  • 134
Deepak Ingole
  • 14,912
  • 10
  • 47
  • 79
  • That's exactly what I do, simple one-line checks like you stated: `if (object[someKey])` that's only possible with `Object.create(null)`. Thanks for clarifying the integrity of my approach. – Data Aug 28 '15 at 04:02
  • 1
    `{} === Object.create(Object.prototype)`. If you mean semantically equivalent, yes. If you mean strictly equal, no. –  Aug 28 '15 at 04:13
  • @Pilot `{} == Object.create(Object.prototype)` returns `false`. These two methods (`{}` and `Object.create(Object.prototype)`) do the same things but their return value are not *equal* (I mean, not `==` nor `===` nor `Object.is()`). I think you should use some English words instead of `==` – DMaster Jan 27 '16 at 10:49
8

I'd like to refer to the Mozilla doc about Objects and maps compared here to give Map a chance as a better practice nowaday.

Objects are similar to Maps in that both let you set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Because of this (and because there were no built-in alternatives), Objects have been used as Maps historically; however, there are important differences that make using a Map preferable in certain cases:

  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.
  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.
  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion and iterating over them.
  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can be bypassed by using map = Object.create(null), but this is seldom done.
  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.
Eric Liu
  • 273
  • 3
  • 3
  • 1
    For modern code, this is absolutely the way to go. Use Map for arbitrary key/value stores, and the legitimate use cases for Object.create(null) all but vanish. That is not to say you won't encounter them: legacy code still uses it frequently, so be ready to handle it. – hraban Oct 28 '19 at 15:30
  • To mention a few downsides: 1) `Map` requires `new`. People like Douglas Crockford consider `new` a hazard and strongly advocate against using it anywhere. And 2) because `Map`s are objects, they can be misused using the `["key"] = value` syntax. – Oliver Schimmer Jul 08 '21 at 23:56
7

From the book You Don't Know JS: this & Object Prototypes:

Object.create(null) is similar to {}, but without the delegation to Object.prototype, so it's "more empty" than just {}.

Object.create(null) is a good practice in case that you need a DMZ (empty object for explicit binding: apply & call, bind).

Vidul
  • 10,128
  • 2
  • 18
  • 20
  • But you can also `bind` with `null` or `0`. –  Aug 28 '15 at 03:43
  • @torazaburo Wrong! You don't want `null` or `undefind` since the function in question can expect the object context or even worse - no expectations for it and `mutation` of the global object. You need a DMO like `ø`. – Vidul Oct 29 '15 at 02:07
  • If the function does **not** expect an object context--such as `Math.max`, to take just one example--then there is nothing whatsoever wrong with passing `0` or `null`, and in fact it's common practice. I do not understand your reference to the mutation of the global object. –  Oct 29 '15 at 03:34
  • @torazaburo Consider the following example: `function global_this() { this.name = 'global property changed'; } global_this.apply(null); console.log(window.name);`; – Vidul Oct 30 '15 at 14:34
  • As I clearly said, the use of 0 or null is limited to cases where the function being invoked does not use this. –  Oct 30 '15 at 17:16
  • @torazaburo You are missing the point - `null` is not safe. It's that simple. – Vidul Oct 30 '15 at 20:47
  • 1
    You're wrong, unfortunately. It is used widely by experienced developers to deal with the case where `this` does not matter, as a work-around to the fact that JS has no version of `bind` or `call` which do not need the initial context argument. Consider that babel, when asked to transpile `foo(...args)`, will **exactly** produce `foo.apply(null, args)`. –  Oct 31 '15 at 05:21
5

You're saying you're using Object.create(null) for when you want to create an Object which doesn't inherit from Object.prototype.

This is exactly the use case for the Object.create method with null as the first argument.

Using {} is equivalent to Object.create(Object.prototype). A lot of the time we want to do this, which is why it's what the literal does, but in your case your control over inheritance is cleaner.

If you need any method from Object.prototype, you can invoke using .call or .apply on your Object,

var o = Object.create(null);
Object.prototype.hasOwnProperty.call(o, 'foo'); // some key foo
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • He also explicitly said he needs object functionality. – Omar Himada Aug 28 '15 at 03:26
  • @Adosi It is an _Object_, what functionality is missing? – Paul S. Aug 28 '15 at 03:31
  • You suggested using "null". null is not an object. He wants object functionality. – Omar Himada Aug 28 '15 at 03:35
  • @Adosi - I think you mis-read my question; I don't need functionality when I use null, it's just a hash map/dictionary for storing simple data, e.g. `var size = Object.create(null); size.height = 3`, etc – Data Aug 28 '15 at 03:38
  • No, you mis-understand my comment. Paul S's suggestion is to use null instead of `Object.create(null)` and `{}`. That would remove any object functinoality in its place. If `var t = null` you cannot treat `t` as an object. – Omar Himada Aug 28 '15 at 03:40
  • Sorry to be a know-it-all, but in JavaScript, "null" is an object. – Steve Hynding Aug 28 '15 at 03:41
  • No it isn't. It is a primitive. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null – Omar Himada Aug 28 '15 at 03:43
  • Why is typeof null === "object"? I'm no longer a know it all with that question :( – Steve Hynding Aug 28 '15 at 03:45
  • @Adosi nowhere have I said use `null` by itself, the answer says **this method with**, i.e. the `Object.create` method – Paul S. Aug 28 '15 at 03:47
  • @Paul S. "This is exactly the use case for this method with null." Unless your understanding of the definition of `use case` is different than anyone elses. You're suggesting OP's exact use case is best met with the use of the literal `null`. – Omar Himada Aug 28 '15 at 03:48
  • 1
    @Adosi `Object.create` is a _method_. I will edit instead of "this" in the answer to make it more explicit – Paul S. Aug 28 '15 at 03:49
3

Something that none of the other answers address is that if you are using {} as a dictionary, and your keys are unpredictable (e.g., input from a user), then you should really opt for Object.create(null) or Map since its conceivable that assigning a value to a certain key using the = operator (i.e., the object's setter) may silently fail because its trying to modify a read-only property in the prototype chain, for example on the __proto__ property:

let dictObject = {};
let dictNull = Object.create(null);

let key = '__proto__';
dictObject[key] = 'value';
dictNull[key] = 'value';

console.log(dictObject[key] === value);  // false
console.log(dictNull[key] === value);  // true

In order to define the __proto__ property on an Object, then you'd need to define the property/value on the object itself:

Object.assign(dictObject, '__proto__', {value:'value'});

So the solution in this case is to use Object.create(null) or Map

Blake Regalia
  • 2,677
  • 2
  • 20
  • 29
-1

In my opinion you should use Map if possible.

If not you should try to design your algorithm/code in a way that avoids lookup inherited prototype keys. Unless you are writing some lower level util libraries, in 99% of cases you don't need or want to worry if a prop is from an object itself or inherited from the prototype chain.

If not then you can use either Object.create(null), or go with hasOwnProperty() guard if there are not a lot of cases concerning inherited prototype keys.

So Object.create(null) should be the last resort.

Jason Lee
  • 357
  • 2
  • 10