Ruby 常數是大寫字母開頭,一個值不太需要改變,我們就會把他指定給常數。像是類別跟模組的名稱不會改變就用常數
class Sky
end
module Cloud
end
常數乍聽之下是不能改變,像是JavaScript宣告一個常數,就不能再指定另一個值給他
JS的範例如下:
const num = 1
num = 2 // Uncaught TypeError: Assignment to constant variable.
回到 Ruby
Ruby 的常數是可以改變的
當我用常數Weapon
裝備了倚天劍,如果改裝備屠龍刀就會出現警告:
Weapon = '倚天劍'
Weapon = '屠龍刀' # warning: already initialized constant Weapon
出現warning
了,但還是可以把屠龍刀
指定給帶有倚天劍
常數Weapon
這就是Ruby很有彈性的一點,連常數都可以變。(誰說拿劍就不能改拿刀,刀狂劍癡葉小釵不就都拿了)
常數要從後面插入字串也可以
Weapon = '倚天劍'
Weapon << '屠龍刀'
Weapon # => "倚天劍屠龍刀"
如果真的不想要這個常數被插入而改變,可以使用#freeze
方法
Weapon = '倚天劍'.freeze
Weapon << '屠龍刀' # FrozenError (can't modify frozen String)
出現FrozenError
了,就代表你不能修改已經凍結的字串
freeze
有兩個好處:
1.要改變Weapon變得困難
2.字串倚天劍
被 cache 住了,之後每次呼叫倚天劍
都會從記憶體相同的位置拿出,而不是每用一次倚天劍
,就要新增一個記憶體位置(object_id)。
常數介紹完,該換變數豪情四兄弟登場
昨天提到 Ruby 的區域變數,,而Ruby總共有四種變數,長相不一樣,有效範圍也不同,請聽我娓娓道來:
區域變數小弟,顧名思義就是變數的有效範圍只在一個小區域裡,開頭最普通,沒有 @, @@ 或 $
read
方法,並呼叫他def read
novel = '神鵰俠侶'
p "最愛武俠小說#{novel}"
end
read # => "最愛武俠小說神鵰俠侶"
p "最愛武俠小說#{novel}" # => NameError (undefined local variable or method `novel' for main:Object)
噴錯了,找不到區域變數novel
,因為novel
區域變數在定義read
方法的區塊之外就拿不到。所以如果一個區域變數被定義在方法的 block 裡面,一出去那個 block ,區域變數就等於是消失了。
novel
呢?novel = '神鵰俠侶'
def read
p "最愛武俠小說#{novel}"
end
p "最愛武俠小說#{novel}" # => "最愛武俠小說神鵰俠侶"
read # => NameError (undefined local variable or method `novel' for main:Object)
呼叫 read
方法噴錯了,這樣也是找不到區域變數novel
因為區域變數是由方法 block 所切割,如法呼叫在在方法外的區域變數
ps. #{}
是在雙引號中帶入變數的方法
實體變數三弟,開頭帶有@
,存在實體中。
範例如下:
先定義一個ComicBook
class
class ComicBook
def initialize(name)
@name = name
end
def read
p "熱血漫畫:#{@name}"
end
end
再由 ComicBook
產生兩個實體
comic1 = ComicBook.new('排球少年')
# => #<ComicBook:0x00007f8e4a98adc0 @name="排球少年">
comic2 = ComicBook.new('棋靈王')
# => #<ComicBook:0x00007f8e4a9783a0 @name="棋靈王">
comic1.read # => "熱血漫畫:排球少年"
comic2.read # => "熱血漫畫:棋靈王"
p "熱血漫畫:#{@name}" # => "熱血漫畫:"
上面例子,先產生一個實體 comic1,從#<ComicBook:0x00007f8e4a98adc0 @name="排球少年">
可以看到 comic1 帶有 class 名稱 ComicBook
,且含有實體變數跟對應的內容@name="排球少年"
。 comic1 可以透過read
方法印出身上的@name
。
然後再產生第二個實體,一樣帶有 class 名稱 ComicBook
,但是實體變數跟對應的內容不一樣了@name="棋靈王"
,透過read
方法印出的結果也不一樣。
實體變數,是可以被實體隨身攜帶。同一個實體之內,可以跨越不同的 method 而被使用。像是範例中產生 @name
的方法是initialize,但是read一樣可以拿到@name
。
雖然 comic2 也把參數"棋靈王"指定給@name
,但是comic1.read
依然是印出"排球少年",而不是"棋靈王",代表不同實體之間,就算實體變數名稱相同,也是各自獨立的個體。
最後一行可以看到,因為實體變數的有效範圍就是實體本身,在實體外面想印出@name
,只會回傳實體變數的預設值 nil
,
以上可以知道實體變數三弟有效範圍比區域變數小弟弟更大 (好像哪裡怪怪的)
大哥全域變數,二哥類別變數,請待下回分曉