类变量与实例变量区别
- 实例变量: 属于特定的对象。
- 类变量: 被一个类的所有实例对象共享,也可以被类方法访问到,类变量是私有的,在类外无法直接访问,只能通过实例方法和类方法去访问它。
类方法,实例变量
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
动态执行代码块
- eval能够编译并执行任何包含Ruby代码段的字符串,但是不安全
- class_eval在类的上下文范围将字符串作为代码来执行,作用在类上面,向类中添加实例方法
- instance_eval在实例的上下文范围执行
- module_eval在类或模块的上下文范围中执行
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