Javascript: typeof and instanceof results contradicting?
I am seeing contradicting results when doing typeof and instanceof on an object.
I have the following test page:
<html>
<body>
<object id="test" />
<script type="text/javascript">
var foo = document.getElementById("test");
console.log(typeof foo); // returns "function"
console.log(foo instanceof Function); // returns false
console.log(foo instanceof Object); // returns true
</script>
</body>
</html>
"typeof foo" is returning "function" but "foo instanceof Function" is returning false.
This doesn't make sense to me. How can an object have a type be function but not be an instance of a funct开发者_运维问答ion? Also, I expected "typeof foo" to return "object".
The essential fact is that DOM elements (obtained, for example, using document.getElementById()
) are not native JavaScript objects. Instead, they are host objects. As such, they are not subject to the usual rules that apply to native JavaScript objects, and their behaviour will (quite legitimately) vary wildly from one browser to the next.
In short, all bets are off. Don't rely on the behaviour of host objects outside of their documented API.
Related references:
- http://michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
- http://twitter.com/kangax/status/14790178873
- http://perfectionkills.com/whats-wrong-with-extending-the-dom/
object
and embed
HTML elements are host objects.
ECMAScript3 11.4.3 didn't define what typeof
should return for these. The table of returned values for Objects was
- Object (native and doesn't implement [[Call]]):
"object"
- Object (native and implements [[Call]]):
"function"
- Object (host): Implementation-dependent
However, returning "function"
is consistent with ECMAScript5 11.4.3:
- Object (native and does not implement [[Call]]):
"object"
- Object (native or host and does implement [[Call]]):
"function"
- Object (host and does not implement [[Call]]): Implementation-defined except may not be
"undefined"
,"boolean"
,"number"
, or"string"
.
object
and embed
HTML elements are objects with an internal [[Call]] property, so typeof
must return "function"
.
This is explained in Bug 268945:
Comment #15, by Boris Zbarsky:
The [[Call]] is very intentional on the DOM side: these are callable objects.
Comment #16, by Tom Schuster:
This bug is invalid these object have a [[Call]] internal method and ES5 11.4.3 explicitly say "Object (native or _host_ and does implement [[Call]])" => "function".
Since object
and embed
HTML elements implement an internal [[Call]] property, they are callable objects. However, they are not functions:
4.3.24 function
member of the Object type that is an instance of the standard built-in
Function
constructor and that may be invoked as a subroutine
object
HTML elements inherit from HTMLObjectElement.prototype
, and embed
HTML elements from HTMLEmbedElement.prototype
.
The prototype chain continues with
HTMLObjectElement.prototype
HTMLElement.prototype
Element.prototype
Node.prototype
Object.prototype
Therefore, they are not Function
instances, because they don't inherit from Function.prototype
.
Safari returns 'function' for typeof document.getElementsByTagName('p'),
which gave me a bad time a while ago, when I assumed all clients would return 'object'.
It is good to test our pre-conceptions, even when an exception makes no sence.
Also, debuggers are lovely, but they do not always report the same values as their browser.
I'm not sure why 'function' is returned for a <object>
node, but the reason instaceof Function
fails is because that's essentially a check against the constructor, the difference of which can be illustrated like so
<html>
<body>
<object id="test" />
<script type="text/javascript">
var foo = document.getElementById("test");
var bar = function() {}
console.log( typeof foo );
console.log( foo instanceof Function );
console.log( foo.constructor ); // Object()
console.log( typeof bar );
console.log( bar instanceof Function );
console.log( bar.constructor ); // Function()
</script>
</body>
</html>
So in this case, it's actually typeof
that is behaving oddly, and not in a way that is documented that I can see.
In your first example this dom element does not exist when the script is run. In your second it does, the answers are correct in the second the difference is between typeof and constructor. typeof returns a function because document.getElementById returns a function. constructor tells you the name of that function "HTMLElement".
you could for example invent your own elements starting with var myElement = new HTMLElement()
I found some additional strange behavior.
If you move the javascript code above the object, then typeof works correctly but instanceof does not work.
<html>
<body>
<script type="text/javascript">
var foo = document.getElementById("test");
console.log(typeof foo); // returns object (this is correct now)
console.log(foo instanceof Function); // returns false (this is correct)
console.log(foo instanceof Object); // returns false (this is wrong)
console.log(foo instanceof HTMLElement); // returns false (this is wrong)
console.log(foo instanceof Node); // returns false (this is wrong)
</script>
<object id="test" />
</body>
VS
<html>
<body>
<object id="test" />
<script type="text/javascript">
var foo = document.getElementById("test");
console.log(typeof foo); // returns function (this is wrong)
console.log(foo instanceof Function); // returns false
console.log(foo instanceof Object); // returns true (this is correct)
console.log(foo instanceof HTMLElement); // returns true (this is correct)
console.log(foo instanceof Node); // returns true (this is correct)
</script>
</body>
So basically if the script is below the object, typeof behaves strangely but if the script is above the object instanceof behaves strangely.
精彩评论