加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Ruby方法查找(与JavaScript比较)

发布时间:2020-12-17 02:23:40 所属栏目:百科 来源:网络整理
导读:我想更好地理解 Ruby中的对象如何具有在类和模块中定义的访问方法.具体来说,我想将它与 JavaScript(我更熟悉)进行比较和对比. 在JavaScript中,对象在对象本身上查找方法,如果在那里找不到它,它将在原型对象上查找方法.此过程将持续到达Object.prototype. //
我想更好地理解 Ruby中的对象如何具有在类和模块中定义的访问方法.具体来说,我想将它与 JavaScript(我更熟悉)进行比较和对比.

在JavaScript中,对象在对象本身上查找方法,如果在那里找不到它,它将在原型对象上查找方法.此过程将持续到达Object.prototype.

// JavaScript Example
var parent = {
  someMethod: function () {
    console.log( 'Inside Parent' );
  }
};

var child = Object.create( parent );
child.someMethod = function () {
  console.log( 'Inside Child' );
};

var obj1 = Object.create( child );
var obj2 = Object.create( child );

obj1.someMethod(); // 'Inside Child'
obj2.someMethod(); // 'Inside Child'

在JavaScript示例中,obj1和obj2都没有对象本身的someMethod函数.需要注意的关键是:

>子对象中有someMethod函数的一个副本,obj1和obj2都委托给子对象.
>这意味着obj和obj2都没有对象本身的someMethod函数的副本.
>如果子对象没有定义someMethod函数,则委托将继续到父对象.

现在我想将其与Ruby中的类似示例进行对比:

# Ruby Example
class Parent
  def some_method
    put 'Inside Parent'
  end
end

class Child < Parent
  def some_method
    puts 'Inside Child'
  end
end

obj1 = Child.new
obj2 = Child.new

obj1.some_method  # 'Inside Child'
obj2.some_method  # 'Inside Child'

这是我的问题:

> Ruby代码中的obj1和obj2是否都拥有some_method方法的副本?或者它类似于JavaScript,其中两个对象都可以通过另一个对象访问some_method(在这种情况下,通过Child类)?
>类似地,当在Ruby中考虑继承时,每个Ruby对象是否都具有同名的所有类和超类方法的副本?

我的直觉告诉我,Ruby对象没有从其类,混合模块和超类继承的方法的单独副本.相反,我的直觉是Ruby处理类似于JavaScript的方法查找,其中对象检查对象本身是否具有方法,如果没有,它在对象的类,混合模块和超类中查找方法,直到查找到达BasicObject .

解决方法

  1. Does obj1 and obj2 in the Ruby code each own a copy of the some_method method? Or is it similar to JavaScript where both objects have access to some_method via another object (in this case,via the Child class)?

你不知道. Ruby语言规范简单地说“如果你这样做,就会发生这种情况”.但是,它没有规定实现这种情况的特定方式.每个Ruby实现都可以按照它认为合适的方式自由实现,只要结果与规范相匹配,规范就不关心如何获得这些结果.

你不能说.如果实现保持适当的抽象,那么你就不可能告诉他们是如何做到的.这只是抽象的本质. (事实上??,这几乎就是抽象的定义.)

  1. Similarly,when inheritance is taken into account in Ruby,does each Ruby object have a copy of all of the class and superclass methods of the same name?

与上述相同.

目前有很多Ruby实现,过去在(完全)的各个阶段都有更多.其中一些实现(编辑)他们自己的对象模型(例如MRI,YARV,Rubinius,MRuby,Topaz,tinyrb,RubyGoLightly),有些人坐在他们试图适合的现有对象模型之上(例如XRuby和JRuby on关于CLI上的Java,Ruby.NET和IronRuby,关于Smalltalk的SmallRuby,smalltalk.rb,Alumina和MagLev,关于Objective-C/C++ocoa的MacRuby和RubyMotion,关于Parrot的Cardinal,关于ActionScript / Flash的Red Sun,关于SAP / ABAP的BlueRuby,ECMAScript上的HotRuby和Opal.rb)

谁能说所有这些实现都完全相同?

My gut tells me that Ruby objects DO NOT have separate copies of the methods inherited from their class,mixed-in modules,and superclasses. Instead,my gut is that Ruby handles method lookup similarly to JavaScript,where objects check if the object itself has the method and if not,it looks up the method in the object’s class,and superclasses until the lookup reaches BasicObject.

尽管我上面写的,这是一个合理的假设,事实上,我所知道的实现(MRI,JRuby,IronRuby,MagLev,Topaz)是如何工作的.

试想一下如果不是这样的意思. String类的每个实例都需要拥有自己的所有String 116方法的副本.想想典型的Ruby程序中有多少个字符串!

ruby -e 'p ObjectSpace.each_object(String).count'
# => 10013

