作用域详解

15 Aug 2013, by

类变量与实例变量区别

类方法,实例变量

class MyClass
  @val
= 1
 
def self.my_method
    p
"#{@val}"
    @val
+= 1
 
end
end


MyClass.my_method
MyClass.my_method
MyClass.my_method

输出
1
2
3

类方法,类变量

class MyClass
  @@val
= 1
 
def self.my_method
    p
"#{@@val}"
    @@val
+= 1
 
end
end

MyClass.my_method
MyClass.my_method
MyClass.my_method

输出
1
2
3

实例方法,类变量

class MyClass
  @@val
= 1
 
def my_method
    p
"#{@@val}"
    @@val
+= 1
 
end
end

c
= MyClass.new
c
.my_method
c
.my_method
c
.my_method

输出
1
2
3
class MyClass
    @my_var
= 1

   
def self.read
        @my_var
   
end

   
def write
        @my_var
= 2
   
end

   
def read
        @my_var
   
end
end

obj
= MyClass.new
obj
.write
obj
.read               # => 2
MyClass.read           # => 1

以上两个实例变量分别属于不同的作用域,并属于不同的对象。一个变量定义于obj充当self的时刻,它是obj对象的实例变量;另一个定义于MyClass充当self的时刻,它是MyClass的实例变量--也就是类的实例变量。类也是对象。

类实例变量只是属于Class类对象的普通实例变量。类实例变量仅仅可以被类本身访问--不能被类的实例或子类访问。


class Foo
 
@@cls_var = 'foo'
 
@cls_instvar = 'foo'

 
def test
   
p "instance:"
   
p "  class variable: #{@@cls_var}"
   
p "  class instance variable: #{@cls_instvar}"
 
end

 
def self.test
   
p "class:"
   
p "  class variable: #{@@cls_var}"
   
p "  class instance variable: #{@cls_instvar}"
 
end
end

f = Foo.new
p "------------"
p "Foo"
p "------------"
f.test
p "~~~~~~~~~~~~"
Foo.test

# 输出
"------------"
"Foo"
"------------"
"instance:"
"  class variable: foo"
"  class instance variable: "
"~~~~~~~~~~~~"
"class:"
"  class variable: foo"
"  class instance variable: foo"
"------------"
"Bar"
"------------"


class Foo
 
@@cls_var = 'foo'
 
@cls_instvar = 'foo'

 
def test
   
p "instance:"
   
p "  class variable: #{@@cls_var}"
   
p "  class instance variable: #{@cls_instvar}"
 
end

 
def self.test
   
p "class:"
   
p "  class variable: #{@@cls_var}"
   
p "  class instance variable: #{@cls_instvar}"
 
end
end

class Bar < Foo
 
@@cls_var = 'bar'
 
@cls_instvar = 'bar'
end

f = Foo.new
p "------------"
p "Foo"
p "------------"
f.test
p "~~~~~~~~~~~~"
Foo.test

p "------------"
p "Bar"
p "------------"
b = Bar.new
b.test
p "~~~~~~~~~~~~"
Bar.test

# 输出
"------------"
"Foo"
"------------"
"instance:"
"  class variable: bar"
"  class instance variable: "
"~~~~~~~~~~~~"
"class:"
"  class variable: bar"
"  class instance variable: foo"
"------------"
"Bar"
"------------"
"instance:"
"  class variable: bar"
"  class instance variable: "
"~~~~~~~~~~~~"
"class:"
"  class variable: bar"
"  class instance variable: bar"

普通的局部变量

对于普通的局部变量(小写字母或者下划线开头的变量)就像在文件系统中一样,某一个文件的可见作用域仅仅在本文件夹中,其他文件夹包括此文件夹的子文件夹中,这个文件都是不可见的,任何对于此文件(变量)的操作都必须在此文件的可见域中进行


   
class A  
     
var = "ok"  

     
def print_var
       
puts var #报错,因为var不可见
     
end

     
class SubA  
       
puts var #报错,因为var不可见  
     
end  
   
end  
     
   
class B  
     
puts var #报错,因为var在另外一个目录中同样不可见  
   
end  


   
class H
       
def initialize
           
var = "ok"
       
end

       
def p
           
puts var   #报错,因为var不可见  
       
end
   
end

常量

对于常量(大写字母开头的变量),有一些不同,常量对于常量所在文件夹中的子文件夹也是可见的


   
class A      
     
Const_var = "ok"  

     
def print_var
       
puts Const_var #正确,因为Const_var对于子目录可见
     
end
     
     
class SubA      
       
puts Const_var #正确,因为Const_var对于子目录可见  
     
end    
   
end    
         
   
class B      
     
puts var #报错,因为Const_var在另外一个目录中不可见      
   
end  

实例变量

而类的instance_variable,同常量类似,不同之处在于它的向子目录可视性仅仅对于方法有效,对于另外一个类无效


   
class A        
     
@var = "ok"      
     
     
def print_var  
       
puts @var #错误,不报错,为 空值  
     
end  
         
     
class SubA        
       
puts @var #错误,因为此时已经在另外一个类中,类A的instance_variable是不可视的  
     
end        
   
end        
           
   
class B        
     
puts @var #报错,因为@var在另外一个目录中不可见        
   
end  


   
class H
       
def initialize
           
@var = "ok"
       
end

       
def p
           
puts @var   #正确,
       
end
   
end

类变量

全局变量, 顶级实例变量

v1 = 1   

class MyClass  

    v2
= 2  

    local_variables        
# => [:v2]  

   
def my_method
        v3
= 3  
        local_variables  
   
end

    local_variables        
# => [:v2]  

end  

obj
= MyCalss.new    
obj
.my_method              # => [:v3]  
obj
.my_method              # => [:v3]  
local_variables            
# => [:v1, :obj]  

共享作用域

my_var = "Success"

class MyClass
   
# 需要打印my_var...

   
def my_method
       
# 需要打印my_var...
   
end
end

重写后可以完成:

my_var = "Success"

MyClass = Class.new do
    puts
"#{my_var}"

    define_method
:my_method do
        puts
"#{my_var}"
   
end
end

MyClass.new.my_method

动态执行代码块

instance_eval()

class MyClass
   
def initialize
        @v
= 1
   
end
end

obj
= MyClass.new
obj
.instance_eval do
   
self
    @v                
# => 1
end

v
= 2
obj
.instance_eval { @v = v }
obj
.instance_eval { @v }       # => 2

以上三行代码在同一个扁平作用域中执行,因此它们都可以访问局部变量v,由于块把运行它的对象作为self,所以它们也能访问obj的实例变量@v。
instance_exec() 与上面方法类似,但允许对块传入参数。

如果在一个实例上调用了instance_eval,就可以在其中定义该实例的单态函数 singleton_method

class A
end

a
= A.new
a
.instance_eval do
   
self  # => a
   
# current class => a's singleton class
   
def method1
        puts
'this is a singleton method of instance a'
   
end
end

a
.method1
#=> this is a singleton method of instance a

b
= A.new
b
.method1
#=>NoMethodError: undefined method `method1' for #<A:0x10043ff70>

因为类class本身也是Class类的一个实例, instance_eval 也可以用在类上,这个时候就可以在其中定义该类的 singleton_method ,即为该类的类函数。换句话说,可以用 instance_eval 来定义类函数class method。

class A
end

A
.instance_eval do
   
self  # => A
   
# current class => A's singleton class
   
def method1
        puts
'this is a singleton method of class A'
   
end
end

A
.method1
#=> this is a singleton method of class A