Scope的描述
- Scope是范围的意思,在编程中是变量的作用范围。
- 访问一个变量,或者修改一个变量,首先要进入它的Scope
- Ruby中有4中普通Scope,和一种闭包Scope
- 普通Scope分别是
- Global Scope
- Class Scope
- Instance Scope
- local Scope
- 闭包Scope专指 Proc对象的Instance Scope,向其它语言中的闭包一样,它会将自由变量存到自己的空间,使得函数的自由变量不被释放
- 普通Scope分别是
变量的访问规则
- 每一个表达式被执行,都肯定是在一个Scope里面,这个Scope被称为当前工作空间CWS
- 最外层的CWS是main
- 向一个对象发送一个方法,首先是绑定了一个方法,Ruby的方法绑定都是动态绑定;绑定方法的时候初始化一个Local Scope,并设置self为这个对象,也就是receiver的引用。
- 上面的 Local Scope 里保存的变量是方法内部声明的变量,且不管这个方法迭代调用自己多少次都是相同的Local Scope
- Local Scope 中可以访问到Global Scope,但如果Local Scope中发生了普通赋值(没有@开头的变量赋值)操作,则直接定义了一个局部变量
- 如果赋值操作的左值由一个@开头,如@a=1, @符号表示进入 Instance Scope,从而可以修改实例变量
- 如果赋值操作的左值由两个@开头,如@@a=1,@@符号表示进入 Class Scope,从而修改了类变量,这个类指的是self.class
- Class Scope 是一个继承链上的类共享的,当然是不包括Class 这个对象的
class A def tell_num puts @@num end end class B
- 上面的例子有一个warning,因为从实例中通过@@num去直接赋值是不被推荐的,正确的做法是类变量只由类来读写
特殊情况
- 上面提到@@可以进入Class Scope,还有一种比较特别的情况,方法定义 def
- def 是一个切换Scope的关键字,进入的就是Class Scope之中。Ruby的闭包并不是由函数嵌套来实现的,嵌套函数只是在执行外层函数的时候为类空间新增了一个函数而已,显然由前面提到的Ruby的函数绑定都是动态绑定,类中定义函数就是新增了一个实例方法。而且因为def 进入了类Scope,所以不再可以访问局部变量。
- 常量
- 在类中定义一个常量,它会保存在类空间。变量必须用@@声明才会。通过 类名::常量名 可以访问到
- 方法中不允许再定义类
- 内部类或者模块是被允许的。类是一种特殊的模块,而模块代表了一个新的NameSpace,所谓名称空间就是一个前缀名称的隐藏
class A class B end end class C
规则的例外
- 私有的setter方法可以被 self显示调用
- 之所有这个例外,是因为下面两条规则的矛盾
- 私有方法不能被显式调用
- 在方法中不显式的方法如果恰好包含了等号结尾,会被认为是一个局部变量的声明
- 有了上面两个矛盾,导致我们必须将setter的权限放开至protected或public。所以Ruby制定了特殊规则,允许setter方法被self(但不能是self的引用)显式调用
class A private def hello=(_nil) puts "hello" end def hi puts "hi" end public def bye begin self.hello=() # will not raise error self.hi # will raise an error rescue NoMethodError =>e puts e end begin this=self this.hello=() #will raise an error rescue NoMethodError =>e puts e end end end a=A.new
- 之所有这个例外,是因为下面两条规则的矛盾