很多优秀的答案深入探讨了Ruby的
“按值传递引用”的原理,但我更喜欢通过示例来学习和理解。希望这能有所帮助。
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar = "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60
当我们进入这个方法时,可以看到我们的 bar 仍然指向字符串"value"。但是,我们将一个字符串对象 "reference" 分配给了 bar,它有一个新的 object_id。在这种情况下,foo 内部的 bar 具有不同的作用域,无论我们在方法中传递了什么,都不再被 bar 访问,因为我们重新分配了它并将其指向内存中保存字符串 "reference" 的新位置。
现在考虑一下相同的方法。唯一的区别是我们在方法内部所做的事情。
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar.replace "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60
bar (reference) after foo with object_id 60
注意区别了吗?我们所做的是:修改了该变量指向的String对象的内容。方法内部仍然存在不同作用域的bar。因此,小心处理传入方法的变量。如果您在原地修改传入的变量(gsub!,replace等),请在方法名称中使用感叹号!来表示。例如:“def foo!”
P.S.:
重要的是要记住foo内部和外部的“bar”是
“不同的”“bar”。它们的范围是不同的。在方法内部,您可以将“bar”重命名为“club”,结果将是相同的。
我经常看到变量在方法内外被重复使用,虽然这样做没问题,但会降低代码的可读性并且是一种代码异味。我强烈建议不要像我上面的示例中那样做 :),而是应该这样做
def foo(fiz)
puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
fiz = "reference"
puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60