当前位置 博文首页 > 详解Ruby中的instance_eval方法及其与class_eval的对比

    详解Ruby中的instance_eval方法及其与class_eval的对比

    作者:kevinhua 时间:2021-02-02 21:16

    instance_eval方法

    这个BasicObject#instance_eval有点类似JS中的bind方法,不同的时,bind是将this传入到对象中,而instance_eval则是将代码块(上下文探针Context Probe)传入到指定的对象中,一个是传对象,一个是传执行体。通过这种方式就可以在instance_eval中的代码块里访问到调用者对象中的变量。

    示例代码

    class MyClass
      def initialize
        @v = 1
      end
    end
    obj = MyClass.new
    
    obj.instance_eval do
      self  #=> #<MyClass:0x33333 @v=1>
      @v   #=> 1 
    end
    
    v = 2
    obj.instance_eval { @v = v }
    obj.instance_eval { @v }  # => 2
    
    

    此外,instance_eval方法还有一个双胞胎兄弟:instance_exec方法。相比前者后者更加灵活,允许对代码块传入参数。

    示例代码

    class C
      def initialize
        @x = 1
      end
    end
    class D
      def twisted_method
        @y = 2
        #C.new.instance_eval { “@x: #{@x}, @y>: #{y}” }
        C.new.instance_exec(@y) { |y| “@x: #{@x}, @y: #{y}” }
      end
    end
    #D.new.twisted_method  # => “@x: 1, @y: ”
    D.new.twisted_method  # => “@x: 1, @y: 2”
    

    因为调用instance_eval后,将调用者作为了当前的self,所以作用域更换到了class C中,之前的作用域就不生效了。这时如果还想访问到之前@y变量,就需要通过参数打包上@y一起随instance_eval转义,但因为instance_eval不能携带参数,所以使用其同胞兄弟instance_exec方法。


    instance_eval 与 class_eval 的区别
    ###instance_eval
    首先从名字可以得到的信息是,instance_eval的调用者receiver必须是一个实例instance,而在instance_eval block的内部,self即为receiver实例本身。

    obj_instance.instance_eval do
     self # => obj_instance
     # current class => obj_instance's singleton class
    end
    <!--more-->
    
    

    根据这个定义,如果在一个实例上调用了instance_eval,就可以在其中定义该实例的单态函数 singleton_method

    class A
    end
    
    a = A.new
    a.instance_eval do
     self # => a
     # current class => a's singleton class
     def method1
      puts 'this is a singleton method of instance a'
     end
    end
    
    a.method1
    #=> this is a singleton method of instance a
    
    b = A.new
    b.method1
    #=>NoMethodError: undefined method `method1' for #<A:0x10043ff70>
    
    

    同样,因为类class本身也是Class类的一个实例,instance_eval也可以用在类上,这个时候就可以在其中定义该类的singleton_method,即为该类的类函数。

    换句话说,可以用instance_eval来定义类函数class method,这比较容易混淆,需要搞清楚。

    class A
    end
    
    A.instance_eval do
     self # => A
     # current class => A's singleton class
     def method1
      puts 'this is a singleton method of class A'
     end
    end
    
    A.method1
    #=> this is a singleton method of class A
    class_eval
    
    

    ###class_eval

    再来看class_eval,首先从名字可以得到的信息是,class_eval的调用者receiver必须是一个类,而在class_eval block的内部,self即为receiver类本身。

    class A
    end
    
    A.class_eval do
     self # => A
     # current class => A
    end
    
    

    根据这个定义,如果在一个类上调用了class_eval,就可以在其中定义该类的实例函数 instance_method

    class A
    end
    
    a = A.new
    a.method1
    #=> NoMethodError: undefined method `method1' for #<A:0x10043ff70>
    
    A.class_eval do
     self # => A
     # current class => A
     def method1
      puts 'this is a instance method of class A'
     end
    end
    
    a.method1
    #=> this is a instance method of class A
    
    

    换句话说,可以用class_eval来定义实例函数instance method,这也比较容易混淆,需要搞清楚。

    js
    下一篇:没有了