ES6 classes : what about instrospection?
In ES5, I could check the existence of a "class" (constructor function) on the window object:
if (window.MyClass) {
... // do something
}
In ES6, according to this article, globally-declared classes are globals, but not properties of the global object (window
, on browsers):
But there are now also global variables that are not properties of the global object. In global scope, the following declarations create such variables:
let
declarationsconst
declarations- Class declarations
So if I can't use if (window.MyClass)
, is there a way to do the same?
Actually is there a proper way to do this without using window object ?
In ES5, we could wheck the existence of a class on the window object
Only if the constructor function was a global, which is poor practice.
In ES6, according to this article, globally-declared classes are globals, but not properties of the global object...
Correct. (The same is true of let
and const
declarations at global scope.) This is defined in §8.1.1.4: Global Environment Records:
A global Environment Record is logically a single record but it is specified as a composite encapsulating an object Environment Record and a declarative Environment Record. The object Environment Record has as its base object the global object of the associated Realm. This global object is the value returned by the global Environment Record’s GetThisBinding concrete method. (E.g., the global object referenced by
window
on browsers — T.J.) The object Environment Record component of a global Environment Record contains the bindings for all built-in globals (clause 18) and all bindings introduced by a FunctionDeclaration, GeneratorDeclaration, or VariableStatement contained in global code. The bindings for all other ECMAScript declarations in global code are contained in the declarative Environment Record component of the global Environment Record.
(My emphasis) So the things that used to go on the global object in ES5 and earlier still do (plus generators, because it would have been even more confusing if they didn't), but the new things (let
, const
, and class
declarations) don't. They're globals, but not properties of the global object.
Back to your question...
So if I can't use
if (window.MyClass)
, is there a way to do the same?
You could use
if (typeof MyClass === "function") {
...since typeof
on an unresolvable symbol doesn't throw a ReferenceError
. This also has the advantage of checking whether MyClass
is in scope for the code, even if it's not global.
There's a gotcha there though: If that code is in the same scope where MyClass
is declared via class
(or let
or const
) but it's above MyClass
in that scope, even the typeof
check will throw a ReferenceError
, because you can't access the binding it creates at all (not even with typeof
) before the class
(or let
or const
).
E.g., this will throw:
if (typeof MyClass === "function") { // ReferenceError here
// Yup, it's defined
// ...
}
// ...
class MyClass {
}
The space from the beginning of the scope to the class
, let
, or const
line is called the temporal dead zone (TDZ) and you can't access the variable binding at all. Consequently, you have to catch the ReferenceError
:
let exists = false;
try {
exists = typeof MyClass === "function";
} catch (e) {
}
Actually is there a proper way to do this without using window object ?
Until JavaScript modules make it to broad browser support, there are a couple of ways:
Use an Asynchronous Module Definition library of some kind to handle loading your modules. Some examples: RequireJS, SystemJS, CommonJS
-
Have a single global variable that you'll use to refer to an object, and make your various application globals properties of that object. Here's a typical way to do that:
var MyApp = MyApp || {}; if (!MyApp.ThisModule) { // You can leave this `if` out // if there's no chance of the file // being loaded more than once MyApp.ThisModule = function(module) { module.MyClass = class MyClass { // ...class definition here... } }({}); }
This also gives you a handy scope (the anonymous function) in which to put any module-level globals.