Is it possible to change the scope of the function after the new function ()?

So, I have this code:

function fn(a){
  var f=(new Function("return a"));
  return f();
}
fn(7)//ReferenceError: a is not defined

      

Same problems with local variables:

function fn(){
  var a=7;
  var f=new Function("return a");
  return f();
}
fn(7)//ReferenceError: a is not defined

      

I want it to return but the new function can not, it can only see the global a

var a=1;
function fn(a){
  var f=(new Function("return a"));
  return f();
}
fn(7)//1

      

With normal initialization, the function can see the argument.

function fn(a){
  var f=function(){return a};
  return f();
}
fn(7)//7

      

I need to call the basic constructor on my project and not use global variables. I know I can solve this problem by providing arguments to the newly created function and call it like this:

function fn(a){
  var f=(new Function('a',"return a"));
  return f(a);
}
fn(7)//7

      

And also can use some parsing function and some dumb method to make the input arguments available like this:

function parsargs(funct){
   //some parsing methodes giving back argument name list from funct.toString()
  return "['a','b']";//like this
}


function fn(a,b){
  var arrgstr,retfunc;

  arrgstr="";
  for(var i in arguments)
  {
    if(i<arguments.length-1)
      arrgstr+=arguments[i]+",";
    else
      arrgstr+=arguments[i];
  }
  //return arrgstr;
  retfunc="var f=new Function("+parsargs()+",'return b*a');return f("+arrgstr+")";
  return (new Function(retfunc))();
}
fn(7,4)//28

      

But there must be an easier way that achieves local variables and functions as well ... Any suggestions?

PS: I am trying to replace eval () in a project

Here is a simplified version of my original problem: fiddle

Answer: NO ...

+3


source to share


4 answers


You could call

create a new function with a context that refers to the required local variables:



function f(a) {
  var b = 30;
  return new Function("return this.a + this.b").call({ a: a, b: b })
}

f(10) // 40

      

+1


source


Your exact question is not clear, but if you can use arguments.callee

(ie lax mode) and that you want to have argument names in fn

, you can "do":

function fn(a,b){
  var strargs = arguments.callee.toString().match(/\(([^\)]*)\)/)[1];
  return (new Function(strargs.split(","),"return a+b")).apply(null,arguments);
}
console.log(fn(7, 3)) // 10

      



But I have a strong feeling that this is an XY question and that we could provide a more useful answer knowing that the real original problem needs to be solved.

+2


source


The reason it's not as easy as you'd like is because JavaScript doesn't blindly place variables in scope. Instead, it parses the body of the function (as much as possible) and determines what variables the code will require. It will then look for variables in external scopes, create references for them, and attach those references to the new function. This is what keeps the variables from the outer scope available inside the function when it is eventually executed.

You want to build a function from the body of a string. JavaScript cannot determine which variables will be used by the body, and therefore it will not make those variables available.

To make it work for function arguments use apply():

:

function fn(a){
   var argref = arguments; // create reference to the arguments of fn() and hence to a or any other arguments
   var func = new Function("return arguments[0]");
   var wrapper = function() {
      return func.apply(null, argref);
   };
   return wrapper;
}

      

Note that you still cannot refer to arguments by name, as the code never specifies the names of the argument names func

- JavaScript cannot magically read your mind and do what you want. If you want to access the arguments by name, you need to give the interpreter names.

This question has some code on how to determine names from a function reference: How to get parameter names / values ​​dynamically from javascript

I don't see a way to make local variables available without passing them fn()

in as arguments. Even if you used them internally fn()

, they would not be available when func()

eventually executed.

A simple solution would be to pass an object fn()

:

function fn(conf) {
   var func = new Function('conf', "return conf.a");
   var wrapper = function(conf) {
       return func.apply(null, conf);
   };
   return wrapper;
}

fn({a:7});

      

+1


source


new Function

does not create a closing context , but eval

does.

Here's a low-tech way to construct a function with an arbitrary evaluated body and access the calling area:

function f(a) {
  return eval("(function() { return a })")()
}

f(10) // 10

      

+1


source







All Articles