Scope的描述

  • Scope是范围的意思,在编程中是变量的作用范围。
  • 访问一个变量,或者修改一个变量,首先要进入它的Scope
  • Ruby中有4中普通Scope,和一种闭包Scope
  • 普通Scope分别是
  • Global Scope
  • Class Scope
  • Instance Scope
  • local Scope
  • 闭包Scope专指 Proc对象的Instance 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<A
	end
	class C<B
	end
	c=C.new
	class <<c
	@@num=1
	end
	A.new.tell_num
  • 上面的例子有一个warning,因为从实例中通过@@num去直接赋值是不被推荐的,正确的做法是类变量只由类来读写

特殊情况

  • 上面提到@@可以进入Class Scope,还有一种比较特别的情况,方法定义 def
  • def 是一个切换Scope的关键字,进入的就是Class Scope之中。Ruby的闭包并不是由函数嵌套来实现的,嵌套函数只是在执行外层函数的时候为类空间新增了一个函数而已,显然由前面提到的Ruby的函数绑定都是动态绑定,类中定义函数就是新增了一个实例方法。而且因为def 进入了类Scope,所以不再可以访问局部变量。
  • 常量
  • 在类中定义一个常量,它会保存在类空间。变量必须用@@声明才会。通过 类名::常量名 可以访问到
  • 方法中不允许再定义类
  • 内部类或者模块是被允许的。类是一种特殊的模块,而模块代表了一个新的NameSpace,所谓名称空间就是一个前缀名称的隐藏
class A
		class B
		end
	end

	class C<A
	end
	b=C::B.new

	puts b
	puts b.class

规则的例外

  • 私有的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