Is there any way to reliably whether a JavaScript object is an exotic object type, and if so what its type is?
By "exotic", I mean (for the purposes of this question) anything which is could not be created using Object.create
. This includes everything the ES2016 spec defines as exotic (any "object that does not have the default behaviour for one or more of the essential internal methods") plus anything created by the ObjectCreate specification method with a non-empty internalSlotsList, plus any kind of host object.
By "reliable", I mean not subject to being tricked by adding / removing properties from the object, using Object.create
or Object.setPrototypeOf
to give the object an unexpected prototype, modifying the object's @@toStringTag, or a constructor's @@hasInstance. This is important for library functions that need to correctly handle arbitrary user data.
(This means, in particular, that instanceof
and Object.prototype.isPrototypeOf()
are not useful. For example: var a = Object.create(Array.prototype)
makes a something that looks and smells like an array—a instanceof Array === true
and a.push
and a.pop
work as expected—but lacks the magic behaviour .length
and is easily shown not to be an actual array exotic: Array.isArray(a) === false
.)
By "type", I mean roughly what the ECMAScript 5.1 language specification referred to as [[Class]] - i.e., that quality that separates an Array instance, with its special [[DefineOwnProperty]] behaviour, from an ordinary Object.
Examples
Some cases are pretty easy:
- Array exotic objects can be reliably detected using
Array.isArray(a)
. - Functions can be reliably detected using
typeof f === 'function'
.- But is there any way to detect if a function is a bound function, native function or closure?
- Some other exotic objects can be reliably detected by careful application of methods from their prototype object, e.g.
- Sets can be detected by calling
Set.prototype.has.apply(s, undefined)
, and seeing whether it throws a TypeError or not.
- Sets can be detected by calling
Is there any general way to make such a detection?
In particular:
- Is there any general way to determine whether an object is plain or exotic?
- Is there any general way to determine the type of an exotic object?
I note that Object.toString.apply(o)
used to work reasonably well for this purpose: although in many browsers it would lie about the type of host objects, for all the types defined in the ES 5.1 spec it could be counted on to reliably tell you whether the object was a plain [object Object]
or an exotic [object <Type>]
. In ES6 and later, however, modifying @@toStringTag
will subvert this test.
A means of detection which works in any conformant JS implementation would be ideal, but a means that is specific to Node.js would still be useful.