作用域(scope)指的是变量的可达性或可见性。不同类型的变量有不同的作用域规则。与self类似,作用域在程序的执行过程中也在不断的变化,也可以根据上下文推断出"谁在什么作用域中"。但是,self和作用域不是同一个东西,在self没有变化的时候,可以开始一个新的局部作用域,有时候,作用域和self一起改变。
全局作用域和全局变量:全局变量最不常用,但需要了解。全局变量用$开始,在整个程序的任何位置都可以访问,穿过所有的边界。
局部作用域:在任意时刻,程序都处在一个特定的局部作用域中。两个作用域的主要区别就是可访问的局部变量不同。
ruby中辨别作用域的开始和结束:
- 顶层有它自己的局部作用域
- 每一个类或模块定义块(class,module)有它自己的局部作用域,即使是嵌套的类/模块定义块也一样。
- 每一个方法定义(def)有它自己的局部作用域
如下:
1 class C 2 a = 1 3 def local_a 4 a = 2 5 p a 6 end 7 p a 8 end 9 C.new.local_a
输出1 2。
PS:每次使用关键字def,class,module引入一个定义块的时候,就开始了一个新的局部作用域。不论它们如何嵌套,都是这样的。这和c语言不一样,c语言中内部作用域可以看到外部作用域的变量,但是ruby中不能。
如下:
1 class A 2 class B 3 a = 1 4 class C 5 a=2 6 p a 7 end 8 p a 9 end 10 end
输出2 1
局部作用域和self
当定义一个块的时候,就开始了新的局部作用域,也开始了一个特定的self的代码,但self和作用域并不是完全平行的起作用。
- 同样的self,不同的局部变量。
class C def a a = 1 p self p a end end c = C.new c.a c.a
两次调用了c.a,其中的self是一样的,但是a却是不一样的变量。
- 不同的self,同样的局部变量
1 class A 2 p self 3 var = "hello" 4 p var 5 define_method :my_method do 6 p self 7 p var 8 end 9 end 10 11 A.new.my_method
输出:
A
"hello"
#<A:0x1f08150>
"hello"
在这个函数里面,局部变量var是一样的,但是self却是不一样的,具体原因见下面的扁平化作用域。
常量的作用域和解析:
常量可定义在类或者模块里面,可以用相对路径或者绝对路径来访问。
1 class A 2 X = 1 3 class B 4 Y = 2 5 class C 6 X = 3 7 end 8 p C::X 9 end 10 end 11 p A::X 12 p A::B::Y 13 p A::B::C::X
输出3 1 2 3
扁平化作用域:
在进入另一个作用域的时候,局部变量立刻失效,那么如何让局部变量能在新的作用域里使用呢?替换关键字就是一个方法。
class 替换成Class.new
module替换成Module.new
def替换成Module#define_method
如下:
1 var = "hello world" 2 class A 3 #在这里打印var 4 def method 5 #在这里打印var 6 end 7 end
假如想实现这个功能,让var能在这两个地方可见,可以用到扁平化作用域的知识。
如下:
1 var = "hello world" 2 A = Class.new do 3 p var 4 define_method :method do 5 p var 6 end 7 end 8 A.new.method
输出两个hello world。
共享作用域:
假如你想在一组方法之间共享一个变量,但是有不希望其他方法可以访问这个变量,可以用到扁平作用域的方法
如下:
1 def define_methods 2 shared = 0 3 4 Kernel.send :define_method,:counter do 5 shared 6 end 7 8 Kernel.send :define_method,:inc do |x| 9 shared += x 10 end 11 end 12 13 define_methods 14 p counter 15 inc(3) 16 p counter
输出0 3 。方法counter和inc之间会共享shared这个变量。
ruby中的作用域