我知道有关闭包的精彩帖子 here和 here ,但似乎都没有解决我想到的特定情况。这个问题最好用代码来证明:
function foo() {
var x = {};
var y = "whatever";
return function bar() {
alert(y);
};
}
var z = foo();
在 bar 中引用 y 会调用一个闭包,只要我将 z 保持在垃圾收集器周围就不会清理 y。问题是——x 会发生什么?即使它没有被引用,它是否也被那个闭包持有?垃圾收集器会发现没有引用 x 并清理它吗?或者只要我坚持使用 z,x 就会与 y 一起持续存在吗? (一个理想的答案是引用 ECMA 规范。)
最佳答案
The question is -- what happens to x?
答案因理论与实现而异。
理论上,是的,x保持事件状态,因为闭包(匿名函数)引用了调用 foo 的上下文的绑定(bind)对象, 其中包括 x .
在实践中,现代 JavaScript 引擎非常智能。如果他们能向自己证明 x 不能从闭包中引用,他们可以将其省略。他们这样做的程度因引擎而异。示例:V8(Chrome 和其他地方的引擎)将从 x 开始, y ,甚至是 x 的对象指的是堆栈,而不是堆;然后退出时 foo ,它会查看哪些内容仍有未完成的引用,并将它们移动到堆中。然后弹出栈指针,其他的东西都不存在了。 :-)
那么,他们如何证明呢?基本上,如果闭包中的代码不引用它并且不使用 eval或 new Function ,JavaScript 引擎很可能能够知道 x不需要。
如果你需要确定即使x仍然存在,该对象即使在旧版浏览器上也可用于 GC,这些浏览器可能是字面上的(愚蠢的),您可以这样做:
x = undefined;
这意味着没有任何东西保留对对象 x 的引用用来指代。所以即使x仍然存在,至少它所指的对象已经准备好收割了。而且它是无害的。但同样,现代引擎会为你优化事情,我不会担心它,除非你遇到特定的性能问题并将其追踪到一些代码分配一旦函数返回就不会被引用的大对象,但是不要'似乎正在清理。
不幸的是,正如您在下面指出的那样,对此有限制,例如 this question 中提到的限制。 .但这并不全是厄运和忧郁,请参阅下面的个人资料快照,了解您可以做什么...
让我们看看 V8 中的这段代码,使用 Chrome 的堆快照功能:
function UsedFlagClass_NoFunction() {}
function UnusedFlagClass_NoFunction() {}
function build_NoFunction() {
var notused = new UnusedFlagClass_NoFunction();
var used = new UsedFlagClass_NoFunction();
return function() { return used; };
}
function UsedFlagClass_FuncDecl() {}
function UnusedFlagClass_FuncDecl() {}
function build_FuncDecl() {
var notused = new UnusedFlagClass_FuncDecl();
var used = new UsedFlagClass_FuncDecl();
function unreachable() { notused; }
return function() { return used; };
}
function UsedFlagClass_FuncExpr() {}
function UnusedFlagClass_FuncExpr() {}
function build_FuncExpr() {
var notused = new UnusedFlagClass_FuncExpr();
var used = new UsedFlagClass_FuncExpr();
var unreachable = function() { notused; };
return function() { return used; };
}
window.noFunction = build_NoFunction();
window.funcDecl = build_FuncDecl();
window.funcExpr = build_FuncExpr();
这是扩展的堆快照:
处理 build_NoFunction 时函数,V8成功识别出notused引用的对象无法到达并摆脱它,但在其他任何一种情况下都不会这样做,尽管 unreachable无法到达,因此 notused无法通过它到达。
那么我们可以做些什么来避免这种不必要的内存消耗呢?
好吧,对于任何可以通过静态分析处理的东西,我们都可以使用 JavaScript-to-JavaScript 编译器,比如 Google 的 Closure Compiler。即使在“简单”模式下,使用 Closure Compiler“编译”上述代码的美化结果如下所示:
function UsedFlagClass_NoFunction() {}
function UnusedFlagClass_NoFunction() {}
function build_NoFunction() {
new UnusedFlagClass_NoFunction;
var a = new UsedFlagClass_NoFunction;
return function () {
return a
}
}
function UsedFlagClass_FuncDecl() {}
function UnusedFlagClass_FuncDecl() {}
function build_FuncDecl() {
new UnusedFlagClass_FuncDecl;
var a = new UsedFlagClass_FuncDecl;
return function () {
return a
}
}
function UsedFlagClass_FuncExpr() {}
function UnusedFlagClass_FuncExpr() {}
function build_FuncExpr() {
new UnusedFlagClass_FuncExpr;
var a = new UsedFlagClass_FuncExpr;
return function () {
return a
}
}
window.noFunction = build_NoFunction();
window.funcDecl = build_FuncDecl();
window.funcExpr = build_FuncExpr();
如您所见,静态分析告诉 CC unreachable是死代码,因此它完全删除了它。
当然,您可能使用过 unreachable在函数执行过程中的某些东西,在函数完成后就不需要它了。它不是死代码,但是函数结束时您不需要的代码。在这种情况下,您必须求助于:
unused = undefined;
最后。由于您不再需要该功能,您也可以释放它:
unused = unreachable = undefined;
(是的,您可以这样做,即使它是使用函数声明创建的。)
不,遗憾的是,只是在做:
unreachable = undefined;
...没有成功(截至撰写本文时)让 V8 计算出 unused可以清理。 :-(
关于关于未引用变量的 JavaScript 闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24468713/
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。
我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.
当我创建一个Rails应用程序时,控制台:railsnewfoo我的代码可以使用字符串“foo”吗?puts"Yourapp'snameis"+app_name_bar 最佳答案 Rails.application.class将为您提供应用程序的全名(例如YourAppName::Application)。从那里您可以使用Rails.application.class.parent获取模块名称。 关于ruby-on-rails-应用程序的名称是否可以作为变量使用?,我们在StackOve
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
如果我有以下一段Ruby代码:classBlahdefself.bleh@blih="Hello"@@bloh="World"endend@blih和@@bloh到底是什么?@blih是Blah类中的一个实例变量,@@bloh是Blah类中的一个类变量,对吗?这是否意味着@@bloh是Blah的类Class中的一个变量? 最佳答案 人们似乎忽略了该方法是类方法。@blih将是常量Bleh的类Class实例的实例变量。因此:irb(main):001:0>classBlehirb(main):002:1>defself.blehirb
我是Ruby的新手,有些闭包逻辑让我感到困惑。考虑这段代码:array=[]foriin(1..5)array[5,5,5,5,5]这对我来说很有意义,因为i被绑定(bind)在循环之外,所以每次循环都会捕获相同的变量。使用每个block可以解决这个问题对我来说也很有意义:array=[](1..5).each{|i|array[1,2,3,4,5]...因为现在每次通过时都单独声明i。但现在我迷路了:为什么我不能通过引入一个中间变量来修复它?array=[]foriin1..5j=iarray[5,5,5,5,5]因为j每次循环都是新的,我认为每次循环都会捕获不同的变量。例如,这绝对