Consider this code
'use strict';
var factory = () => script => eval(script);
var closure = factory();
closure('var v = 0');
var val = closure('typeof v');
console.log(val);
This is my attempt to achieve it. I want to create a closure and then to allow users to create a new local variable in that closure. Is that even possible?
I somewhere read that "native function 'eval' can even create a new variable in the local execution context.". So, why doesn't it work in my example? My guess is that it is because the function finished executin and number of variables after function ends cannot be changed, but I am not sure.
My example script creates a closure and tries to declare and initialize new variable v
in that closure and assign number 0
to it. I expected that the result of typeof v
should be number
, but it is actually undefined
.
So, I have two questions:
v
as expectedWell it is locally scoped, but a bit too local
var factory = () =>{
return script =>{
//this is the scope. you cant access any variables outside of this.
eval(script);
};
//you want to eval here, which is impossible
};
You could do very hacky scope stuff to work around this ( store all variables in context):
var factory = (context={}) => script =>{ with(context){ return eval(script); }};
You need to initialize all local variables on creation:
var exec=factory({v:0, b:undefined});// note that you need to set a value explicitly {v,b} wont worm
and then it works as expected:
console.log(
exec("v"),//0
exec("v=2"),//2
exec("v"),//2
typeof v //undefined
);
http://jsbin.com/xibozurizi/edit?console
If you dont want to go that deep, the only thing you could do would be concatenating the strings:
var factory = code => concat => (eval(code),res=eval(concat),code+=concat,res);
// or shorter / more buggy
var factory = code => concat => eval(code+=";"+concat);
var exec=factory("var a=1;");
console.log(
exec("a;"),//1
exec("var b=a+1"),
exec("b"),//2
tyepof a, typeof b //undefined
);
http://jsbin.com/midawahobi/edit?console
The upper code will run the strings multiple times, which is may not wanted. Another approach:
var factory=code=>({
code,
run(c){
return eval(this.code+";"+c);
},
add(c){ this.code+=";"+c}
});
So you can do
var exec=factory("var a='hello'");
exec.run("alert(a)")//alerts hello
exec.add("var b=a+' world'");
console.log(exec.code,exec.run("b"));//hello world, the upper alert isnt run again
http://jsbin.com/lihezuwaxo/edit?console
note that evaling is always a bad idea...