来自JavaScript背景我已经习惯于能够使用JavaScript的动态范围来封装函数中的值。例如:
function Dog( firstname, lastname ) {
this.fullname = firstname + lastname
return {
say_name: function () {
return fullname;
}
}
}
现在在Ruby中,我不太确定这样的事情会运作得很好:
class Foo
attr_accessor :bar, :baz
def initialize bar, baz
@bar = bar
@baz = baz
end
def give_me_a_proc
return Proc.new { @bar + @baz }
end
end
任何人都可以快速解释一下范围如何在Ruby中运行?如果我调用从give_me_a_proc
返回的Proc,它是否仍然可以访问其定义时间范围?
一旦我定义了proc,或者在Foo
中进行的任何更改被定义到Proc后,这些值是否也会被修复?
答案 0 :(得分:3)
是的,它仍然可以访问定义时间范围。请参阅documentation for Proc class。
更改将转至Proc后定义。 irb的成绩与你的班级一起运行。
> foo = Foo.new(1, 2)
=> #<Foo:0x007f9002167080 @bar=1, @baz=2>
> quux = foo.give_me_a_proc
=> #<Proc:0x007f900214f688@(irb):11>
> quux.call
=> 3
> foo.bar = 3
=> 3
> quux.call
=> 5
答案 1 :(得分:3)
在Ruby中,proc是一个闭包。在Javascript中,函数是一个闭包。闭包的想法是它“关闭”定义它的环境。在Ruby中,proc中的变量仍然可以更改,即使是生活在proc之外的代码,新值也会反映在proc中(请参阅peakxu进行演示)。不确定Javascript闭包是否以这种方式工作。
答案 2 :(得分:2)
Ruby procs和lambdas是闭包。执行return Proc.new { @bar + @baz }
时,您实际上正在捕获self
,这是查找实例变量的方式。 Ruby块也是闭包。 Ruby允许您更改变量,并且更改将传播到调用范围,假设调用范围仍然存在:
@hi = 0
def get_hi()
lambda {@hi = 42}
end
get_hi.call()
@hi #=> 42
注意:除非你的proc有非常宽松的参数要求(它不关心它是否有任何参数,有多少,有点像C int f(void)
),否则使用lambda
而不是{{1 }}。 Proc.new
检查以确保您获得正确数量的参数。
答案 3 :(得分:1)
Ruby有一个从对象获取闭包的方法,它非常适合你的例子:
class Foo
# reopen Foo class
def get_sum
@bar + @baz
end
end
m = Foo.new(5,20).method(:get_sum)
m.call #=> 25
m
是Method
对象,它充当Foo
实例中的闭包,因此实例变量和self
的值仍然可用。