作用域详解

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