My Notebook

How to use an object as a key in JavaScript

Author
Date
Category
Web Development/JavaScript

At first glance it seems perfectly reasonable to use an object as a key in JavaScript, but it doesn't work as you might expect it to. This is a dangerous pitfall, because there are no warnings or error messages and the resulting code seems to work fine. The nasty bugs, introduced by this, surface months later and are very hard to track down.

The Problem

It is easy to be fooled into believing this works. A quick test like the following piece of code seems to work as expected:

var obj1 = {};
var table = {};
table[obj1] = "obj1";
console.log(table[obj1]) // Outputs "obj1"

However, if you add another object to the table, you get weird results:

var obj1 = {};
var obj2 = {};
var table = {};
table[obj1] = "obj1";
table[obj2] = "obj2";
console.log(table[obj1]) // Outputs "obj2"

The mystery immediately resolves with the following code sample:

var obj1 = {};
var table = {};
table[obj1] = "obj1";
console.log(table["[object Object]"]) // Outputs "obj1"

JavaScript simply turns every object into the same string "[object Object]" and uses that as the key to the table. There are no warning messages, nothing.

A simple Workaround

I used the following workaround to fix a bug in the syntax highlighter Prism.js. The corresponding commit is b86c727. I cannot claim all the credit for fixing it, because I had introduced it myself months earlier.

The idea is very simple. We generate a unique id for every object that is inserted into the table. The best place to store such an id is in a non-enumerable property on the object itself. Instead of using the object as a key, we use its id:

var uniqueId = 0;
function objId(obj) {
    if (!obj['__id']) {
		Object.defineProperty(obj, '__id', { value: ++uniqueId });
	}
	return obj['__id'];
}

var obj1 = {};
var obj2 = {};
var table = {};

table[objId(obj1)] = "obj1";
table[objId(obj2)] = "obj2";

console.log(table[objId(obj1)]) // Outputs "obj1"
console.log(table[objId(obj2)]) // Outputs "obj2"

The function objId() returns a unique id for every object. The property '__id' is not enumerable, which means it won't show up in a for (var poperty in obj) {} statement.

Conclusion

I cannot imagine a situation, where you would want to use the key "[object Object]" for anything. It is almost certainly a bug, and every JavaScript linter should show a big warning.

References