Es6 Classes:What About Instrospection

ES6 classes : what about instrospection?

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:

  1. Use an Asynchronous Module Definition library of some kind to handle loading your modules. Some examples: RequireJS, SystemJS, CommonJS

  2. 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.

What is concept of reflection in JavaScript?

reflection is a part of metaprogramming.

Metaprogramming is a programming technique in which computer programs have the ability to treat programs as their data. It means that a program can be designed to read, generate, analyse or transform other programs, and even modify itself while running.

so concept of reflection is that, just like we see our reflection in the mirror: we can see things we couldnt see without it, such as: our hair, our lips, tongue, etc.

in short, if a method or a class call this reflection stuff, it can see objects outside its knowledge, such as: instance variables, list of methods the class has, and other properties, what class called this method.

this is useful when we do metaprogramming. as method or class should be aware of things outside their (or even writer's) knowledge.

concrete example:
lets say water and a cup. if you want to program water physics to fit cup, there are many ways. lets say you work with 100 other developers and have no idea what kind of container they would come up with (and someone might end up with a lake or even river) and you choose metaprogramming style. you have class water, and class cup. your water class has to be aware of properties of cup such as: max volume, weight, curves, or even max/min temperature.

result: you wont have to insert those informations as parameters whenever someone call class water as it might be difficult to track if they are growing in number.

(im not sure if that is a good enough example and explanation, so please comment a better one if it happen to cross your mind)

Have private properties & methods in ES6 classes

It seems like classes in ES6 doesn't explicitly provide anything to have private data or methods.

Correct. The class syntax is for normal classes with prototype methods. If you want private variables, you put them in the constructor as always:

class Deferred {
constructor() {
// initialise private data
var isPending = true;
var handlers = {
resolve: [],
reject: [],
notify: []
};

// Private method
function trigger(event, params) {
...
}

// initialise public properties
this.promise = new Promise(this);

// and create privileged methods
this.resolve = trigger.bind(null, 'resolve');
this.reject = trigger.bind(null, 'reject');
this.notify = trigger.bind(null, 'notify');

this.on = function(event, handler) {

};
}
}

Get the class name of ES6 class instance

someClassInstance.constructor.name is exactly the correct way to do this. Transpilers may not support this, but it is the standard way per the specification. (The name property of functions declared via ClassDeclaration productions is set in 14.5.15, step 6.)

Get functions (methods) of a class

This function will get all functions. Inherited or not, enumerable or not. All functions are included.

function getAllFuncs(toCheck) {
const props = [];
let obj = toCheck;
do {
props.push(...Object.getOwnPropertyNames(obj));
} while (obj = Object.getPrototypeOf(obj));

return props.sort().filter((e, i, arr) => {
if (e!=arr[i+1] && typeof toCheck[e] == 'function') return true;
});
}

Do test

getAllFuncs([1,3]);

console output:

["constructor", "toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "entries", "keys", "constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__", "__lookupSetter__"]

Note

It doesn't return functions defined via symbols;

Create instance of Class Using Reflection in JavaScript

JavaScript doesn't have classes. But if by "class" you mean you have a constructor function:

function MyClassName() {
// do constructor things here
}

But the name of that function is in a variable:

var someclass = "MyClassName";

Then you can instantiate an instance like this:

var obj = new window[someclass]();

The above only works if MyClassName is in the global scope.

What does the Reflect object do in JavaScript?

UPDATE 2015:
As pointed out by 7th's answer, now that ES6 (ECMAScript 2015) has been finalized, more appropriate documentation is now available:

  • ES6 spec, Reflection
  • MDN Reflect (including details and examples to all of its methods)

**Original answer (for (historic) understanding and extra examples)**:

The Reflection proposal seems to have progressed to the Draft ECMAScript 6 Specification. This document currently outlines the Reflect-object's methods and only states the following about the Reflect-object itself:

The Reflect object is a single ordinary object.

The value of the [[Prototype]] internal slot of the Reflect object is the standard built-in Object prototype object (19.1.3).

The Reflect object is not a function object. It does not have a [[Construct]] internal method; it is not possible to use the Reflect object as a constructor with the new operator. The Reflect object also does not have a [[Call]] internal method; it is not possible to invoke the Reflect object as a function.

However, there is a short explanation about it's purpose in ES Harmony:

The “@reflect” module serves multiple purposes:
  • Now that we have modules, a “@reflect” module is a more natural place for many of the reflection methods previously defined on Object.
    For backwards-compatibility purposes, it is unlikely that the static methods on Object will disappear. However, new methods should likely be added to the “@reflect” module rather than to the Object constructor.
  • A natural home for proxies, avoiding the need for a global Proxy binding.
  • Most methods in this module map one-to-one onto Proxy traps. Proxy handlers need these methods to conveniently forward operations, as shown below.


So, the Reflect object provides a number of utility functions, many of which appear to overlap with ES5 methods defined on the global Object.

However, that doesn't really explain what existing problems this intends to solve or what functionality is added. I suspected this could be shimmed and indeed, the above harmony-spec links to a 'non-normative, approximate implementation of these methods'.

Examining that code could give (further) idea's about it's use, but thankfully there is also a wiki that outlines a number of reasons why the Reflect object is useful:

(I've copied (and formatted) the following text for future reference from that source as they are the only examples I could find. Besides that, they make sense, already have a good explanation and touch the question's apply example.)


More useful return values

Many operations in Reflect are similar to ES5 operations defined on Object, such as Reflect.getOwnPropertyDescriptor and Reflect.defineProperty. However, whereas Object.defineProperty(obj, name, desc) will either return obj when the property was successfully defined, or throw a TypeError otherwise, Reflect.defineProperty(obj, name, desc) is specced to simply return a boolean that indicates whether or not the property was successfully defined. This allows you to refactor this code:

try {
Object.defineProperty(obj, name, desc);
// property defined successfully
} catch (e) {
// possible failure (and might accidentally catch the wrong exception)
}

To this:

if (Reflect.defineProperty(obj, name, desc)) {
// success
} else {
// failure
}

Other methods that return such a boolean success status are Reflect.set (to update a property), Reflect.deleteProperty (to delete a property), Reflect.preventExtensions (to make an object non-extensible) and Reflect.setPrototypeOf (to update an object's prototype link).


First-class operations

In ES5, the way to detect whether an object obj defines or inherits a certain property name is to write (name in obj). Similarly, to delete a property, one uses delete obj[name]. While dedicated syntax is nice and short, it also means you must explicitly wrap these operations in functions when you want to pass the operation around as a first-class value.

With Reflect, these operations are readily defined as first-class functions:

Reflect.has(obj, name) is the functional equivalent of (name in obj) and Reflect.deleteProperty(obj, name) is a function that does the same as delete obj[name].


More reliable function application

In ES5, when one wants to call a function f with a variable number of arguments packed as an array args and binding the this value to obj, one can write:

f.apply(obj, args)

However, f could be an object that intentionally or unintentionally defines its own apply method. When you really want to make sure that the built-in apply function is called, one typically writes:

Function.prototype.apply.call(f, obj, args)

Not only is this verbose, it quickly becomes hard to understand. With Reflect, you can now make a reliable function call in a shorter and easier to understand way:

Reflect.apply(f, obj, args)


Variable-argument constructors

Imagine you want to call a constructor function with a variable number of arguments. In ES6, thanks to the new spread syntax, it will be possible to write code like:

var obj = new F(...args)

In ES5, this is harder to write, because one can only use F.apply or F.call to call a function with a variable number of arguments, but there is no F.construct function to new the function with a variable number of arguments. With Reflect, one can now write, in ES5:

var obj = Reflect.construct(F, args)


Default forwarding behavior for Proxy traps

When using Proxy objects to wrap existing objects, it is very common to intercept an operation, do something, and then to "do the default thing", which is typically to apply the intercepted operation to the wrapped object. For example, say I want to simply log all property accesses to an object obj:

var loggedObj = new Proxy(obj, {
get: function(target, name) {
console.log("get", target, name);
// now do the default thing
}
});

The Reflect and Proxy APIs were designed in tandem, such that for each Proxy trap, there exists a corresponding method on Reflect that "does the default thing". Hence, whenever you find yourself wanting to "do the default" thing inside a Proxy handler, the correct thing to do is to always call the corresponding method in the Reflect object:

var loggedObj = new Proxy(obj, {
get: function(target, name) {
console.log("get", target, name);
return Reflect.get(target, name);
}
});

The return type of the Reflect methods is guaranteed to be compatible with the return type of the Proxy traps.


Control the this-binding of accessors

In ES5 it's fairly easy to do a generic property access or property update. For instance:

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update

The Reflect.get and Reflect.set methods allow you to do the same thing, but additionally accept as a last optional argument a receiver parameter that allows you to explicitly set the this-binding when the property that you get/set is an accessor:

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)

This is occasionally useful when you're wrapping obj and you want any self-sends within the accessor to get re-routed to your wrapper, e.g. if obj is defined as:

var obj = {
get foo() { return this.bar(); },
bar: function() { ... }
}

Calling Reflect.get(obj, "foo", wrapper) will cause the this.bar() call to get rerouted to wrapper.


Avoid legacy __proto__

On some browsers, __proto__ is defined as a special property that gives access to an object's prototype. ES5 standardized a new method Object.getPrototypeOf(obj) to query the prototype. Reflect.getPrototypeOf(obj) does exactly the same, except that Reflect also defines a corresponding Reflect.setPrototypeOf(obj, newProto) to set the object's prototype. This is the new ES6-compliant way of updating an object's prototype.

Note that: setPrototypeOf also exists on Object (as correctly pointed out by Knu's comment)!


EDIT:

Side-note (addressing comments to the Q): There is a short and simple answer on 'Q: ES6 Modules vs. HTML Imports' that explains Realms and Loader objects.

Another explanation is offered by this link:

A realm object abstracts the notion of a distinct global environment,
with its own global object, copy of the standard library, and
"intrinsics" (standard objects that are not bound to global variables,
like the initial value of Object.prototype).

Extensible web: This is the dynamic equivalent of a same-origin
<iframe> without DOM.

Worth mentioning though: all this is still in draft, this is not a specification etched in stone! It's ES6, so keep browser-compatibility in mind!

ES6: If class has method?

I think you're looking for

build (methodnames) {
for (const methodname of methodnames) {
if (typeof this[methodname] == "function") {
this[methodname]();
}
}
}

There's nothing special about classes - and in fact you should ignore them. If you want to call some method, the only thing that is important is that there is a function as a property value. It doesn't matter whether the method is an own property of the prototype object of the class that created the instance.

Can I use in Google Apps Scripts a defined Class in a library with ES6 (V8)?

As written in the official documentation,

Only the following properties in the script are available to library users:

  • enumerable global properties

    • function declarations,
    • variables created outside a function with var, and
    • properties explicitly set on the global object.

This would mean every property in the global this object are available to library users.

Before ES6, All declarations outside a function (and function declaration themselves) were properties of this global object. After ES6, There are two kinds of global records:

  • Object record- Same as ES5.

    • Function declarations
    • Function generators
    • Variable assignments
  • Declarative record - New

    • Everything else - let, const, class

Those in the declarative record are not accessible from the global "object", though they are globals themselves. Thus, the class declaration in the library is not accessible to library users. You could simply add a variable assignment to the class to add a property to the global object(outside any function):

var Update = class Update{/*your code here*/}

References:

  • Library official documentation
  • Global environment records
  • Related Answers:

    • ES6- What about introspection
    • Do let statements create properties on the global object


Related Topics



Leave a reply



Submit