16

I'd like to use functions as keys in a javascript object. The following works, at least in Chrome:

var registry = {};
function Foo(){  };
function Bar(){  };
registry[Foo] = 42;
registry[Bar] = 43;
alert(registry[Foo] + " < " + registry[Bar]);

Is this covered by the standard? By which browsers is it supported?

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
  • Well, worked in Chrome. So it might work in Safari and Firefox – Danilo Valente Jun 01 '12 at 23:48
  • i think it is like you alert a function, somehow the engine parses it as a string because it is permissive... But I'ld say it is not a good idea, unless you cast it as a string yourself before (is such thing possible I wonder) – Sebas Jun 01 '12 at 23:52
  • The keys of any kind of any dictionary-class object must have a well defined == operation. Such an operation can not be defined for functions (http://en.wikipedia.org/wiki/Rice%27s_theorem). – Robert Hensing Jan 09 '13 at 15:01
  • @RobertHensing if so then how does `addEventListener` and `removeEventListener` work? `addEventListener` will not add same callback twice for same event. And obviously `removeEventListener` needs to recognise the callback being removed. I did not understand the Rice's theorem, but in my mind a function can be represented as a pointer. Why isn't this used? Update: keys of type object do not work either. – Roland Pihlakas Jan 21 '15 at 09:57
  • [Specification](//tc39.es/ecma262/#sec-property-accessors-runtime-semantics-evaluation), [documentation](//developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Property_Accessors#property_names). Related: [Why does \[\]\[\[\]\] evaluate to undefined?](/q/49310701/4642212). – Sebastian Simon Dec 20 '21 at 20:44

2 Answers2

17

Everything you put between square brackets is converted into a string, and this happens even if you put a function, a date, a regexp... So there, you're actually creating an object like this:

var registry = {
    "function Foo(){  }" : 42,
    "function Bar(){  }" : 43
};

This is a default behaviour, it works in IE too if you were wondering. It was actually exploited by John Resig in his famous addEvent function.

MaxArt
  • 22,200
  • 10
  • 82
  • 81
  • 2
    Note that functions with different closures can be converted to the same string, and access the same property. From the node console: > var maker = function(a) { return function(){ console.log(a); }; }; > var bb = maker('bb') > bb() bb > var cc = maker('dd') > cc() dd > var test = {}; > test[bb]='AA' 'AA' > test[bb] 'AA' > bb() bb > test { 'function (){ console.log(a); }': 'AA' } > test[cc] = 'DD' 'DD' > test { 'function (){ console.log(a); }': 'DD' } > test[bb] 'DD' > test[cc] 'DD' – JoeAndrieu Jan 01 '16 at 08:40
8

ECMAScript 5.1 - Section 11.2.1:

The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:

  1. Let baseReference be the result of evaluating MemberExpression .
  2. Let baseValue be GetValue(baseReference).
  3. Let propertyNameReference be the result of evaluating Expression.
  4. Let propertyNameValue be GetValue(propertyNameReference).
  5. Call CheckObjectCoercible(baseValue).
  6. Let propertyNameString be ToString(propertyNameValue).
  7. If the syntactic production that is being evaluated is contained in strict mode code, let strict be true, else let strict be false.
  8. Return a value of type Reference whose base value is base Value and whose referenced name is propertyNameString , and whose strict mode flag is strict.

So when using obj[whatever], whatever is converted to a string. For a function this will be a string containing the sourcecode of the function.

Example:

js> var func = function() { return 'hi'; };
js> function helloworld() { return 'hello world'; }
js> var obj = {};
js> obj[func] = 123;
js> obj[helloworld] = 456;
js> obj
({'function () {\n    return "hi";\n}':123,
  'function helloworld() {\n    return "hello world";\n}':456
})
ThiefMaster
  • 310,957
  • 84
  • 592
  • 636