在 proc 上定义的binding 方法返回创建 proc 的绑定,并且不关心 proc 内容。
require 'pp'
some_local_var = 'hello'
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}" }
p.call()
pp eval("local_variables", p) #=> ['p', 'some_local_var']
变量local_var 仅在proc 执行的短时间内存在。您之前执行它的事实并不意味着它仍然存在(可能是如果 GC 尚未运行,但您不应该使用它)。一般来说,您应该将其视为不可访问。但是,由于 proc 可以访问它自己的绑定并且它自己携带绑定,因此您可以通过在 proc 之外声明内部变量来欺骗它:
require 'pp'
local_var = 'hello'
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}" }
pp eval("local_var", p) #=> hello
p.call()
pp eval("local_var", p) #=> value
这可用于创建行为类似于对象的 proc:
def get_counter
i = 0
proc { p i+=1 }
end
a = get_counter
b = get_counter
5.times { a.call } #=> 1 2 3 4 5
3.times { b.call } #=> 1 2 3
eval('i', a) #=> 5
eval('i', b) #=> 3
3.times { a.call } #=> 6 7 8
但这不是最佳实践,而是 Ruby 程序中大多数内存泄漏的主要原因。
在 Ruby 1.8.7 中,您可以传递 proc 而不是绑定,但 eval 在内部调用 proc 上的 binding 方法。稍后已将其删除,因此现在您需要显式提取它。
pp eval("local_variables", p.binding)
更新:
由于这是 ruby,显然我们可以做任何事情,自然有一种方法可以访问 proc 内部变量:
require 'pp'
proc_binding = nil;
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}"; proc_binding = binding }
p.call()
pp eval("local_variables", proc_binding) #=> ["proc_binding", "p", "local_var"]
但要小心 - 将内部绑定存储在变量中意味着即使在 proc 运行之后也会引用 local_var,因此不会被 GC 收集。此外,由于 proc 正在引用它并且尚未清理,因此不会在本地重新声明它,这几乎是毁掉你生活的快速方法。这些技术在极少数情况下可能很有用,但它们通常会导致很多极其难以调试的问题。