Ruby的方法查找算法实际上是really simple:
- 检索
class
接收者指针
- 如果该方法存在,则调用它
- 否则检索
superclass
指针,然后重复
就是这样。
如果算法到达没有更多超类的地步,但它仍然没有找到该方法,它将再次重新启动整个过程,method_missing
作为消息和原始消息的名称添加到参数之前。但仅此而已。这就是整个算法。它非常小而且非常简单,而且它has非常小且非常简单,因为方法查找是面向对象语言中最常执行的操作。
注:我完全无视Module#prepend http://ruby-doc.org/core/Module.html#method-i-prepend / Module#prepend_features http://ruby-doc.org/core/Module.html#method-i-prepend_features因为我对它是如何工作的还不够了解。我只知道它的作用,这对我来说就足够了。
另请注意:我忽略了性能优化,例如在多态内联缓存等中缓存方法查找的结果。
好的,但是这里有一个技巧:这些到底在哪里class
and superclass
指针指向?嗯,他们确实not指出什么Object#class http://ruby-doc.org/core/Object.html#method-i-class and Class#superclass http://ruby-doc.org/core/Class.html#method-i-superclass方法返回。那么,让我们退后一步。
每个对象都有一个class
指向对象的类的指针。并且每个班级都有一个superclass
指向其超类的指针。
让我们开始一个运行示例:
class Foo; end
现在,我们上课了Foo
,及其superclass
指针指向Object
.
foo = Foo.new
还有我们的对象foo
's class
指针指向Foo
.
def foo.bar; end
现在事情开始变得有趣了。我们创建了一个单例方法。嗯,实际上,不存在单例方法这样的东西,它实际上只是一个普通的方法。单例类。那么,这是如何运作的呢?好吧,现在class
指针指向foo
的单例类和foo
的单例类superclass
指针指向Foo
!换句话说,单例类被插入到两者之间foo
及其“真实”类别Foo
.
然而,当我们问foo
关于它的类,它仍然响应Foo
:
foo.class #=> Foo
The Object#class
方法知道单例类,并且简单地跳过它们,遵循superclass
指针,直到找到“正常”类并返回该类。
下一个并发症:
module Bar; end
class Foo
include Bar
end
这里会发生什么? Ruby 创建了一个new类(我们称之为Barʹ
),称为包括类。该类的方法表指针、类变量表指针、常量表指针指向Bar
的方法表、类变量表、常量表。然后,Ruby 使Barʹ
's superclass
指针指向Foo
的当前超类,然后使Foo
的超类指针指向Barʹ
。换句话说,包含模块会创建一个新类,该新类将作为包含该模块的类的超类插入。
这里有一点复杂:你也可以include
模块到模块。这是如何运作的?好吧,Ruby 只是跟踪包含在模块中的模块。然后,当模块被包含到类中时,它将为每个包含的模块递归地重复上述步骤。
这就是您需要了解的有关 Ruby 方法查找的全部内容:
- 找到班级
- 跟随超类
- 单例类插入上面的对象
- 包含类插入上面的类
现在我们来看看您的一些问题:
当在类上显式调用方法时,有很多关于搜索类及其包含的模块的顺序的说明(因此确切地说是什么super
每种情况下都会调用)。但是当没有显式调用方法时,例如一个平原func args
而不是self.func args
搜索顺序是什么?
相同。self
是隐式接收者,如果不指定接收者,则接收者是self
。并且括号是可选的。换句话说:
func args
is 一模一样 as
self.func(args)
为什么在我下面的示例中,成员方法调用func
找到全局之前的成员方法,但是func2
找到全局没有method_missing
被召唤?
Ruby 中不存在“全局方法”这样的东西。也没有“成员方法”这样的东西。每个方法都是一个实例方法。时期。没有全局、静态、类、单例、成员方法、过程、函数或子例程。
在顶层定义的方法成为private
类的实例方法Object
. Test
继承自Object
。运行我上面概述的步骤,您将确切地发现发生了什么:
- 取回
x
的类指针:Test
- Does
Test
有一个方法叫做func
: 是的,所以调用它。
现在再说一遍:
- 取回
x
的类指针:Test
- Does
Test
有一个方法叫做func2
: No!
- 取回
Test
的超类指针:Object
- Does
Object
有一个方法叫做func2
: 是的,所以调用它。
当全局是模块/类/类型时,为什么根本找不到同名的成员?
再说一次,这里没有全局,这里没有成员。这也与模块或类没有任何关系。而且 Ruby 没有(静态)类型。
Math
是对常量的引用。如果你想调用一个同名的方法,你必须确保 Ruby 可以知道它是一个方法。有两件事only方法可以有:接收者和参数。因此,您可以添加接收器:
self.Math
或参数:
Math()
现在 Ruby 知道你指的是方法Math
而不是常数Math
.
顺便说一句,这同样适用于局部变量。和二传手。如果你想调用 setter 而不是分配局部变量,你需要说
self.func = 'setter method'