All three of the above pass the tests when executed on FireFox v 19.0 an Opera v12.14
No, at least in Opera the tests in the fiddle fail for window.screen
, Math
, JSON
, DOMError
, LSParserFilter
, DOMImplementationLS
, window.opera
, SVGException
, and document.implementation
.
Is this a bug in Chrome/Chromium?
What? That different functions return different results? No.
And for each of the four objects, what should the correct result be (so I can determine which function is the most accurate)?
How do you defined "correct"? How do you defined "plain object"?
I believe that all 3 routines are using the following criteria:
Check to see if an object is a plain object (created using "{}" or "new Object").
That's hardly a useful criterion, since those objects where you experience discrepancies are not "created" - they are host objects (or even native objects) that just happen to exist.
Yet, we could compare which criteria those functions use:
jQuery is very odd; you can read it's source code at github. In short: An object or function, whose [[Class]] is not one of
Boolean Number String Function Array Date RegExp Error
, that has not a truthynodeName
property, that has not awindow
property pointing to itself, and that has noconstructor
property or whoseprototype
property of theconstructor
has an ownisPrototypeOf
property.They seem to do this for cross-browser support, but as you can see it fails in some cases where you would have it expected not to be a plain object.
Utility is a bit obfuscated, but the check itself is simple: An object whose [[Class]] is
Object
and whose prototype isObject.prototype
(or rather, whose prototype has aisPrototypeOf
method that yieldstrue
for{}
).Lodash has a few oddities like jQuery, but not that hard - you can read the source at github. It first checks for object type and not null, then gets
Object.prototype
viagetPrototypeOf(getPrototypeOf(…))
from avalueOf
method if it exists. If it found one, either the object or its prototype must be thatObject.prototype
object and it must not be anArguments
object. If it did not found one, it falls back to the shim which I'm not going to explain here.All these things are done to support detecting plain objects from different environments (e.g. iframes) with a different
Object.prototype
object and in browsers that do not provide agetPrototypeOf
method."Alternative" implementation: This tests the prototype of the object to be either
null
(but explicitly excludingObject.prototype
) or to beObject.prototype
and the [[Class]] value to beObject
.
should host objects be counted as plain objects?
Maybe. It always depends on your use case…
act like it was created by
new Object
Then just
getPrototypeOf(obj) == Object.prototype
should be fine (if you don't need to support cross-frame objects). TheMath
andJSON
objects would fulfill this requirement.Have no interfering enumerable properties on the prototypes
Then you might also allow
getPrototypeOf(obj) == null
or even manually check for enumerable properties like lodash does. This would now includeObject.prototype
as a "plain object" for example.creatable by
new Object
Then also add the check for [[Class]] to be
Object
to exclude native objects likeJSON
,Math
,Arguments
and all those host objects with implementation-specific classes. Would you really expect those to be passed in the functions that testisPlainObject
, and would they cause havoc if they passed the other tests?
See also Say what? at niftysnippets.org (blog by T.J.Crowder)