Ruby 变量

变量是编程最基础的概念。但 Ruby 的变量系统比一般语言更"丰富"——它有五种变量,以前缀符号区分作用域。本章把每种变量的命名、作用域、用法讲透,并附带常量与并行赋值两个 Ruby 特色。

1. 局部变量(local variable)

最常见、最安全的变量。命名以小写字母或下划线开头,只在定义它的作用域里可见(方法内、块内、if 内):

# 局部变量:小写或下划线开头,只在当前作用域生效
name = "小明"
age = 18
_count = 0

puts name, age

# 出了方法就访问不到
def greet
  message = "hi"     # message 只在 greet 内部可见
  puts message
end

greet
# puts message  # ❌ 报错:undefined local variable

局部变量的好处是不会污染外部作用域。方法用完就被回收,不会影响其他代码。日常写代码 95% 的变量都是局部变量。

2. 动态类型

Ruby 是动态类型语言——变量本身没有类型,值才有类型。同一个变量可以重新指向不同类型的值:

# Ruby 是动态类型:变量本身没有类型,值才有
x = 10            # 此时 x 是 Integer
puts x.class      # Integer

x = "hello"       # 同一个 x 重新指向字符串
puts x.class      # String

x = [1, 2, 3]     # 又指向数组
puts x.class      # Array

灵活但要注意:别滥用这个特性在同一变量上来回切换类型,会让代码难读。一般约定"一个变量一种语义"。

3. 实例变量(instance variable)

@ 开头,属于某个具体对象。不同对象的同名实例变量互不影响:

# 实例变量:@ 开头,属于"某个对象"
class Person
  def initialize(name)
    @name = name       # 每个对象有自己的 @name
  end

  def name
    @name              # 实例方法里直接用
  end
end

p1 = Person.new("小明")
p2 = Person.new("小红")
puts p1.name       # 小明
puts p2.name       # 小红
puts p1.instance_variables   # [:@name]

实例变量是面向对象的核心——它保存对象的"状态"。后面类与面向对象一章会深入讲。@name 这种变量在对象创建时初始化,对象销毁时被回收。

注意:未初始化的实例变量返回 nil(不是报错),所以读 @foo 不存在时得到 nil。这跟局部变量不一样——局部变量未定义就读会报错。

4. 类变量(class variable)

@@ 开头,属于类本身,所有对象共享同一份:

# 类变量:@@ 开头,所有对象共享
class Counter
  @@count = 0      # 类变量,所有实例共享

  def initialize
    @@count += 1
  end

  def self.total
    @@count
  end
end

Counter.new
Counter.new
Counter.new
puts Counter.total    # 3

类变量常用来统计"全局信息"——比如已经创建了多少个对象、缓存配置。但现代 Ruby 社区倾向用类实例变量(@count 写在 class 上下文里)替代类变量,因为类变量在继承时行为容易出错。

5. 全局变量(global variable)

$ 开头,程序任何地方都能访问。但社区强烈不推荐用——它破坏封装、让代码难以追踪:

# 全局变量:$ 开头,程序任何地方都能访问(谨慎使用!)
$app_name = "我的应用"
$debug = true

def show
  puts $app_name    # 方法里也能访问
end

show
puts defined?($app_name)   # "global-variable"

# Ruby 预定义了一些全局变量:
puts $0           # 当前脚本名
puts $$           # 当前进程 PID
puts $:           # load 路径数组

Ruby 预定义了一些特殊全局变量用得比较多,如 $0(脚本名)、$$(进程 PID)、$DEBUG。这些是约定俗成的,不算坏味道。但你自己写的全局变量,99% 都应该改成实例变量或常量。

6. 常量(constant)

命名以大写字母开头,约定是全程大写下划线(SCREAMING_SNAKE_CASE)。Ruby 的常量是"软"常量——重新赋值会警告但不报错:

# 常量:大写字母开头,赋值后"不应该"改(改了只警告,不报错)
MAX_SIZE = 100
API_URL = "https://api.example.com"

class Config
  TIMEOUT = 30      # 类内部的常量
end

puts MAX_SIZE
puts Config::TIMEOUT    # 用 :: 访问类里的常量

# 重新赋值会警告:
MAX_SIZE = 200
# warning: already initialized constant MAX_SIZE

类内部的常量用 :: 访问(如 Config::TIMEOUT),这是 Ruby 的作用域操作符。

7. 并行赋值(Ruby 特色)

Ruby 支持一次性给多个变量赋值,甚至能直接交换变量:

# 并行赋值:Ruby 一个很有特色的语法
a, b, c = 1, 2, 3
puts a, b, c       # 1 2 3

# 交换两个变量,不需要临时变量
a, b = b, a
puts a, b          # 2 1

# 从方法返回多个值
def min_max(arr)
  [arr.min, arr.max]
end

lo, hi = min_max([3, 1, 4, 1, 5, 9])
puts lo, hi        # 1 9

这个语法在实际项目里用得很多,尤其是方法返回多值的场景。本质是利用了 Ruby 的"多返回值是数组解构"的语法糖。

8. 五种变量对照表

小结

本章你认识了 Ruby 的五种变量,以及它们的作用域前缀符号。记住口诀:无前缀是局部,@ 是对象,@@ 是类,$ 是全局,大写是常量。下一篇我们看 数据类型——这些变量里能装什么样的值。

← 上一篇 Ruby 基础语法

下一篇 Ruby 数据类型

✈️💬