The old style JavaScript var
declaration outside of a closure is global (top-level scope) and can be accessed in a browser from the window
object. For example, the declaration var x = 3;
can be accessed with window['x']
.
How do you similarly access a const
or let
declaration given the name (string) of the declaration?
var x = 3;
const y = 7;
let z = 21;
console.log('x = ' + window['x']); //x = 3
console.log('y = ' + window['y']); //y = undefined
console.log('z = ' + window['z']); //z = undefined
For the above example, how do you get the values 7
and 21
for "y" and "z" instead of undefined
?
Fiddle with the code:
https://jsfiddle.net/g78ah6we/
Edits (notes added for clarity):
1. While not typical, there are use cases, such as from within a library, that it's necessary to access a declaration based only on the name of the declaration.
2. Only read access is needed (none of the declarations will be modified).
3. The window
object is mentioned just to show the old way, but this question is not actually about using the window
object (or the global
object).
eval
Accessing global const
and let
definitions can be done using an indirect call to eval
. That is make eval
the result of a comma separated expression or assign it to a variable first. If the syntactic access is not directly to the built-in eval
function it's an indirect access, and indirect access executes in global scope.
You can also set global let
variables by building script to perform the setting operation.
"use strict";
let myVar = "global variable myVar";
console.log( myVar);
(function myLibrary() {
const myVar = "local variable myVar";
const indirectEval = eval;
var varName = "myVar";
console.log( eval(varName)); // direct call uses local scope
console.log( indirectEval(varName)); // indirect call uses global scope
var result = "\"updated global variable even though shadowed\"";
var js = varName + '=' + result;
indirectEval(js);
// but trying to define a new let variable doesn't attach to global scope
var js2 ='let letVar2 = "let variable two"';
indirectEval( js2);
})();
console.log( myVar)
console.log( "letVar2: " + typeof letVar2);
What you can't do is add a let
or const
variable to global scope using an indirect call to eval: they are block level declarations and the code eval
evaluates is considered a block - so the declarations are discarded when (indirect call to ) eval
returns.
PS. This is a technical answer. And yes, I have heard that "eval is evil" before, one or three times.
(0,eval)("identifierString");
as for example:
var x = 3;
const y = 7;
let z = 21;
{
const y = "shadow"
let z = 42;
console.log('x = ' + (0,eval)('x')); //x = 3
console.log('y = ' + (0,eval)('y')); //y = 7
console.log('z = ' + (0,eval)('z')); //z = 21
}
eval
A direct call to eval
only obtains the values of global variables that have not been shadowed in function scope of the call. This may restrict choice of variable names, or where the call can be made from, within the library.
An indirect call executes in global scope and can obtain the value of global variables irrespective of name shadowing within the library.
Creating a new Function
object from source text, and calling it, may provide be an alternative to using an indirect call to eval
in a web page. However the difference is largely semantic rather than one being better than the other.
If the global variable name (var
, let
, const
or class
identifier) comes from user input it really should be checked for validity (not all that easy) or at least accessed within a try/catch block to trap used of undeclared identifiers or use of name declarations before initialization.
Personally I would recommend finding alternatives to using global variable name strings in general. Providing a static name space object on the library (e.g. myLibrary.data
) and processing string values that are property names of the object, or including option object parameters in library calls, come to mind.