5

I'm seeing the following behaviour, in the middle of a javascript debug session:

o // function (a1, a2, a3) {  return a1 + a2 + a3; }

typeof(o) //'function'

for (var n in o) { console.log(n); } //<a list of properties>

Object.keys(o) //TypeError: not an object

Object.prototype.toString.call(o); //"[object Function]"

which makes me wonder - can a function, ever not be object?

[running on Chrome 29, on a mac]


Note that this is in the middle of a very complex debug session. I don't exactly know where 'o' is coming from, or how it was created. Also, I've so far been unable to reproduce this issue with a simple test case. A simple setup works as expected:

var t = function() { return true; } //undefined
t.a = "aa" //"aa"
Object.keys(t) //["a"]
blueberryfields
  • 45,910
  • 28
  • 89
  • 168
  • 4
    Please reconstruct where `o` was coming from - maybe it is a buggy host object. The question is useless however if this is not reproducable. – Bergi Sep 17 '13 at 18:45
  • I get an empty array when I try that on a function with no properties (also in Chrome 29/Mac). Functions are *always* objects. – bfavaretto Sep 17 '13 at 18:46
  • No error,chrome 29.0.1547.66 m on win7. – wener Sep 17 '13 at 18:46
  • 3
    You were not using a ES5 shim that overwrites `Object.keys` despite its existence and that does the type check wrong (with `typeof`)? – Bergi Sep 17 '13 at 18:47
  • 1
    I'm not looking for debugging help - I'm looking to better understand javascript. Clearly, sometimes, functions are not objects - either this is correct behaviour, or I've run into something much funkier (ie, a virtual machine bug or equivalent) – blueberryfields Sep 17 '13 at 18:47
  • 1
    @Bergi the question is not useless if not reproducible. The asker is asking in general terms, meaning that someone who understands JavaScript well enough should be able to answer the question without needing to be able to reproduce the issue. – DigitalZebra Sep 17 '13 at 18:48
  • 2
    *"Clearly, sometimes, functions are not objects..."* No that's the part that's not clear without a reproduceable test – user2736012 Sep 17 '13 at 18:48
  • @blueberryfields: Your problem reminds me of this: http://stackoverflow.com/questions/18721969/ie8-queryselector-null-vs-normal-null. The issue is (probably) that `0` is a "host object", which makes up its own rules. – gen_Eric Sep 17 '13 at 18:49
  • Instead of `typeof o`, try `Object.prototype.toString.call(o);` – user2736012 Sep 17 '13 at 18:49
  • Alright, now try `o + ""` to see what you get. – user2736012 Sep 17 '13 at 18:52
  • "function (a1, a2, a3) { return a1 + a2 + a3; }" – blueberryfields Sep 17 '13 at 18:54
  • When you say you don't know where `o` comes from or how it was created, are you saying it's not in your code at all, but appears in the debugger? – user2736012 Sep 17 '13 at 18:55
  • 1
    Is there any asynchronous code running? Can you post a larger sample of your actual code? – user2736012 Sep 17 '13 at 19:13
  • "o" is an object that appears in the system I'm debugging. i can see it's source (it's not much more complex than i document it to be). i don't see any asynchronous code working anywhere, but the system is large, maybe i'm missing something? when i attempt to reproduce this by just copying the code i'm looking at, and manually creating o from its source code, i don't see the error message... still, how would an async system cause this kind of problem? – blueberryfields Sep 17 '13 at 19:22
  • Does this only occur when you're stepping through code in the debugger? Or does it always happen? The async code would need to be in between (or part of) the lines of code you show at the top, and would need to have an effect on the `o` variable. – user2736012 Sep 17 '13 at 19:24
  • if `for (var n in o) { console.log(n); }` gives you a list of properties, that would be odd since the properties of a function are non-enumerable. What are the properties it gives? – user2736012 Sep 17 '13 at 19:29
  • 3
    ahahahaha. It's a custom implementation of "Object.keys"! I'm going to go smack my head into some bricks. – blueberryfields Sep 17 '13 at 19:31
  • @blueberryfields: If I may ask, how is it that you didn't know you had a broken `Object.keys()` overwriting the native one? Could you post the source in an answer? – user2736012 Sep 17 '13 at 19:33
  • 3
    @Bergi deserves credit for [anticipating an overwrite of `Object.keys`](http://stackoverflow.com/questions/18857500/when-is-a-function-not-an-object/18857728?noredirect=1#comment27825254_18857500) a long time ago. – user2736012 Sep 17 '13 at 19:45

2 Answers2

4

When you see something like this, you might try:

console.log(Object.keys)

or equivalent.

(if you can find the original implementation of keys for your browser, to compare and make sure it's identical to what you're seeing)

(read the comments on the question for more ideas of things to look at if seeing this kind of problem)


False alarm. Functions are always objects, and the people at Chrome know how to make virtual machines.

I ran

grep -r "Object.defineProperty(" *

grep -r "Object.defineProperties(" *

and found a place where Object.keys is being overwritten, with a buggy function.

The related code was being loaded dynamically, so I didn't get to see it explicitly get loaded in the browser.

blueberryfields
  • 45,910
  • 28
  • 89
  • 168
  • Good job. I suggest you leave this question with all the answers/comments. There is a lot of interesting knowledge here. – freakish Sep 17 '13 at 19:37
  • Whatever code you have that dynamically loaded a broken overwrite of `Object.keys` needs to be removed from your code base, followed by an email to the developer suggesting that they apply for a job at their local car wash.. – user2736012 Sep 17 '13 at 19:38
  • 1
    @user2736012 it's on my todo list... One thing at a time :P – blueberryfields Sep 17 '13 at 19:38
  • 3
    ...but +1 for drilling down to the issue. In retrospect, a `console.log(Object.keys)` would have been a good suggestion. – user2736012 Sep 17 '13 at 19:39
2

The first step performed by the Object.keys algorithm is:

  1. If the Type(O) is not Object, throw a TypeError exception.

Since you're getting such an error, I believe your object must be a host object, as Bergi commented above. Still, it's strange that Object.prototype.toString.call(o) is giving "[object Function]", since host objects are not allowed to use "Function" as their [[Class]] property value.

* For the meaning of Type(x), see the last sentence from Section 8 of the spec.

Community
  • 1
  • 1
bfavaretto
  • 71,580
  • 16
  • 111
  • 150
  • See also, this question: http://stackoverflow.com/questions/18721969/ie8-queryselector-null-vs-normal-null – gen_Eric Sep 17 '13 at 18:56
  • @user2736012 I'm not saying it can't work on a host object (it does work on document and window in Chrome, for example). And my first version was incorrect, the spec says the type must be Object, or an error will be thrown. – bfavaretto Sep 17 '13 at 18:58
  • Deleting my comment because you updated. But looking at the `Object.prototype.toString.call` output, it seems to be native, unless there's a bug. The output `[object Function]` isn't permitted for host objects. – user2736012 Sep 17 '13 at 19:03
  • @user2736012 That's actually not true: http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2 – freakish Sep 17 '13 at 19:05
  • @bfavaretto Specification actually defines host/native objects. What worries me is that there is no such distinction in that condition, i.e. you can't assume that by `Object` they mean native object. Besides, I can't see an example of `Object.keys` not working with a host object. – freakish Sep 17 '13 at 19:06
  • 4
    @freakish No, because 15.2.4.2 step 4 uses `[[Class]]`, but section [8.6.2](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2) says: `The value of the [[Class]] internal property of a host object may be any String value except one of "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String"`. Thus, shouldn't `[object Function]` be not spec-compliant for a host object? – apsillers Sep 17 '13 at 19:07
  • Yep, what @apsillers said. – user2736012 Sep 17 '13 at 19:08
  • @apsillers Fair enough. +1: nice finding. – freakish Sep 17 '13 at 19:09
  • @freakish They do mean native Object, see section 8. And user2736012 is right, a host object cannot return `[object Function]` for toString (see [8.6.2](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6.2)) – bfavaretto Sep 17 '13 at 19:09
  • @bfavaretto Could you show me where exactly this is stated? I can't find it. – freakish Sep 17 '13 at 19:11
  • @bfavaretto: Could you point out where it states that `Object` can not mean "host object"? I don't see how the last sentence of `8` restricts to native objects. – user2736012 Sep 17 '13 at 19:11
  • 1
    @bfavaretto There's even more: every time they talk about host/native object it is clearly stated so, for example: [assume O is a native ECMAScript object](http://www.ecma-international.org/ecma-262/5.1/#sec-8.12). And here they even distinguish the same condition: http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.2.1 I'm sorry, but this is an overinterpretation of ECMAScript and I have to give -1. – freakish Sep 17 '13 at 19:23
  • @freakish My comment was wrong, that's not stated in the spec. In fact, my answer even says that it seems to work with anything that inherits from `Object.prototype`. I'm assuming the object in the OP code doesn't. – bfavaretto Sep 17 '13 at 19:32
  • 1
    It doesn't even need to inherit from `Object.prototype`. This will work: `Object.keys(Object.create(null));` – user2736012 Sep 17 '13 at 19:34
  • @user2736012 True. I'll stop guessing and delete this answer, since the OP now states he was using a custom Object.keys. – bfavaretto Sep 17 '13 at 19:37
  • @bfavaretto Please, don't. There's a lot of knowledge here that might be helpful to someone. – freakish Sep 17 '13 at 19:38