如何在闭包中创建新变量
How to create a new variable in a closure
考虑这段代码
'use strict';
var factory = () => script => eval(script);
var closure = factory();
closure('var v = 0');
var val = closure('typeof v');
console.log(val);
这是我的尝试。我想创建一个闭包,然后允许用户在该闭包中创建一个新的局部变量。这可能吗?
我在某处读到 "native function 'eval' can even create a new variable in the local execution context."。那么,为什么它在我的示例中不起作用?我的猜测是因为函数执行完毕,函数结束后的变量个数不能改变,但我不确定。
我的示例脚本创建了一个闭包并尝试在该闭包中声明和初始化新变量 v
并为其分配编号 0
。本以为typeof v
的结果应该是number
,但实际上是undefined
.
那么,我有两个问题:
- 为什么它没有按预期创建变量
v
- 如何实际实现该目标(工作示例是什么)?
您对 eval()
的调用确实 创建了一个新变量,并且它是在调用的本地执行上下文中创建的。该上下文是 inside 该函数,因此它不会影响您调用 closure()
.[=13= 的上下文 outside ]
一般来说eval()
是要严格避免的。它使优化变得困难(或不可能),因此使用它的函数将被优化器忽略。在一些罕见的情况下它很有用,但声明一个新的局部变量似乎不是其中之一。
你可以这样做
var factory = () => script => eval(script);
var closure = factory();
var v;
closure('v=20')
好吧,它是局部范围的,但有点太局部了
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
};
你可以做一些非常 hacky 范围的事情来解决这个问题(将所有变量存储在上下文中):
var factory = (context={}) => script =>{ with(context){ return eval(script); }};
您需要在创建时初始化所有局部变量:
var exec=factory({v:0, b:undefined});// note that you need to set a value explicitly {v,b} wont worm
然后它按预期工作:
console.log(
exec("v"),//0
exec("v=2"),//2
exec("v"),//2
typeof v //undefined
);
http://jsbin.com/xibozurizi/edit?console
如果您不想深入了解,唯一能做的就是连接字符串:
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
上面的代码会多次运行字符串,这可能不是我们想要的。另一种方法:
var factory=code=>({
code,
run(c){
return eval(this.code+";"+c);
},
add(c){ this.code+=";"+c}
});
所以你可以做到
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
请注意,求值总是一个坏主意...
考虑这段代码
'use strict';
var factory = () => script => eval(script);
var closure = factory();
closure('var v = 0');
var val = closure('typeof v');
console.log(val);
这是我的尝试。我想创建一个闭包,然后允许用户在该闭包中创建一个新的局部变量。这可能吗?
我在某处读到 "native function 'eval' can even create a new variable in the local execution context."。那么,为什么它在我的示例中不起作用?我的猜测是因为函数执行完毕,函数结束后的变量个数不能改变,但我不确定。
我的示例脚本创建了一个闭包并尝试在该闭包中声明和初始化新变量 v
并为其分配编号 0
。本以为typeof v
的结果应该是number
,但实际上是undefined
.
那么,我有两个问题:
- 为什么它没有按预期创建变量
v
- 如何实际实现该目标(工作示例是什么)?
您对 eval()
的调用确实 创建了一个新变量,并且它是在调用的本地执行上下文中创建的。该上下文是 inside 该函数,因此它不会影响您调用 closure()
.[=13= 的上下文 outside ]
一般来说eval()
是要严格避免的。它使优化变得困难(或不可能),因此使用它的函数将被优化器忽略。在一些罕见的情况下它很有用,但声明一个新的局部变量似乎不是其中之一。
你可以这样做
var factory = () => script => eval(script);
var closure = factory();
var v;
closure('v=20')
好吧,它是局部范围的,但有点太局部了
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
};
你可以做一些非常 hacky 范围的事情来解决这个问题(将所有变量存储在上下文中):
var factory = (context={}) => script =>{ with(context){ return eval(script); }};
您需要在创建时初始化所有局部变量:
var exec=factory({v:0, b:undefined});// note that you need to set a value explicitly {v,b} wont worm
然后它按预期工作:
console.log(
exec("v"),//0
exec("v=2"),//2
exec("v"),//2
typeof v //undefined
);
http://jsbin.com/xibozurizi/edit?console
如果您不想深入了解,唯一能做的就是连接字符串:
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
上面的代码会多次运行字符串,这可能不是我们想要的。另一种方法:
var factory=code=>({
code,
run(c){
return eval(this.code+";"+c);
},
add(c){ this.code+=";"+c}
});
所以你可以做到
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
请注意,求值总是一个坏主意...