instance_eval
和之间的主要区别在于class_eval
它instance_eval
在实例的上下文中class_eval
工作,而在类的上下文中工作。我不确定你对 Rails 有多熟悉,但让我们看一个例子:
class Test3 < ActiveRecord::Base
end
t = Test3.first
t.class_eval { belongs_to :test_25 } #=> Defines a relationship to test_25 for this instance
t.test_25 #=> Method is defined (but fails because of how belongs_to works)
t2 = Test3.find(2)
t2.test_25 #=> NoMethodError
t.class.class_eval { belongs_to :another_test }
t.another_test #=> returns an instance of another_test (assuming relationship exists)
t2.another_test #=> same as t.another_test
t.class_eval { id } #=> NameError
t.instance_eval { id } #=> returns the id of the instance
t.instance_eval { belongs_to :your_mom } #=> NoMethodError
发生这种情况是因为belongs_to
实际上是在类主体的上下文中发生的方法调用,您不能从实例中调用它。当您尝试调用id
with 时class_eval
,它会失败,因为它id
是在实例上定义的方法,而不是在类中。
使用这两种方法定义方法,class_eval
并且instance_eval
在针对实例调用时工作原理基本相同。他们将仅在调用它的对象的实例上定义一个方法。
t.class_eval do
def some_method
puts "Hi!"
end
end
t.instance_eval do
def another_method
puts "Hello!"
end
end
t.some_method #=> "Hi!"
t.another_method #=> "Hello!"
t2.some_method #=> NoMethodError
t2.another_method #=> NoMethodError
但是,在处理类时,它们有所不同。
t.class.class_eval do
def meow
puts "meow!"
end
end
t.class.instance_eval do
def bark
puts "woof!"
end
end
t.meow #=> meow!
t2.meow #=> meow!
t.bark #=> NoMethodError
t2.bark #=> NoMethodError
那么树皮去哪儿了?它是在类的单例类的实例上定义的。我将在下面进行更多解释。但现在:
t.class.bark #=> woof!
Test3.bark #=> woof!
因此,要回答您关于self
类主体中所指内容的问题,您可以构建一个小测试:
a = class Test4
def bar
puts "Now, I'm a #{self.inspect}"
end
def self.baz
puts "I'm a #{self.inspect}"
end
class << self
def foo
puts "I'm a #{self.inspect}"
end
def self.huh?
puts "Hmmm? indeed"
end
instance_eval do
define_method :razors do
puts "Sounds painful"
end
end
"But check this out, I'm a #{self.inspect}"
end
end
puts Test4.foo #=> "I'm a Test4"
puts Test4.baz #=> "I'm a Test4"
puts Test4.new.bar #=> Now I'm a #<Test4:0x007fa473358cd8>
puts a #=> But check this out, I'm a #<Class:Test4>
所以这里发生的情况是,在上面的第一个 puts 语句中,我们看到 inspect 告诉我们self
在类方法体的上下文中是指类 Test4。在第二个puts
中,我们看到了相同的东西,只是定义不同(使用self.method_name
定义类方法的符号)。在第三个中,我们看到self
指的是 Test4 的一个实例。最后一个有点有趣,因为我们看到的是一个被调用self
的实例。那是因为当你定义一个类时,你就是在创建一个对象。Ruby 中的一切都是对象。这个对象实例称为元类或特征类或单例类。Class
Test4
您可以使用class << self
成语访问特征类。当您在那里时,您实际上可以访问特征类的内部。您可以在 eigenclass 内部定义实例方法,这与 call 一致self.method_name
。但是由于您在 eigenclass 的上下文中,您可以将方法附加到 eigenclass 的 eigenclass。
Test4.huh? #=> NoMethodError
Test4.singleton_class.huh? #=> Hmmm? indeed
当您instance_eval
在方法的上下文中调用时,您实际上是在调用instance_eval
类本身,这意味着您在 Test4 上创建实例方法。我在特征类中调用 instance_eval 的位置怎么样?它在 Test4 的 eigenclass 实例上创建一个方法:
Test4.razors #=> Sounds painful
希望这可以解决您的一些问题。我知道我在输入这个答案时学到了一些东西!