2012-07-20 45 views
4

來自JavaScript後臺我已經習慣於能夠使用JavaScript的動態範圍來封裝函數中的值。例如:Ruby scope,從函數返回一個Proc

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中做出的更改,即使在定義之後,這些值也會被固定下來。

回答

3

在Ruby中,一個proc是一個閉包。在Javascript中,函數是閉包。關閉的想法是它「關閉」了它所定義的環境。在Ruby中,proc中的變量仍然可以改變,即使代碼處於proc之外,新值也會反映在proc中(參見peakxu的演示)。不確定Javascript關閉是否以這種方式工作。

+0

謝謝,這正是我正在尋找的那種解釋 – 2012-07-20 16:55:01

+0

歡迎。感謝您的反饋。你扔掉這些東西,你想知道它是否達到了標準。 :) – seph 2012-07-20 22:35:09

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:[email protected](irb):11> 
> quux.call 
=> 3 
> foo.bar = 3 
=> 3 
> quux.call 
=> 5 
2

Ruby procs和lambdas是關閉。當你做return Proc.new { @bar + @baz }你真的捕獲了self,這是實例變量的查找方式。 Ruby塊也是閉包。紅寶石不會讓你改變的變量和更改將傳播到呼叫範圍,假設呼叫範圍內仍然存在:

@hi = 0 
def get_hi() 
    lambda {@hi = 42} 
end 
get_hi.call() 
@hi #=> 42 

注意:除非你的PROC具有非常寬鬆的參數要求(不介意它得到任何參數,有多少種,像C的int f(void)),使用lambda而不是Proc.newlambda檢查以確保您獲得正確數量的參數。

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 

mMethod對象,並將其作爲在Foo的實例關閉,所以實例變量並且self的值仍然可用。