即使在这个最简单的程序中,它不需要任何库,并且只创建一个单独的字符串(用于将数字打印到屏幕上),已经有超过10000个字符串.其中每一个都有自己的100多个String方法的副本.这将是对记忆的巨大浪费.

这也是同步的噩梦! Ruby允许您随时使用monkeypatch方法.如果我重新定义String类中的方法怎么办? Ruby现在必须更新该方法的每个副本,甚至跨越不同的线程.

我实际上只计算了直接在String中定义的公共方法.考虑到私有方法,方法的数量甚至更大.当然,还有继承:字符串不仅需要String中每个方法的副本,还需要Comparable,Object,Kernel和BasicObject中每个方法的副本.你能想象系统中的每个对象都有require的副本吗?

不,它在大多数Ruby实现中的工作方式是这样的.对象具有标识,实例变量和类(在静态类型的伪Ruby中):

struct Object
  object_id: Id
  ivars: Dictionary<Symbol,*Object>
  class: *Class
end

模块有方法字典,常量字典和类变量字典:

struct Module
  methods: Dictionary<Symbol,*Method>
  constants: Dictionary<Symbol,*Object>
  cvars: Dictionary<Symbol,*Object>
end

类就像一个模块,但它也有一个超类:

struct Class
  methods: Dictionary<Symbol,*Object>
  superclass: *Class
end

当你在一个对象上调用一个方法时,Ruby将查找该对象的类指针并尝试在那里找到该方法.如果没有,它将查看类的超类指针等,直到它到达一个没有超类的类.此时它实际上不会放弃,而是尝试在原始对象上调用method_missing方法,将您尝试调用的方法的名称作为参数传递,但这也只是一个普通的方法调用,所以它遵循所有相同的规则(除非如果对method_missing的调用到达层次结构的顶部,它将不会再次尝试调用它,这将导致无限循环).

哦,但我们忽略了一件事:单身方法!每个对象也需要有自己的方法字典.实际上,除了类之外,每个对象都有自己的私有单例类:

struct Object
  object_id: Id
  ivars: Dictionary<Symbol,*Object>
  class: *Class
  singleton_class: Class
end

因此,方法查找首先在单例类中开始,然后才进入类.

那么mixins怎么样?哦,对,每个模块和类还需要包含mixins的列表:

struct Module
  methods: Dictionary<Symbol,*Object>
  mixins: List<*Module>
end

struct Class
  methods: Dictionary<Symbol,*Object>
  superclass: *Class
  mixins: List<*Module>
end

现在,算法是:首先在单例类中查找,然后是类,然后是超类(es),然而,“look”也意味着“在查看方法字典之后,还要查看所有方法字典”在进入超类之前,包括mixins(以及包含的mixins中包含的mixins,依此类推,递归).

这听起来很复杂吗?它是!这并不好.方法查找是面向对象系统中最常执行的算法,它需要简单且快速.因此,一些Ruby实现(例如MRI,YARV)所做的是将解释器的“类”和“超类”的内部概念与程序员对这些相同概念的看法分离开来.

一个对象不再具有单例类和类,它只有一个类:

struct Object
  object_id: Id
  ivars: Dictionary<Symbol,*Object>
  class: *Class
  singleton_class: Class
end

一个类不再包含一个包含mixins的列表,只是一个超类.然而,它可能是隐藏的.还要注意字典成为指针,你会在一瞬间看到原因:

struct Class
  methods: *Dictionary<Symbol,*Method>
  constants: *Dictionary<Symbol,*Object>
  cvars: *Dictionary<Symbol,*Object>
  superclass: *Class
  visible?: Bool
end

现在,对象的类指针将始终指向单例类,而单例类的超类指针将始终指向对象的实际类.如果将mixin M包含在C类中,Ruby将创建一个新的不可见类M’,它与mixin共享其方法,常量和cvar字典.这个mixin类将成为C的超类,C的旧超类将成为mixin类的超类:

M′ = Class.new(
  methods = M->methods
  constants = M->constants
  cvars = M->cvars
  superclass = C->superclass
  visible? = false
)

C->superclass = *M'

实际上,它更多涉及,因为它还必须为M(和递归)中包含的mixin创建类,但最后,我们最终得到的是一个很好的线性方法查找路径,没有侧步进入单身类并包含mixins.

现在,方法查找算法就是这样的:

def lookup(meth,obj)
  c = obj->class

  until res = c->methods[meth]
    c = c->superclass
    raise MethodNotFound,meth if c.nil?
  end

  res
end

漂亮干净,精益和快速.

作为权衡,找出一个对象的类或一个类的超类稍微困难一点,因为你不能简单地返回类或超类指针,你必须走链,直到找到一个类是没有隐藏.但是你多久调用一次Object#class或Class#superclass?在调试之外你甚至可以打电话吗?

不幸的是,Module #prepend不适合这张图片.而且Refinements真的搞砸了,这就是许多Ruby实现甚至都没有实现它们的原因.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读