【问题标题】:How to create a new variable in a closure如何在闭包中创建新变量
【发布时间】:2017-07-15 13:44:07
【问题描述】:

考虑这段代码

'use strict';

var factory = () => script => eval(script);
var closure = factory();

closure('var v = 0');
var val = closure('typeof v');

console.log(val);

这是我实现它的尝试。我想创建一个闭包,然后允许用户在该闭包中创建一个新的局部变量。这可能吗?

我在某处读到“本机函数'eval'甚至可以在本地执行上下文中创建一个新变量。”。那么,为什么它在我的示例中不起作用?我的猜测是因为函数结束执行,函数结束后变量的数量不能改变,但我不确定。

我的示例脚本创建了一个闭包,并尝试在该闭包中声明和初始化新变量v,并为其分配编号0。我原以为typeof v的结果应该是number,但实际上是undefined

所以,我有两个问题:

  1. 为什么不按预期创建变量v
  2. 如何实际实现这一点(工作示例是什么)?

【问题讨论】:

  • 你到底想在这里构建什么?如何创建一个对象来存储您的上下文并向该对象添加属性,而不是尝试破解闭包。
  • 为什么需要这个?我已经完成了 75% 的答案,但我意识到这看起来是多么奇怪和无用。为什么需要在这样的范围内创建新变量?请注意,每次您拨打closure 时,您都在创建一个新范围。对closure 的所有调用都未“连接”。
  • @Thomas。我知道我可以使用 object 来代替,但我只是要求这种方法以便更好地了解 javascript 的工作原理。
  • @Carcigenicate。我知道它没用(或者至少我找不到它在现实世界中的用途),但我只是想了解 javascript 的工作原理

标签: javascript closures eval v8 ecmascript-2016


【解决方案1】:

它是局部作用域的,但有点过于局部了

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

请注意,评估总是一个坏主意...

【讨论】:

  • 好的,我想我明白了。很好解释。但是,抱歉,我没有足够的代表 +1。
  • @FigliDelleTenebre 不客气 ;) 我想你很快就能...
【解决方案2】:

您对eval() 的调用确实创建了一个新变量,并且它是在调用的本地执行上下文中创建的。该上下文是inside该函数,因此它不会影响上下文outside,您调用closure()

一般eval() 是要严格避免的。它使优化变得困难(或不可能),因此使用它的函数将被优化器忽略。在极少数情况下它很有用,但声明一个新的局部变量似乎不是其中之一。

【讨论】:

  • 我知道eval 不用于商业或严肃用途,但我只是想了解 JavaScript 的概念及其工作原理,仅此而已。我知道应该避免评估代码。
【解决方案3】:

你可以这样做

var factory = () => script => eval(script);
var closure = factory();
var v;
closure('v=20')

【讨论】:

  • 但它会创建全局变量(或至少是父范围内的变量)。如果你尝试创建一个新的闭包,它将使用相同的变量v
猜你喜欢
  • 1970-01-01
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-22
  • 1970-01-01
相关资源
最近更新 更多