在ruby中变量是一个内存空间的指针,这意味着变量本身实际上不包含值,它包含内存中一个指向该值的一块内存空间。

通过以下两个示例我们想一下b最终的返回值是什么.

示例1:

a = "hi there"
b = a
a = "not here"

示例2:

a = "hi there"
b = a
a << ", Bob"

示例1中b返回的值是"hi there",而示例2中b变成了 "hi there, Bob".

为什么会出现这种情况呢,这是因为变量是物理内存空间的指针(variables are pointers to physical space in memory)。换句话说,变量是对应内存空间的一个标签,引用两张图比较一下:

image

图中我们看到a = "not here"重新通过一个新的内存地址给a做了赋值,a现在完全指向了一个新的字符串,这是=的原因,所以我们要理解不同的内存地址可以存储相同的值,但他们在内存中仍然是不同的。即使我们最后一行是a = "hi there",结果也是一样的,a和b指向不同的内存地址,但是它们的值碰巧是相等的。

下面我们看看示例2:

image

可以看到,那行a << ", Bob"并没有被重新赋值一个新的字符串,而是改变了调用对象自身的值,而该值也是b指向的内存空间值,所以b随着指向的值的变化,返回值自然也发生了变化。

这样我们得出结论,在ruby中一些操作会改变内存空间值(比如<<),而一些操作只是简单的将变量指向一个新的或不同的内存空间(比如=)。

这种规则也适用于array,hash,或其他的拥有可以改变调用者自身的数据类型。如果你调用了一个会改变调用者(caller)值的方法,那么其他指向该对象的值也会受到影响。所以当你的多个变量指向相同的内存地址或者在处理占用不同内存地址的变量时,你要特别注意。

我们再尝试个例子:

a = [1, 2, 3, 3]
b = a
c = a.uniq
d = a.uniq! 

这里执行完c = a.uniq的时候a和b还是[1, 2, 3, 3],c是[1, 2, 3]。

执行完d = a.uniq! 后a,b,c,d全变成[1, 2, 3]

同理还有array的map和map!, 所以在ruby中末尾带!(bang)的方法一般具有破坏caller的作用,但也不是完全,还有类似<<等方法没有叹号却属于bang方法,这些需要在开发过程中,慢慢积累。