设为首页 加入收藏

TOP

理解Ruby中的作用域(一)
2017-10-09 14:22:54 】 浏览:314
Tags:理解 Ruby 作用

 

标题图片

  作用域对于Ruby以及其它编程语言都是一个需要理解的至关重要的基础知识。在我刚开始学习ruby的时候遇到很多诸如变量未定义、变量没有正确赋值之类的问题,归根结底是因为自己对于ruby作用域的了解不够,但在你看看完我的这篇文章后,相信你不会再担心会遇到这些头疼的问题。

  什么是作用域?

  当谈论到作用域的时候,应该马上想到变量可见性这两个词,变量可见性是作用域的主要内容,没错,作用域就是关于在代码的什么地方什么变量是可见的,当你充分了解了作用域后,给你一段代码,你可以轻易知道此时什么变量是可见的,还有最重要的是知道什么变量在这段代码执行时是不可见的。

  那就从一开始的地方就将所有变量定义好,让所有变量在程序的所有地方都是可见的,不就可以免除作用域的问题了?这样不是让生活更简单吗?嗯,但事实并不是这样......

  你也许知道很多对立的程序员阵营,如:函数式编程阵营与面向对象编程阵营、不同的变量命名风格阵营、不同的代码格式阵营等等,但从没有人听说过支持去除作用域的阵营,特别是那些有着丰富编程经验的程序员更是保留作用域的忠实支持者,为什么? 因为如果你编程的经历越多,你会越来越发觉对所有变量在整个程序中保持可见是多么愚蠢、破坏性的行为,因为一开始就将所有变量定义并对整个程序都可见,那么在程序运行时你很难追踪什么时候、哪一段代码对哪个变量做了修改,而对于多人协作的工程,当面对成千上万行的代码时你很难知道某个变量是谁定义的?在什么地方被赋值?大量使用全局变量会使得你的程序变得难以检测追踪、运行结果难以预测,如果使用全局变量,你会遇到一个很棘手的问题就是如何给成千个全局变量进行唯一命名。

  作用域提供开发者一个实现类似计算机安全系统中的最少权限原则的方式,试想一下你正在开发一个银行系统,而所有人都可以进行读写所有的数据,某个人对存款进行了更改但不能确定他是这笔存款的所有者,这将是多么可怕的一件事!

  Ruby变量作用域快速浏览

  你可能已经对ruby的变量作用域有所了解,但我发现大部分教程都是对变量类型仅仅做一个简单的介绍,而没有对其有一个精确的讲解,下面是对于ruby中各类型变量的一个详细介绍:

  类变量(以@@为前缀):仅对定义该类变量的类及其子类可见。

  实例变量(以@为前缀):对定义该 变量的类的实例及其实例方法可见,但不可以直接被类使用。

  全局变量(以$为前缀):对整个ruby脚本程序可见。

  局部变量:仅在局部代码块中可见,这也是在编程中最经常使用到和容易出现问题的变量类型,因为局部变量的作用范围依赖很多的上下文代码块。

  下面用一张图片简洁明了地阐述4种变量作用域的关系。

  接下来的篇章我会专注于介绍这局部变量。从我的经验以及与他人交谈中发现大部分作用域的问题都是由于对局部变量没有一个很好的理解。

  局部变量什么时候被定义?

  在ruby语言中,对于在一个类中定义的实例变量(如@variable)不需要显式提前声明,在类的方法中尝试获取一个还未声明的实例变量会返回nil,而当你尝试获取一个未声明的局部变量的值时会抛出NameError错误 (undefined local variable or method)。

  Ruby解释器在看到局部变量赋值语句时将该变量加入局部作用域,需要注意的是无论该局部变量的赋值是否会执行,只要ruby解释器看到程序存在该变量赋值语句就会将该变量加入局部作用域,所以像下面的代码是可以正常执行而不报错的。

 

if false # the code below will not run
  a = 'hello' # ruby解释器看到该条语句将a变量加入局部作用域
end
p a # nil, 因为对a的赋值语句没有执行

 

  你可以尝试下删除 a = ‘hello’ 这条语句,看看会有什么情况发生。

  局部变量命名冲突

  假设你有以下代码

def something
  'hello'
end

p something
==> hello
something= 'Ruby'
p something
==> Ruby #'hello' is not printed

  在ruby中方法的调用可以像变量一样不需显式添加一个括号和方法接收对象,所以你可能会遇到像上面代码的命名冲突问题。

  当你的ruby代码中存在同名的变量名和方法名,同名的变量会以较高的优先级覆盖掉同名的方法,但这并不表示你不能再调用该方法,此时可以通过在方法调用时显式添加括号或者在调用方法前显式添加self作为方法接收对象。

def some_var; 'I am a method'; end
some_var = 'I am a variable'
p some_var # I am a variable
p some_var() # I am a method
p self.some_var # I am a method. 显式使用self对象调用some_var方法

  一个很有效的判断变量是否在作用域之外的方法

  首先在你的代码段中找到你要查看的变量 ,接着一直往上查找改变量,直到你找到该变量,这时会有两种情况:

  1. 到了作用域的起始地点(def/class/module/do-end 代码块的开头)
  2. 找到对该变量赋值的语句

  如果你在遇到2之前先遇到1的情况,那么很有可能你的代码会抛出NameError错误,如果你在遇到1之前先遇到情况2,那么恭喜你,该局部变量就在这段代码的作用域当中。

  实例变量 vs 局部变量

  实例变量属于某个对象,在该对象的所有方法中都可用,当局部变量是属于某个特定的作用域,仅在该作用域下可用。实例变量在每个新实例中可用进行修改,而局部变量会在进入一个新作用域时被改变或者覆盖,那如何知道作用域什么时候会改变?答案是:作用域门。

  作用域门:理解作用域至关重要的一个概念

  当使用下面这些语句时,你猜想会对作用域产生什么影响?

  1. 使用class关键字定义一个类;
  2. 使用module 定义一个模块;
  3. 使用def关键字定义一个方法。

  当你使用这些关键字的时候你就开辟了一个新的作用域,相当于ruby打开了一扇门让你的代码进入一个全新的上下文环境。所有的class/def/module 定义被成为作用域门,因为它们开启了一个新的作用域,在这个作用域中所有的旧作用域都不再可用,旧的局部变量会被新的局部变量所替代。

  如果你对上面的陈述感到疑惑,没关系,通过下面的例子可以让你更好地掌握这一概念。

v0 = 0
class SomeClass # 开启新作用域
  v1 = 1
  p local_variables # 打印出所有局部变量

  def some_method # 开启新作用域
    v2 = 2
    p local_variables
  end # 作用域关闭
end # 作用域关闭

som
编程开发网
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Logstash为什么那么慢?—— json.. 下一篇关于安装ruby brew 提示失败

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }