Fwd: Indirect references to variables

# John Gardner (9 years ago)

ECMAScript currently offers no clean way to "dereference" a variable in the current scope. For instance, assume an author wishes to obtain a reference to a class using a variable that holds its name:

class Paintbrush{  }

let className = "Paintbrush";

// Would only work in browsers, not NodeJS
console.log( window[className] );

// Doesn't even work in NodeJS
console.log( global[className] || this[className] );

A hacky workaround is to create an anonymous function that simply returns a reference to the named variable:

function dereference(name){
    return new Function([], "return " + name)();
}
dereference("Paintbrush") === Paintbrush; // true

This isn't an elegant solution, nor a preferable one. Another approach might be to leverage eval, which opens up the obvious issues of performance and security.

Having a way of indirectly referencing another variable would fix this:

class Paintbrush{  }

let className       = "Paintbrush";

let classReference  = \className;
console.log(classReference === Paintbrush); // true

Sticking a backslash before a bareword identifier creates a reference to an object whose name matches the identifier's string value. If no such object exists in the current scope, it simply returns undefined.

I can't see this being used in everyday programs, but it would facilitate Ajax programming considerably, where classes or functions can only be specified by name:

{"className":"Polygon", "vertices": [[0,0]...] }

let meshClass = \ajaxData.className;
if(meshClass instanceof Mesh){
    new meshClass(ajaxData.vertices);
}
# Frankie Bagnardi (9 years ago)

This is a common situation, but one easily solved by object literals. Reflecting on the scope is confusing and would hurt tooling (it's essentially eval).

var mapping = {Polygon: Polygon};
var meshClass = mapping[ajaxData.className];
# Bergi (9 years ago)

John Gardner schrieb:

ECMAScript currently offers no clean way to "dereference" a variable in the current scope.

There is one. It's called eval.

A hacky workaround is to create an anonymous function that simply returns a reference to the named variable:

 function dereference(name){
     return new Function([], "return " + name)();
 }
 dereference("Paintbrush") === Paintbrush; // true

Actually that does only work with global variables as well, quite like the property access on the global object.

Another approach might be to leverage eval, which opens up the obvious issues of performance and security.

And so does your proposed backslash operator. The only difference I can see would be that you are wishing to evaluate only identifier references not arbitrary expressions, but that can be trivially dealt with by testing the expression to match the Identifer production. A regex could do that. Neither performance nor security would be any better though.

The preferred way of what you are trying to do is to whitelist the classes you want to make available, and by naming/aliasing them explicitly. Basically

 var classReference = {Paintbrush, …}[className];
 var instance = new classReference();

For additional security, you might want to use an object that doesn't inherit from Object.prototype, or just a Map right away.

, Bergi

# John Gardner (9 years ago)

Trouble is, if one has literally hundreds of classes or functions that need to be matched, they'd rather not pool them all into one massive object literal for the sake of easier mapping. DRY principle fully relevant.

Also, yes, while it would be a pain for static type analysis, it wouldn't necessarily be the same as eval. Eval executes arbitrary code, whereas the indirect references would only point to modifiers only:

var className = \"doSomethingSinister(/etc/passwd/);"

That line would literally be looking for a property in the current scope that'd be named this:

global["doSomethingSinister(/etc/passwd/);"]
window["doSomethingSinister(/etc/passwd/);"]
# Bradley Meck (9 years ago)

Are you putting hundreds of classes into a single scope?

# John Gardner (9 years ago)

Only at top-level. This issue actually surfaced when I realised I had no way to indirectly access classes by name... and for some reason, neither the global object nor the eval hack were returning anything. It worked fine for simple variables, so I wondered if that was an intentional side-effect of classes in ECMAScript.

(Of course, if not, it may actually be an issue with V8's implementation...)

# Jordan Harband (9 years ago)

Can you not put each class in its own module, and simply require, by name, the one you want?

# John Gardner (9 years ago)

Never mind. This is beginning to south like too much of a damn edge case for it to be of language-wide relevance.

Pretend this discussion never happened.

# Robin Cafolla (9 years ago)

You're probably better off building some sort of class map into your application to handle this.

Either

A) You have all your classes loaded in one scope, you can have a map of strings to those classes which a method can retrieve and instantiate. Drawback is that your method (and the map) needs to also be defined in the same scope.

or

B) You don't have all classes in scope and are loading them dynamically (with something like requirejs), in which case your function needs a class-map of class names to paths, which your loader can pull in for you and asynchronously create. I tend towards using a service manager pattern to do this in applications where I have a requirement.

In the second instance you could introduce a compile step to create your class map. Very much dependent on your workflow. Regardless of how you're loading your classes or how you're scoping your code, some sort of class map is the solution.

The only case I can think of where loading by string really becomes an issue is with ES6 modules, which AFAIK, being static would need to all be loaded at the top of the module where you're de-serializing your classes.

When ES6 modules start landing in browsers I'll be tending even more strongly towards the service manager pattern: One known place to resolve instances and factories for classes.

,

Robin Cafolla