C# 是否需要引用变量才能将其包含在闭包中?
创建闭包时(在Javascript或C#中),闭包创建时作用域中的所有变量是否都“包含”在闭包中?或者只是在新创建的方法中引用的变量 示例C#代码: Javascript代码示例:C# 是否需要引用变量才能将其包含在闭包中?,c#,javascript,closures,C#,Javascript,Closures,创建闭包时(在Javascript或C#中),闭包创建时作用域中的所有变量是否都“包含”在闭包中?或者只是在新创建的方法中引用的变量 示例C#代码: Javascript代码示例: var referenced = 1; var notReferenced = 2; // Will this be enclosed? var func = function () { alert(referenced); } (当我读到关于IE中通过使用Java
var referenced = 1;
var notReferenced = 2; // Will this be enclosed?
var func = function () {
alert(referenced);
}
(当我读到关于IE中通过使用Javascript闭包创建循环引用而导致内存泄漏的内容时,我想到了这个问题。)注:我所说的“随附”是指MSDN所称的“已捕获”。()我只能回答关于C的问题: 不,它不会被封闭,因为它是不需要的 我在本文中对“是否将其附上?”的解释如下: 它是否会在为lambda表达式生成的闭包中捕获,即自动生成的类中是否有包含此变量值的字段 免责声明:
这是一个实现细节。我的答案是正确的,对于MS针对您的具体情况实现的C#编译器。它可能与Mono编译器或更新版本的MS编译器不同。或者,正如乔恩的回答所显示的那样,对于一个更复杂的例子,情况可能会有所不同。我只能从C方面回答 如果闭包没有实际捕获该变量,它将作为方法中的正常局部变量。如果它被捕获,它将被提升为合成类中的一个实例变量。(我假设当你谈论变量被“封闭”时,这就是你所说的。) 但是,请注意,如果两个lambda表达式各自捕获相同范围的不同变量,则在当前实现中,两个lambda将使用相同的合成类:
private Action Foo() {
var expensive = ...;
var cheap = ...;
Action temp = () => Console.WriteLine(expensive);
return () => Console.WriteLine(cheap);
}
在这里,返回的操作仍将保持“昂贵”引用的活动状态。不过,所有这些都是Microsoft C#4编译器的实现细节,将来可能会发生变化
创建闭包时(在Javascript或C#中),闭包创建时作用域中的所有变量是否都“包含”在闭包中?或者只是在新创建的方法中引用的变量
我只能回答有关JavaScript的问题
这是特定于实现的。有些引擎包含所有变量。一些发动机进行优化,仅包含参考变量
我知道Chrome是优化的,只包含它关心的变量
进一步阅读:
为了澄清闭包的IE内存泄漏问题,主要是关于主机对象(DOM)和JavaScript之间的循环引用。因此,除非您使用DOM(
el.attachEvent
或类似的东西)这里有两个问题。在将来,当你有两个问题时,你可能会考虑发表两个单独的问题。
在Javascript中创建闭包时,闭包创建时作用域中的所有变量是否都“包含”在其中
您没有说明您正在谈论的“JavaScript”的许多版本中的哪一个。我假设您正在谈论ECMAScript 3语言的正确实现。如果你正在谈论“JavaScript”的其他版本,请说出你正在谈论的版本
ECMAScript 5更新了词法环境和“eval”如何工作的规则。自2001年以来,我没有成为技术委员会39的成员,我也没有及时了解ECMAScript 5规范的最新变化;如果您想在最近的ECMAScript 5规则的上下文中找到答案,请找到该规范的专家;我不是
在ECMAScript 3的上下文中,您的问题的答案是肯定的。ECMAScript 3规范在这一点上非常清楚,但您不需要查看规范就知道一定是这样的:
function f()
{
var referenced = 1;
var notReferenced = 2;
var func = function ()
{
alert(referenced);
alert(eval("notReferenced"));
}
return func;
}
f()();
如果未捕获“NotReference”,则“eval”如何正确工作
这在ECMAScript规范中都有解释,但我可以在这里简单总结一下
每个变量都与一个“变量对象”相关联,该对象的属性名称是变量的名称。函数f的变量对象与函数f的激活对象相同——也就是说,每次调用函数f时都会神奇地创建一个对象。变量对象有三个属性:“已引用”、“未引用”和“func”
有一个称为“全局对象”的变量对象,它表示任何函数之外的代码。它有一个属性“f”
每个执行上下文都有一个范围链,它是在尝试计算标识符时搜索其属性的对象列表
每个函数对象都有一个与之关联的范围链,它是创建函数时生效的范围链的副本
与“f”关联的范围链是全局对象
当执行进入“f”时,执行上下文的当前作用域链将激活对象“f”推送到它上面
当“f”创建分配给“func”的函数对象时,其关联的作用域链是执行上下文的当前作用域链的副本,即包含“f”激活的作用域链和全局对象
好了,现在所有的对象都设置正确了。f返回func,然后调用它。为该函数创建激活对象的。执行上下文的作用域链是从函数对象获取的——记住,它是“f”的激活对象加上全局对象——我们将当前激活对象推送到作用域链的副本上。因为我们现在执行的匿名函数没有
function f()
{
var referenced = 1;
var notReferenced = 2;
var func = function ()
{
alert(referenced);
alert(eval("notReferenced"));
}
return func;
}
f()();
function f()
{
var func = function ()
{
alert(newlyCreated);
}
eval("var newlyCreated = 123;");
return func;
}
f()();