Sunday, February 19, 2017

Is there a simple method of turning a global var into a local var?

Leave a Comment

Let's say we have a function that looks like this:

const fn = () => x; 

This function should return the value of x where x is available in the global scope. Initially this is undefined but if we define x:

const x = 42; 

Then we can expect fn to return 42.

Now let's say we wanted to render fn as a string. In JavaScript we have toString for this purpose. However let's also say we wanted to eventually execute fn in a new context (i.e. using eval) and so any global references it uses should be internalized either before or during our call to toString.

How can we make x a local variable whose value reflects the global value of x at the time we convert fn to a string? Assume we cannot know x is named x. That said we can assume the variables are contained in the same module.

4 Answers

Answers 1

You can use OR operator || to concatenate current value of x to fn.toString() call

const fn = () => x;  const x = 42;  const _fn = `${fn.toString()} || ${x}`;  console.log(_fn, eval(_fn)());

Answers 2

If you want lock certain variables while converting function to string, you have to pass that variables along the stringified function.

It could be implemented like this (written with types -- typescript notation)

const prepareForEval =    (fn: Function, variablesToLock: { [varName: string]: any }): string => {     const stringifiedVariables = Object.keys(variablesToLock)         .map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`);      return stringifiedVariables.join("") + fn.toString();   } 

Then use it like this

const stringifiedFunction = prepareForEval(someFunction, { x: x, y: y })  // you can even simplify declaration of object, in ES6 you simply write const stringifiedFunction = prepareForEval(someFunction, { x, y }) // all variables you write into curly braces will be stringified // and therefor "locked" in time you call prepareForEval() 

Any eval will declare stringified variables and funtion in place, where it was executed. This could be problem, you might redeclare some variable to new, unknown value, you must know the name of stringified function to be able to call it or it can produce an error, if you redeclare already declared const variable.

To overcome that issue, you shall implement the stringified function as immediatelly executed anonymous function with its own scope, like

const prepareForEval =    (fn: Function, variablesToLock: { [varName: string]: any }): string => {     const stringifiedVariables = Object.keys(variablesToLock)         .map(varName => `var ${varName}=${JSON.stringify(variablesToLock[varName])};`);      return       `var ${fn.name} = (function() {         ${stringifiedVariables.join("")         return ${fn.toString()};       `;)();   } 

this modification will declare function and variables in separate scope and then it will assign that function to fn.name constant. The variables will not polute the scope, where you eval, it will just declare new fn.name variable and this new variable will be set to deserialized function.

Answers 3

do you mean this? only answer can post code, so I use answer

var x = 42    function fn() {    return x  }      (() => {    var x = 56    var localFn = eval('"use strict";(' + fn.toString()+')')    console.log(localFn)    console.log(localFn())  })()

  • why rename to localFn, if you use var fn=xx in this scope the outer fn never exists!
  • in nodejs? refer nodejs vm
  • passing context? you can not save js context unless you maintain your own scope like angularjs

Answers 4

If you're already "going there" by using eval() to execute fn() in the new context, then why not define the function itself using eval()?

eval('const fn = () => ' + x + ';')

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment