Reputation: 8870
I've come across this article that suggests using 'bare objects' for your hashmap needs if your keys are always strings.
Bare objects are objects created using null
as the prototype value, for instance with Object.create(null)
. Using the object literal notation (i.e. {}
) do not create a bare objects since they set Object.prototype
as the prototype.
The article points out that the great thing about bare objects is that you can use them as hashmaps without having to worry about builtin keys like toString
potentially causing bugs when using a key with the same name.
Is this behavior part of the ES5 and/or ES6 standard? That is, if I use bare objects as string key hashmaps in my code, can I rely on my code behaving in the way that I would expect? Are there any caveats to here?
Upvotes: 4
Views: 949
Reputation: 64923
First of all, ECMA-Script 2015 and above has collections like Map
. That is, in newer JavaScript implementations you don't need to simulate dictionaries/hashmaps/hashtables with objects anymore.
The article points out that the great thing about bare objects is that you can use them as hashmaps without having to worry about builtin keys like toString potentially causing bugs when using a key with the same name.
The article ignores that you don't need to worry about toString
in literal objects because there're well-supported functions to get own object's properties without walking through the prototype chain.
For example, let's say I've declared a literal object as follows: var obj = { text: "Matias" };
.
A regular for..in
loop would iterate Object.prototype
properties, but Object.keys
will iterate own object properties only:
Object.keys(obj).forEach(propertyName => {
var someOwnProperty = obj[propertyName ];
});
In addition, a regular for..in
could work as Object.keys
using Object.prototype.hasOwnProperty
:
for(var propertyName in obj) {
if(obj.hasOwnProperty(propertyName)) {
// True if property is declared on obj and not in some
// level of the prototype chain
}
}
AN UPDATE HERE: @bergi is right about something. If obj
would declare an own property hasOwnProperty
, above for..in
wouldn't work because obj.hasOwnProperty
wouldn't be Object.prototype.hasOwnProperty
anymore.
Imagine that you've the following object that would produce above described issue:
var obj = {
hasOwnProperty: "hey! I'm not Object.prototype.hasOwnProperty anymore!"
};
That hasOwnProperty
would hide Object.prototype.hasOwnProperty
.
Above described issue can be circumvented using Object.prototype.hasOwnProperty
directly with Function.prototype.call
:
for(var propertyName in obj) {
if(Object.prototype.hasOwnProperty.call(obj, propertyName)) {
// True if property is declared on obj and not in some
// level of the prototype chain
}
}
Or you could store Object.prototype.hasOwnProperty
in a variable to simplify the if
statement setting what would be this
once the function gets called with Function.prototype.bind
:
var hasOwnProperty = Object.prototype.hasOwnProperty.bind(obj);
for(var propertyName in obj) {
if(hasOwnProperty(propertyName)) {
// True if property is declared on obj and not in some
// level of the prototype chain
}
}
While you can create bare objects with Object.create(null)
, the problem comes when a given bare object is the prototype of some other object:
var bareObject = Object.create(null, {
text: { value: "hello world" }
});
var secondObject = Object.create(bareObject);
secondObject.text2 = "bye!";
for(var property in secondObject) {
// WAIT, secondObject prototype is a bare object!
// And I can't call secondObject.hasOwnProperty to check
// if the enumerated property is declared in the own object...
}
If this isn't the case and given object is just a bare object, you can use in
operator:
if("someProperty" in bareObject) {
}
Otherwise, you would need to go with calling Object.prototype.hasOwnProperty
with Function.prototype.call
or Function.prototype.bind
as described above in my answer.
Anyway, as I state at the beginning of my answer, if you're working with ES2015 and above, and you're using a transpiler like BabelJS, you can go with the new JavaScript standard collection types instead of simulating dictionaries with objects.
Is this behavior part of the ES5 and/or ES6 standard? That is, if I use bare objects as string key hashmaps in my code, can I rely on my code behaving in the way that I would expect? Are there any caveats to here?
Object.create
was introduced in ECMA-Script 5. It behaves as you expect in almost any modern Web browser and NodeJS.
Upvotes: 5
Reputation: 664513
Is this behavior part of the ES5 and/or ES6 standard?
Yes. You can find Object.create(null)
and the behaviour of looking up / assigning properties closely specified in the ES5, ES6 and ES7 standards.
If I use bare objects as string key hashmaps in my code, can I rely on my code behaving in the way that I would expect?
No. There is no guarantee in the standard that objects are implemented as hashmaps, and there are no complexity/performance assertions either. Implementations might have very slow property lookup on large objects.
Since ES6, you should use Map
objects which guarantee sub-linear complexity.
Upvotes: 0