Associative Arrays and Javascript
Andrew Dupont has a post about Associative Arrays in Javascript. He makes the good point that you shouldn't use the Array object for this functionality, instead use 'Object'.
Unfortunately using 'Object' has its own set of pitfalls. Try the following code in your favourite Javascript implementation:
This will display details on the toString method on 'Object'. So we can't use a plan Object as an associative array as it has some default entries in it already obtained from its prototype.
This can be worked around by using the Javascript standard function 'hasOwnProperty'. So you could write a simple Hashtable object with a 'get' method that does:
Now the following code works:
It has a subtle bug too though. This breaks:
Oops, we overwrote our 'get' method. So the Hashtable object really needs to store its data in an internal object:
The following code now works as expected:
The trap of using a basic Object as an associative array is seductive but as you can see it has pitfalls. They are easy to work around but it's easy to forget. Dojo's current implementation of Dictionary (as of 0.3) has this problem for example. There's a thread on the mailing list at the moment about fixing it (raised by me).
Categories: javascript
Unfortunately using 'Object' has its own set of pitfalls. Try the following code in your favourite Javascript implementation:
var x = {}
x["toString"]This will display details on the toString method on 'Object'. So we can't use a plan Object as an associative array as it has some default entries in it already obtained from its prototype.
This can be worked around by using the Javascript standard function 'hasOwnProperty'. So you could write a simple Hashtable object with a 'get' method that does:
function Hashtable() { }
Hashtable.prototype.get = function(key) {
if(this.hasOwnProperty(key))
return this[key];
else
throw "No such key";
}Now the following code works:
var x = new Hashtable();
x.get("toString");
=> uncaught exception: No such key
x["blah"] = 5;
x.get("blah");
=> 5
It has a subtle bug too though. This breaks:
x["get"] = 5;
x.get("get");
=> TypeError: x.get is not a function
Oops, we overwrote our 'get' method. So the Hashtable object really needs to store its data in an internal object:
function Hashtable() { this.table = new Object }
Hashtable.prototype.get = function(key) {
if(this.table.hasOwnProperty(key))
return this.table[key];
else
throw "No such key";
}
Hashtable.prototype.put = function(key, value) {
this.table[key] = value;
}The following code now works as expected:
var x = new Hashtable();
x.put("get", 5);
x.get("get");
=> 5
x.get("toString");
=> uncaught exception: No such key
The trap of using a basic Object as an associative array is seductive but as you can see it has pitfalls. They are easy to work around but it's easy to forget. Dojo's current implementation of Dictionary (as of 0.3) has this problem for example. There's a thread on the mailing list at the moment about fixing it (raised by me).
Categories: javascript

4 Comments:
There's another problem. On mozilla based browsers every object has a __proto__ member which returns true for hasOwnProperty. It's also especially dangerous here since x.get("__proto__") will return Object.prototype. Nice.
I think the best solution is to prepend a 'unique' prefix to the key.
Would be nice with a special hash object that does not inherit any members from prototypes...
But since such an object does not exist we have to live with workarounds ;-)
Here's my workaround/solution to the problem:
http://www.thomasfrank.se/object_prototype_is_erlaubt.html
How about this:
js> table = {};
[object Object]
js> table.__proto__ = null;
null
js> table;
js: uncaught JavaScript runtime exception: TypeError: Cannot find default value for object.
how about this combination:
function Hashtable() { this.table = {}; if('__proto__' in this.table) this.table.__proto__ = null; }
Hashtable.prototype.get = function(key) {
if(key in this.table)
return this.table[key];
else
throw "No such key";
}
Hashtable.prototype.put = function(key, value) {
this.table[key] = value;
}
Post a Comment
<< Home