yesterday, i was in the middle of testing an optimization to a javascript tool, when i saw this sequence occur in my debugger:
o // function (a1, a2, a3) { return a1 + a2 + a3; } typeof(o) //'function' (1) for (var n in o) { console.log(n); } // --> a long list of items Object.keys(o) //TypeError: not an object (2) Object.prototype.toString.call(o); //"[object Function]"
headscratch
clearly there’s a bug here, somewhere. but where? care to take a guess before reading on?
instinctually, this felt like a deep kind of problem. maybe a vm level bug, or a strange corner case in the javascript language definition.
as far as a bug goes, though, these kinds of bugs, are rare. Object.keys is a core function – significant production level code uses it, and relies on it functioning properly; it’s incredibly unlikely that a bug, especially one this major, would have escaped into a production level browser.
and if this strange behaviour is part of the ECMAscript standard, then it would definitely be near the top of javascript gotchas – i would have seen it documented somewhere.
so, i reasoned, this must be a result of work done in the particular bit of java code i was looking at. someone must be explicitly messing around with the system, and there should be traces of the relevant javascript in my current debugging session, not anywhere deeper down in the system.
unfortunately for me, despite having reasoned correctly thus far, a quick check for code that might alter the systems’ behaviour didn’t yield results. so i followed my instincts, and proceeded to, in parallel, test for a bug in the virtual machine, at the same time reading through ECMAScript specifications. time not completely wasted – i learned, for example, that:
- the ECMAScript definition is not fully clear on certain components – such as “host object”s. as far as i understand, these are convenience objects the vm may provide to a user, and are usually vm dependant. so while there may be consensus between browser vendors on some of the objects, specifics vary between vendors, and implementations may not be complete, or standard compliant. not entirely relevant here – my object was a standard, run of the mill, javascript object, and not a “host object”. still interesting though
- this kind of issue may pop up if you’re dealing with concurrent code execution – an object can mutate under your feet, for example. very unlikely for most javascript applications, though, and not an issue here
- the output of native functions, like Object.keys and Object.prototype.toString.call, can vary depending on how an object is instantiated (try regular var a = function.. versus var a = new Function(…)). the function i was looking at, however, didn’t do any strange instantiation behaviour
my instinct, of course, was off – and my initial reasoning had been correct.
after much experimentation, i eventually (almost accidentally, since i wasn’t explicitly looking for it) discovered a dynamically loaded bit of javascript, which was (drumroll) …
overriding the native implementation of Object.keys, with a buggy bit of code.
so, false alarm. javascript functions are, as expected, always objects, and my VM doesn’t include a very nasty looking bug.