Ruby方法查找(与JavaScript比较)
我想更好地理解
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都委托给子对象. 现在我想将其与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处理类似于JavaScript的方法查找,其中对象检查对象本身是否具有方法,如果没有,它在对象的类,混合模块和超类中查找方法,直到查找到达BasicObject . 解决方法
你不知道. Ruby语言规范简单地说“如果你这样做,就会发生这种情况”.但是,它没有规定实现这种情况的特定方式.每个Ruby实现都可以按照它认为合适的方式自由实现,只要结果与规范相匹配,规范就不关心如何获得这些结果. 你不能说.如果实现保持适当的抽象,那么你就不可能告诉他们是如何做到的.这只是抽象的本质. (事实上??,这几乎就是抽象的定义.)
与上述相同. 目前有很多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) 谁能说所有这些实现都完全相同?
尽管我上面写的,这是一个合理的假设,事实上,我所知道的实现(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实现甚至都没有实现它们的原因. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |