ruby奇特的作用域

接触ruby也有一段时间了,但是对ruby的作用域问题依旧感到迷惑不解,在c++,java中的一些作用域规则对于ruby 完全不起作用。你无法想象一个在方法前面定义的一个变量居然在方法内部是不可见的,下面我们就来说说它的奇特 之处。

作用域门

在ruby中没有java的inner scope的概念(在类中声明的字段对于类的方法都是可见的),而ruby的作用域简单来说 就是一组绑定,当ruby进入到某个作用域区间的时候,ruby的运行环境就绑定到该作用域区间中,若运行完成退出 后就会被另一组绑定所取代,所以它的作用域规范并不是堆栈嵌套关系。那么能够决定作用域区间的关键字为:

    module   模块
    class    类
    def      方法

当ruby程序遇到这三个词的时候会关闭掉前一个作用域,而打开一个新的作用域。在各个作用域中的局部变量显然 不能进入到下一个作用域中,如果想让局部变量穿过作用域门,可以使用方法来代替作用域门,这样可以实现扁平 化作用域(ruby元编程)

instance_eval

这是一个极具魔力的方法,通过它可以让代码块在调用它的对象实例中执行。我们先来看一段代码:

    #
    @component = "uaa"
    build_role "ruby",@component do
        write_env "#{@component}"
    end

    #
    require ''
    def build_role :name,:component,&block
       b = BuildBase.new(:name,:component)
       b.instance_eval(&block)
    end

在此端代码中,我通过build_role方法将代码块引入到BuildBase类的一个实例中执行,而这段代码根本是处于一个 与BuildBase类无关的类中,我们来剖析其中的要害。

    1. 既然这个代码块被引入到另一个类的实例中执行,那么block的作用域就切换到了关联实例中
    2. 由于发生了作用域切换,所以write_env方法必须在BuildBase中,而不是本类中
    3. 如果BuildBase中没有@component这个实例变量,那么“#{@component}”字符串为空,因为作用域已经
发生了切换

这是一个很神奇的事情,它看起来代码如此的优雅,它实际上也是通过参数的形式将变量传入下一个作用域。 instance_eval能做的还远不止这些,我们假想一个类继承的机制,我们习惯性将各个类的公共属性抽象出来形成 一个基类,然后再在继承类中改写和覆盖基类中的方法及变量即可。如果我们使用instance_eval,而抛弃继承的 思想,怎么来实现?我们也可以将公共属性和方法列出来形成一个类,然后各个类的区别被写成一个个代码块,最后 将各个代码块导入到公共类的实例环境中执行,这个想法是不是听起来比继承更酷,不过在实际运用中得看具体情 况而定。

class << self

我们在阅读ruby工程的源代码的时候,经常会被class << self这种写法所困惑。显然self就是当前的对象,那么 class << self就是在当前对象的基础上内建出一个类,实际上它就是当前对象对应的类本身。阅读过ruby元编程 的人都知道它其实就是一个eigenclass即单件类,这里不做展开。

纵所周知,类方法就是单件方法,它不依赖于对象的存在来调用,如果我们需要来保存和处理自身的多个对象,那 么就必须借助类方法和类变量了,有了class << self我们可以更优雅地写出类方法了,但是一条须注意:class是 个作用域门,它转换了作用域。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories ruby  tagged with ruby