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.
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.
But is there any way to detect if a function is a bound function, native function or closure?
No. They are all plain and simple functions.
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.
You can use the instanceof operator to test if an object is an instance of a particular function constructor (or something up its prototype chain).
var mySet = new Set();
console.log(mySet instanceof Set);
Related
Is it true to say that all objects (besides some custom objects), has a prototype object, in JavaScript?
I would bet it is indeed this way (because all out of the box objects likely to inherit properties from a prototype object besides some custom ones), but I'm not sure in that 100%.
Edit:
I asked this after reading in first edition of JavaScript: The good parts, book about prototypes - The chapter starts:
Every object is linked to a prototype object.
But it seemed a bit wired to me after what I read on objects in various places and I also read some definitions for prototype that were unclear to me; I wanted to see if I have a primal, basic of the concept of "prototype" in JS.
Nearly all objects in JavaScript are instances of Object; a typical object inherits properties (including methods) from Object.prototype, although these properties may be shadowed (a.k.a. overridden). However, an Object may be deliberately created for which this is not true (e.g. by Object.create(null)), or it may be altered so that this is no longer true (e.g. with Object.setPrototypeOf).
Javascript MDN
No, it's false.
You can create an object without prototype:
var obj = Object.create(null);
To check whether an object has a prototype, you can do
console.log(Object.getPrototypeOf(someObject));
I was recently reading some tutorials of JavaScript, and every article states that Functions descend from Objects.
The above statements leads to an understanding that Objects were created before Functions were available.
If it is so, then how can constructor of an Object be a function (As per my understanding object Function hasn't been created yet)?
Please help me out with this confusion.
Functions Descend from Objects
In JavaScript functions are basically objects.
They are what is called first class objects - meaning they can do anything that an object can and are basically interchangeable.
Therefore all properties and methods available on the object level are also available on the function level.
You might want to read this
How can constructor of an Object be a function
The short answer is that you are trying to understand something that is irrelevant to understand in order for you to use the Javascript language for coding.
The longer answer is that you must distinguish between built-in objects:
Object
Function
Array
and the objects you create yourself as part of your code.
The built-in objects are part of the Javascript language and are simply available to you out-of-the-box. Your question relates to the internal workings of the language as you are inquiring about how the built-in Object is constructed. This has little value to understand unless you are looking to become part of the team developing the Javascript language.
The objects you create yourself as part of your code is almost always linked to other objects via the objects internal [[Prototype]] property. So if you for example do:
var x = {"foo": "bar"};
you have now created your own object with the property foo and the value bar. You can then do:
console.log(x.hasOwnProperty("foo")); //true
even though x does not have a method called .hasOwnProperty() directly on it. The reason the above code works is, that x is linked to the built-in Object.prototype via x's internal [[Prototype]] property and via this link delegates to Object.prototype.hasOwnProperty(). So all the objects you create as part of your code has access to use the methods on Object.prototype.
I was reading over the draft for ES6, and I noticed this note in the Object.prototype.toString section:
Historically, this function was occasionally used to access the string
value of the [[Class]] internal property that was used in previous
editions of this specification as a nominal type tag for various
built-in objects. This definition of toString preserves the ability to
use it as a reliable test for those specific kinds of built-in objects
but it does not provide a reliable type testing mechanism for other
kinds of built-in or program defined objects.
From reading this thread on es-discuss, it sounds like [[Class]] is being replaced with [[NativeBrand]] in the ES6 draft so that they can specify it as being non-extensible (those were at least Allen Wirfs-Brock's thoughts).
Curious, I ran a quick test in FireFox and Chrome (with experimental JavaScript enabled):
Object.prototype.toString.apply(new WeakMap());
=> '[object WeakMap]'
"WeakMap" is not one of the [[NativeBrand]]s specified in the ES6 draft. However, this test returned "[object WeakMap]" on both browsers.
So I'm confused. I have a few questions.
1. Do Chrome and Firefox behave correctly?
From one way of reading the draft it sounds like they should return [object Object] (and all of this is pretty new, so I wouldn't be surprised to see this change in future editions of these browsers). However, it's hard for me to understand the intention of this section of the draft, especially since there are some places with "???".
Does anyone who has been following es-discuss more fervently have any relevant information? Or anyone who can understand the draft language better?
2. Is there an alternative to Object.prototype.toString?
From the note quoted above it makes it sound as if Object.prototype.toString is retained for legacy reasons, as if there's something new now that should be used instead. Especially the part of the node that reads "it does not provide a reliable type testing mechanism for other kinds of built-in ... objects". Does that mean that future built-ins can't be tested with this method?
Let's use a concrete example.
If I want to ensure an object I have received from an unknown source is a String object (an actual constructed String object, not a primitive string), I could do:
if (Object.prototype.toString.apply(unknownObject) != '[object String]')
throw new TypeError('String object expected.');
This lets me know if unknownObject is a String object no matter what frame it was constructed in.
My question is, should this be the approach I take moving forward into ES6? Or is there an alternative? Something like Object.getNativeBrandOf?
3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?
Will this work?
if (Object.prototype.toString.apply(unknownObject) != '[object Symbol]')
throw new TypeError('Symbol expected.');
...assuming Symbol is the eventual name for Private Names.
Should I use this?
if (Object.prototype.toString.apply(unknownObject) != '[object WeakMap]')
throw new TypeError('WeakMap expected.');
... or something else?
The reason I ask is I am currently writing code that I want to be able to transition as easily as possible to ES6 in a year or two when possible. If there is a replacement for Object.prototype.toString, then I can just shim it in and continue from there. Thanks!
Update
benvie's answer provided me with the correct term to search for and understand the answer to my questions.
I found an email from Allen Wirfs-Brock on es-discuss concerning this issue.
Here's what I found, for anyone else asking the same questions:
1. Do Chrome and Firefox behave correctly?
Yes, why is explained below.
2. Is there an alternative to Object.prototype.toString?
As it is now, there will be a couple "alternatives" in the sense of possibilities, but not in the sense of replacements.
a. Using the ##toStringTag symbol. However, my understanding is that Object.prototype.toString should still probably be used. ##toStringTag is provided to allow extending the results that can be returned from Object.prototype.toString. If you have a prototype you would like to add your own string tag to, you could use ##toStringTag to set the value to any string. Object.prototype.toString will return this value except in the case where this value is one of the ES5 built-ins, in which case the string tag will be prepended with '~'.
b. Using private symbols on user-defined objects. I read one email promoting this as the best way to do the same type of check on a user-defined object. However, I don't see how that really settles the issue, as I fail to understand how it could be a cross-frame solution and it doesn't let you check against ES6 built-ins.
So even though there are some alternatives, it's good to stick with Object.prototype.toString now and going forward, with one caveat:
It'll work to make sure you have an ES5 built-in, such as String, but it won't be fool-proof to make sure you have an ES6 built-in because they can be spoofed with ##toStringTag. I'm not sure why this is, and I may be missing something, or it could change as the spec evolves.
3. Since [[NativeBrand]] seems like it won't include future types of objects, how would one test for these objects?
As mentioned above, Object.prototype.toString can still be used on ES6 built-ins, but it's not fool-proof as it can be spoofed by anyone with access to the ##toStringTag symbol. However, maybe there shouldn't be a fool-proof method, since Object.prototype.toString(weakmap) == '[object WeakMap]' doesn't mean that weakmap instanceof WeakMap (and it shouldn't!). The weakmap could have come from another frame, or it could be a user-created weakmap-like object. The only thing you really know is it reports to be functionally equivalent to a WeakMap.
It does seem to beg the question why you can't have a user-defined object which reports to be functionally equivalent to a String or Array (sans the prefixed "~").
This is currently a moving target in the ES6 spec. For the existing set of objects, the existing mechanics are maintained for various reasons including compatibility. In the most recent ES6 spec, published October 26th, you can find some hints of the potential future direction
15.4.6.2.4 ArrayIterator.prototype.##toStringTag
The initial value of the ##toStringTag property is the
string value "Array Iterator".
15.14.5.13 Map.prototype.##toStringTag
The initial value of the ##toStringTag property is the string value "Map".
You can find the original discussion that originated this in this thread on es-discuss
I am currently learning advanced JavaScript, with an aim to build a standards compliant (HTML5, CSS3, ESv5) library. Along my way I have already asked a couple of related questions to try and figure out where to start, what to do, what not to do, what to avoid etc. I have already begun reading the ECMA-262 (ECMAScript version 5) documentation, and have been running a few tests before I get started on development work.
Previous questions:
Writing ECMAScript5 compliant code
What's the difference between JavaScript, JScript & ECMAScript?
In my research I found out that different browsers implement the standard differently, and in that respect, they implement different objects. For example, IE implements an object called ActiveXObject, but this is not the case in FireFox. So I wrote a little test facility which determines if something is defined within the browser.
Consider the following which tests a few known objects (including jQuery since this is not built in).
Again, I have reached a point where I am in need of help:
Questions:
Given the example above, what is the difference between an object and a function?
Do I write functions or objects in ES/JS?
Why is Object a function and not an object?
Is there any hierarchical structure to built in objects / functions?
Can built in objects / functions be redefined as something entirely different?
Can built in objects / functions be undefined?
Can built in objects / functions be assigned new features if they do not already support them natively?
If an object is defined in one browser and not another, how can I compensate for this?
P.S. I do not want answers relating to specific implementations (JavaScript/JScript), rather answers relating to the standard (ECMAScript v5). Thanks in advance!
Given the example above, what is the difference between an object and a function?
In Chrome, all these items are functions. In general however, a function is an object with the addition that it holds code and that you can call it. So, you can also just add properties to functions (like jQuery does: $("selector") or $.ajax).
Do I write functions or objects in ES/JS?
Well, obviously that depends on what you code. function() {} gives you a function; {} gives you an object. (Again, functions are objects in the end.)
Why is Object a function and not an object?
Object is a function because you can call it, either as a constructor or not:
Object(); // returns an empty object
new Object(); // same
Also, given that almost everything is an instance of Object, it follows that Object is a constructor and thus a function. (Note again that functions are also objects.)
Is there any hierarchical structure to built in objects / functions?
As for the ECMAScript built-in objects, there is in a sense. There are constructor functions (String) on the global object, functions for instances (Array.prototype.forEach), and "static" functions (Object.defineProperty which is meant to be used on objects, Array.isArray for arrays).
Can built in objects / functions be redefined as something entirely different?
Sure, you can do Object = null. But any code relying on Object will start throwing exceptions, so it's not recommended at all.
Can built in objects / functions be undefined?
No, an object is not undefined by definition. undefined is not an object and vice-versa. This holds for any object.
Can built in objects / functions be assigned new features if they do not already support them natively?
Yes, if e.g. Array.prototype.forEach does not exist, you could set it yourself. But it should be noted that such functions turn up in for(var key in arr) loops which again can cause code to behave differently. This can be solved using Object.defineProperty by using {enumerable: false}. But there is another caveat: the function is shared across the whole environment (e.g. the current page). If other code is also setting them you're experiencing collisions.
If an object is defined in one browser and not another, how can I compensate for this?
You can "shim" such functions. For e.g. ES5 functions such as Array.prototype.forEach there are shims available which make them available on older browsers as well. Underscore.js may be a good example.
Given the example above, what is the difference between an object and a function?
A function is just an object which is callable. However, I guess you ask for the types of host objects (Node, HTMLCollection etc): Their behaviour is implementation-dependent ("not ecmascript-native") - you can't rely on anything.
Do I write functions or objects in ES/JS?
Huh? You write code, which can be interpreted.
Why is Object a function and not an object?
Object is the native object constructor, and therefore a function (and also an Object).
Is there any hierarchical structure to built in objects / functions?
Do you ask for "Everything is an Object"? If you ask for the structure of DOM interfaces: They are implementation-dependent host objects again, but most implementors have a inheritance system based on the DOM specification.
Can built in objects / functions be redefined as something entirely different? Can built in objects / functions be undefined?
No. You can overwrite the global variables pointing to them (the properties of the global object), but every instance will nevertheless be constructed from the native (then [nearly] unaccessible) constructors.
Can built in objects / functions be assigned new features if they do not already support them natively? If an object is defined in one browser and not another, how can I compensate for this?
Yes, you can extend the native objects and their prototypes. But watch out for host objects, they might not like it. If an object is defined only in certain environments, you can easily test for its existance and possibly shim it (es5, html5).
As part of my research into ECMAScript / JavaScript, I have found the following resource which provides a lot of information regarding the JS DOM.
http://krook.org/jsdom/index-all.html
I don't understand the difference between native objects and host objects in JavaScript. Does the latter simply refer to non-primitive function objects that were created by a custom constructor (e.g., var bird1 = new Bird();)?
Both terms are defined in the ECMAScript specification:
native object
object in an ECMAScript implementation whose semantics are fully
defined by this specification rather than by the host environment.
NOTE Standard native objects are defined in this specification. Some
native objects are built-in; others may be constructed during the
course of execution of an ECMAScript program.
Source: http://es5.github.com/#x4.3.6
host object
object supplied by the host environment to complete the
execution environment of ECMAScript.
NOTE Any object that is not native is a host object.
Source: http://es5.github.com/#x4.3.8
A few examples:
Native objects: Object (constructor), Date, Math, parseInt, eval, string methods like indexOf and replace, array methods, ...
Host objects (assuming browser environment): window, document, location, history, XMLHttpRequest, setTimeout, getElementsByTagName, querySelectorAll, ...
It is more clear if we distinguish between three kinds of objects:
Built-in objects: String, Math, RegExp, Object, Function etc. - core predefined objects always available in JavaScript. Defined in the ECMAScript spec.
Host objects: objects like window, XmlHttpRequest, DOM nodes and so on, which is provided by the browser environment. They are distinct from the built-in objects because not all environment will have the same host objects. If JavaScript runs outside of the browser, for example as server side scripting language like in Node.js, different host objects will be available.
User objects: objects defined in JavaScript code. So 'Bird' in your example would be a user object.
The JavaScript spec groups built-in objects and user objects together as native objects. This is an unorthodox use of the term "native", since user objects are obviously implemented in JavaScript while the built-ins is most likely implemented in a different language under the hood, just as the host objects would be. But from the perspective of the JavaScript spec, both builtins and user objects are native to JavaScript because they are defined in the JavaScript spec, while host objects are not.
Here's my understanding of the spec.
This:
var bird = new Bird();
...results in a native Object that simply happened to be created using the new operator.
Native objects have an internal [[Class]] property of one of the following:
"Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String".
For your bird1 it will be:
"Object"
Just like if you create a function:
function my_func() {
// ...
}
...my_func isn't defined in ECMAScript, but it is still a native object with the internal [[Class]]:
"Function"
A host object is an object provided by the environment in order to serve a specific purpose to that environment not defined in by the specification.
For example:
var divs = document.getElementsByTagName('div')
The object referenced by divs is a NodeList, which is integrated into the environment in such a manner that it feels like a regular JavaScript object, yet it isn't defined anywhere by the specification.
Its internal [[Class]] property is:
"NodeList"
This provides implementation designers some flexibility in suiting the implementation to the specific need of the environment.
There are requirements of host objects that are defined throughout the spec.
In addition to the other answers regarding Host Objects.
Host objects are specific to a environment. So next to the browsers' host objects, there are also specific objects in nodejs.
For the sake of the example, first starting with the Standard objects as defined in Javascript. Then the common objects for the Browser/DOM. Node has it's own Objects.
Standard Javascript built-in object examples:
Object
Function
Boolean
Symbol
Number
Math
... (See full list on MDN web docs)
Host Objects Document Object Model Examples:
Window
Document
History
... (See full list on DOM objects on MDN web docs)
XMLHttpRequest (part of Web API)
... (See full list Web API on MDN web docs)
Host Objects in Node.js:
http
https
fs
url
os
... (See full list on nodejs.org)
Could not see a convincing answer to the question whether var bird1 = new Bird(); is a native or host object. Assuming Bird is a user defined function, a native non-built-in object will be created according to http://es5.github.io/#x13.2 by the javascript implementation. In contrast, native built-in objects will be present since the start of a javascript program (such as Object and many others). A difference between a native object and a host object is that former is created by the javascript implementation and the latter is provided by the host environment. As a result host object internal [[class]] property can be different from those used by built-in objects (i.e. "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", and "String").
Also, worthwhile noting that ECMA6 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf does not use the terminology native and host objects any more. Instead, it defines below object types, with more clear explanations of their intended behaviour.
4.3.6 ordinary object
object that has the default behaviour for the essential internal methods that must be supported by all objects
4.3.7 exotic object
object that does not have the default behaviour for one or more of the essential internal methods that must be supported by all objects
NOTE
Any object that is not an ordinary object is an exotic object.
4.3.8 standard object
object whose semantics are defined by this specification
4.3.9 built-in object
object specified and supplied by an ECMAScript implementation
Considering three objects: Host, Native, Custom.
Host Objects are created by the environment and are environment specific. Best known environment would be a web-browser but could be another platform. The host objects created in web-browser could be the window object or the document. Typically a browser uses an API to create Host Objects to reflect the Document Object Model into JavaScript. (Webbrowser have different JavaScript Engines that do this) A host object is created automatically the moment the page renders in a browser.
A Native Object is created by the developer using predefined classes of JavaScript. Native Objects are in your written script.
Than, a Custom Object is made by the developer from a custom (not predefined, or partially predefined) class.
Native objects are objects that adhere to the specs, i.e. "standard objects".
Host objects are objects that the browser (or other runtime environment like Node) provides.
Most host objects are native objects, and whenever you instantiate something using new, you can be 99.99% sure that it is a native object, unless you mess around with weird host objects.
This notion has been introduced due to the presence of very bizarre objects in IE(and other old browsers?). For example:
typeof document.all == "undefined"; // true
document.all.myElementId; // object
When seeing this, everyone would agree that document.all is clearly "non-standard", and thus a non-native host object.
So why not call native objects standard objects in the first place? Simple: after all, the Standard(!) document talks about non-native objects too, and calling them non-standard would lead to a paradox.
Again:
native == "standard"
host == provided by the browser or Node or …
most host objects are native, and all non-host objects are native too
This may be overkill, but for simplicity a native object is one that exist and is usable in any environment that implements an ECMAScript compliant engine. This is usually (but not always) a browser.
So, your Internet Explorer or your Google Chrome, doesn't make the String object available to you, for example. The reason you can use the String object is because it is "native" (built-in) to the JavaScript language itself.
However, if you'd like to create a pop-up window, you'll need to use the window object. The window object is provided by the browser software itself, so it is not native to JavaScript, but it is part of the "Browser Object Model" or the BOM.