JavaScript dictionaries: use hasOwnProperty, set __proto__ to null, or use ES proxies?

Siddharth Agarwal sid at mozillamessaging.com
Tue Dec 14 13:20:56 PST 2010


On 10-12-2010 18:16, Siddharth Agarwal wrote:
>
> There are a few ways out of this:
> (a) Use hasOwnProperty [2] instead of "in" everywhere we want to test 
> membership.
> (b) Set __proto__ [3] to null at dictionary creation time.
> (c) Similar to (b), define a Dict constructor and set its prototype to 
> {__proto__: null}. Then create dictionaries with new Dict() instead of 
> {}.
> (d) Building on (c), use ECMAScript proxies [4] for Dict to get better 
> semantics.

(Sorry for the rambling post.)

So I've had a chance to look at this over the weekend, and I tried 
working on an implementation using proxies. Whatever I managed to do is 
available at GitHub [1].

(a) is what we went with in the end for bug 609941. However it still has 
issues with special keys, e.g. "__proto__" (see the readme at [1]). It 
is of course extremely unlikely that a user will have a tag named 
__proto__, which is why that fix is fine for now.

(b) and (c) have the problems I mentioned in my first post.

(d) unfortunately has the same problem: since there's no way to 
differentiate between an implicit conversion to a string and an explicit 
access to the toString property, we risk having subtle bugs depending on 
user input.

At this point I'm convinced that the property syntax is most likely the 
wrong way to go. So I switched to a Python-inspired get/set/has syntax, 
and used a prefix for every key to avoid collisions with special 
properties. The implementation is at [2]. In micro-benchmarks it is 
almost as fast as native property access, which is a good thing.

I also wrote a proxy-based wrapper [3] that allows "in" and for..in to 
work -- however, in micro-benchmarks get/set operations are around 2x as 
slow. This is similar to the slowdown the authors of the original paper 
[4] found.

Considering that, I think the non-proxy implementation is the best for now.

Open question: what should for (let x in dict) over the dictionary 
return? For consistency with objects it should return just keys, but 
these days people prefer writing |for (let x in Iterator(dict))|, and 
with normal objects that returns a [key, value] pair. I haven't found a 
way to differentiate the two cases. Of course we could just not do 
anything at all and instead require the user to specify what he wants 
(I've added iterators for all three possibilities).

Sid

[1] https://github.com/sid0/jsdict
[2] https://github.com/sid0/jsdict/blob/master/modules/dictbase.js
[3] https://github.com/sid0/jsdict/blob/master/modules/dict.js
[4] http://www.google.com/research/pubs/pub36574.html


More information about the tb-planning mailing list