变量和常数都指向一个对象。即使不赋值, 它也是指向nil对象的。赋值操作只不过是让它指向另一个新对象而已。
所以, 赋值时并不会拷贝并生成一个新对象. 而是让赋值表达式左边的变量或常数指向表达式右边的对象。
尽管如此, 可能还是有人不理解. 这也是情有可原的, 因为上面的解释并不能涵盖所有的情况. 实际上, Fixnum、NilClass、 TrueClass、FalseClass以及Symbol类的实例会被变量或常数直接保存, 所以赋值时会被拷贝。其他类的实例都在内存上的其他地方, 变量和常数会指向它们。请参考立即值和使用。
2.2 局部变量的作用域是如何划定的?顶层、类(模块)定义或方法定义都是彼此独立的作用域。另外, 在块导入新的作用域时, 它还可以使用外侧的局部变量。
块之所以与众不同, 是因为这样能够保证Thread或过程对象中的局部变量的"局部性"。while、until、for是控制结构, 它们不会导入新的作用域。另外, loop是方法, 它的后面跟着块。
2.3 何时才能使用局部变量?在Ruby解释器运行Ruby脚本时, 它会一次读取整个脚本,然后进行语法分析。若没有语法问题的话, 才会开始执行句法分析中得到的代码。
在进行语法分析时, 只有在遇到局部变量的赋值语句之后, 才能使用它。例如
for i in 1..2 if i == 2 print a else a = 1 end end把上述代码写入名为test.rb的脚本. 执行该脚本后发生如下错误
test.rb:3: undefined local variable or method `a' for #<Object:0x40101f4c> (NameError) from test.rb:1:in `each' from test.rb:1当i值为1时,并不会发生错误;当i变成2之后就不行了。这是因为, 在进行语法分析时并不会按照运行时的逻辑顺序来进行, 而只是机械地逐行分析. 在遇到print a语句时, a并未被赋值, 因而无法使用该局部变量. 之后,在运行时因为找不到名为a的方法, 所以发生错误。
相反地, 若使用如下脚本则不会出现错误。
a = 1 if false; print a #=> nil若您不想因为局部变量的这个特性而费神的话, 我们推荐您在使用局部变量之前, 添加a = nil赋值语句。这样作还有一个好处, 就是可以加快局部变量的使用速度。
2.4 常数的作用域是如何划定的?类/模块中定义的常数可以用在该类/模块中。
若类/模块定义发生嵌套时, 可在内侧类/模块中使用外侧的常数。
还可以使用超类以及包含模块中的常数。
因为顶层中定义的常数已经被添加到Object类中, 所以您可以在所有的类/模块中使用顶层中的常数。
若遇到无法直接使用的常数时, 可以使用 类/模块名+::操作符+常数名 的方式来使用它。
2.5 实参是怎么传递给形参的呢?方法调用时, 会把实参赋值给形参。请您参考向变量进行赋值来了解Ruby中赋值的含义。若实参中的对象包含可以改变自身状态的方法时,就必须注意其副作用(当然了,也有可能不是副作用)了。请参考破坏性的方法。
2.6 将实参赋值给形参之后,对实参本身有什么影响吗?形参是局部变量, 对其进行赋值之后, 它就会指向其他对象. 仅此而已, 它并不会对原来的实参有什么影响。
2.7 若向形参所指对象发送消息的话,可能出现什么结果?形参所指对象实际上就是实参所指对象. 若该对象接到消息时状态发生变化的话,将会影响到主调方。请参考破坏性的方法。
2.8 参数前面的*是什么意思?各位C语言大侠请看好, 这可不是什么指针。在Ruby的参数前面添加一个*表示, 它可以接受以数组形式传来的不定量的参数。
def foo(*all) for e in all print e, " " end end foo(1, 2, 3) #=> 1 2 3另外,如果在方法调用中传了一个带*的数组, 则表示先展开数组然后再进行传递。
a = [1, 2, 3] foo(*a)现在只能在以下部分的尾部使用*
多重赋值的左边 多重赋值的右边 参数列表(定义方法时) 参数列表(调用方法时) case的when部分下面是在第(1)种形式中使用*的例子
x, *y = [7, 8, 9]上面的代码相当于x = 7、y = [8, 9]。另外,下面的代码
x, = [7, 8, 9]也是合法的, 此时x = 7. 而
x = [7, 8, 9]则表示x = [7, 8, 9]。
2.9 参数前面的&代表什么?在参数前面添加&之后,就可以像使用块那样来传递/接收过程对象。它只能位于参数列表的末尾。
2.10 可以给形参指定默认值吗?可以。
在调用函数时,才会计算该默认值。您可以使用任意表达式来设定Ruby的默认值(C++只能使用编译时的常数). 调用方法时,会在方法的作用域内计算默认值。
2.11 如何向块传递参数呢?在块内部的前端,使用||将形参括起来之后, 就可以使用实参进行多重赋值了。该形参只是普通的局部变量, 若块的外侧已经有同名参数时, 块参数的作用域将扩大到块外侧, 请留意这个问题。
2.12 为什么变量和常数的值会自己发生变化?请看下例。
A = a = b = "abc"; b << "d"; print a, " ", A #=> abcd abcd对变量或常数进行赋值, 是为了以后通过它们来使用对象。这并不是将对象本身赋值给变量或常数, 而只是让它们记住对该对象的引用。变量可以修改这个引用来指向其他的对象, 而常数却不能修改引用。
对变量或常数使用方法时, 实际上就是对它们所指的对象使用该方法。在上例中, <<方法修改了对象的状态,所以引发了"非预期"的结果。若该对象是数值的话, 就不会发生这种问题, 因为数值没有修改其自身状态的方法。若对数值使用方法时, 将返回新的对象。
这个例子虽然是用字符串来作演示的, 但就算使用带有可修改自身状态的方法的那些对象, 如数组或哈希表等来试验的话, 效果也是一样的。
2.13 常数不能被修改吗?若想让指向某对象的常数转而指向其他对象时, 就会出现warning。
若该对象带有破坏性的方法的话, 则可以修改该对象的内容。