Using indirect calls to 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.
For read access only using hard-coded variable name strings (to prevent code insertion) you could use the pattern:
(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
}
Indirect vs direct calls to 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.
Issues
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.