Ruby 元编程 第二版随笔(二)

  调用方法时发生了什么? 

  What happens when you call a method?

      调用方法时Ruby会做两件事:

     (1)找到这个方法,这个过程成为方法查找。

   (2)执行这个方法,为了做到这点,ruby要用到一个称为self 的东西。

       

    1.方法查找

         Method lookup

         

      调用一个方法前,Ruby会在对象中查找那个方法。不过,在进一步学习之前,我们还要掌握两个新概念:接受者(receiver)和祖先链(ancestors chain)。接受者就是你调用方法所在的对象。例如,在my_string.reverse()语句中,my_string就是接收者。为了理解祖先链 的概念,可以先观察一个Ruby类。想像从一个类找到它的超类,然后依次往上找,直到找到basicObject(Ruby类体系结构的根节点)。在这个过程中,经历的类的路径就是该类的祖先链。(祖先链中可能会包含模块,后面再说。)

       知道了接收者和祖先链,就可以用一句话来概括方法查找的过程:Ruby首先在接收者的类中查找。然后顺着祖先链向上查找,直到找到这个方法为止。

       

class MyClass
  def my_method; 'my_method'; end
end

class MySubclass < MyClass
end

   

 obj = MySubclass.new
 obj.my_method()                #   => "my_method()"

  

           我们已经知道了祖先链是从类开始到其超类结束。 实际上,祖先链中也包含模块(module)。当吧一      个模块包含在一个类(或者一个模块)中时,Ruby就会把这个模块加入该类的祖先链中,该模块在祖先链      中的位置就在包含它的类之上。

   

module M1
  def my_method
    'M1#my_method()'
  end
end

class C
  inculde M1
end

class D < C; en   

 D.ancestors            #  =>  [D, C, M1, Object, Kernel, BasicObject] 

          从Ruby 2.0 开始,还可以用另外一种方法吧模块插入一个类的祖先链中:使用prepend方法。 他的功能和include方法相似, 不过这个方法会把模块插入到祖先链中包含它的该类的下方,而不像include方法那样插入上方:

   

class C2
  prepend M2
end

class D2 < C2; end

  

   

D2.ancestors                #   =>  [D2, M2, C2, Object, Kennel, BasicObject]

    关于include和prepend,还有一个重要的知识点。

   多重包含

   如果试图在某个类的祖先链中多次加入同一个模块,会如何呢?

module M1; end

module M2
  include M1
end

module M3
  prepend M1
  inculde M2
end

   

M3.ancestors        #  => [M1, M2, M3]

          可以看出虽然我们多次的添加模块但是最后并没有重复,每次include或者prepend的时候,如果该模块 已经存在于祖先链中, 那么Ruby会悄悄的忽略这个包含(include或prepend)指令。因此一个模块只会在一条祖先链中出现一次。

    无处不在的Kernel模块

    Ruby中有一些方法(如print)可以随时随地的进行调用,看起来就像是所有对象都有print方法一样。这是因为这些方法实际上都是kerenl模块的私有方法:

Kernel.pricate_instance_methods.grep(/^pr/) # => [:printf, :print, :proc]

        这里的秘密在于Object类包含了Kernel模块,因此Kernel模块就进入了每个对象的祖先链。于是,无论哪个对象都可以随意调用Kernel模块的方法。这使得print看起来就像一个关键字,其实它只是一个方法而已。 

猜你喜欢

转载自994800477.iteye.com/blog/2298218
今日推荐