iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 4
0
Modern Web

關於 Ruby on Rails,我想說的是系列 第 4

[Day 4]Ruby 常數與變數四兄弟(下)

讓我們繼續昨天的變數兄弟介紹吧

類別變數 Class Variable

類別變數二哥,開頭是@@。對,就是大家愛用的的表情符號,不過它可不像表情符號可以隨便用。
類別變數存在class內,而且隨著class被繼承,還會傳遞到下一個class。
這樣講太抽象,來看個範例:

class Ball
  @@diameter = 11       # 指定 11 給類別變數 @@diameter
  def self.size         #這邊用的是 class_method 的定義式
    p "直徑是#{@@diameter}cm"
  end

  def length
	 p "直徑是#{@@diameter}cm"
  end
end

Ball.size           #=> "直徑是11cm"
Ball.new.length     #=> "直徑是11cm"

先定義Ball這個 class,以及能印出類別變數@@diameter的類別方法size
呼叫Ball.size,得到@@diameter的值是11。
另外,類別變數也可以在實體方法內被呼叫,例如 Ball.new.length

Volleyball繼承自Ball,但是指定不同的值給@@diameter

看看會發生什麼事:

class Volleyball < Ball
  @@diameter = 20
end

Volleyball.size  # => "直徑是20cm"
Ball.size        # => "直徑是20cm"
p @@diameter    # => uninitialized class variable @@diameter in Object (NameError)

Volleyball.size回傳 "直徑是20cm",很正常。(鄉民: 30cm才正常)
沒想到Ball.size也得到"直徑是20cm"。
這時想在全域印出@@diameter會噴錯,回答找不到這個類別變數。

到此可以歸納類別變數的特性: 存在class內,而且隨著class被繼承,還會傳遞到下一個class。
重要的是,只要是同一個繼承練上,類別變數就會被互相干擾,所以Effective Ruby作法15,才會說「寧用類別實體變數,莫碰類別變數」。

類別實體變數 Class Instance Variable

類別實體變數是三弟(實體變數)另一個身份,是類別實體變數,相較於前面的實體變數有效範圍擴大到單ㄧclass層級,又不像類別變數被整個繼承體系所共享,更符合我們的需求。像是OOP的特性之一:封裝,也不會希望class 之間互相污染。
上面的例子@@diameter都改成類別實體變數@diameter會如何?

class Ball
  @diameter = 11

  def self.size
    p "直徑是:#{@diameter}cm"
  end
end
Ball.size       # => "直徑是:11cm"

class Volleyball < Ball
  @diameter = 20
end

Volleyball.size  # => "直徑是:12cm"
Ball.size        # => "直徑是:11cm"
p @diameter      # => nil

Volleyball.size還是一樣,但是第二次Ball.size保留了自己本來的變數值11。原本互相的問題用類別實體變數就解決了。

全域變數 Global Variable

全域變數大哥,開頭是$,全域都可呼叫,所以你如果在隱密的地方更改到全域變數,可能也不會發現,debug難度提高,大哥擁有薩諾斯一般的破壞力,謹慎使用。
何時會用全域變數呢? 像我們公司使用 Redis來快取資料,因為希望整個app都可以用到Redis,所以就將Redis的實體指定給全域變數

$redis =  Redis.new(url: REDIS_URL)  

但是後來我們公司導入rubocop,使用全域變數會跳出警告,所以我們改用其他方法來處理,這也說明了全域變數大哥是如同薩諾斯的存在,少用為妙。

還有一個全域變數的例子:

$LOAD_PATH #=> ["/Users/maxhuang/Desktop/Todolist/app/assets", "/Users/maxhuang/Desktop/Todolist/app/channels", ...

這是可以回傳 Gem 路徑的實體變數


總結一下變數四兄弟:

姓名 特徵 範例 預設值
區域變數 name
實體變數 前面有@符號 @name nil
類別變數 前面有@@符號 @@name
全域變數 前面有$符號 $name nil

了解區域變數,實體變數,類別變數,全域變數,四兄弟的能力範圍後,以後就不容易碰到變數範圍的bug了。


像辦公室上週就遇到類似這個bug

if tag.preset? # => NameError (undefined local variable or method `tag' for main:Object)
 tag = Tag.last
end

因為tag 還沒有被定義就被拿來用,Ruby 沒有像 JavaScript 會 Hoisting,這裡就會出現 NameError 的例外。


上一篇
[Day 3] Ruby 常數與變數四兄弟(上)
下一篇
[Day 5] 類別(class)與模組(module)
系列文
關於 Ruby on Rails,我想說的是23
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言