Ruby Language
Zmienny zakres i widoczność
Szukaj…
Składnia
- $ zmienna_globalna
- @@ zmienna_klasy
- @instance_variable
- zmienna_lokalna
Uwagi
Zmienne klas są współdzielone w hierarchii klas. Może to spowodować zaskakujące zachowanie.
class A
@@variable = :x
def self.variable
@@variable
end
end
class B < A
@@variable = :y
end
A.variable # :y
Klasy są obiektami, więc zmiennych instancji można użyć do zapewnienia stanu specyficznego dla każdej klasy.
class A
@variable = :x
def self.variable
@variable
end
end
class B < A
@variable = :y
end
A.variable # :x
Zmienne lokalne
Zmienne lokalne (w przeciwieństwie do innych klas zmiennych) nie mają żadnego prefiksu
local_variable = "local"
p local_variable
# => local
Jego zakres zależy od miejsca, w którym został zadeklarowany, nie można go używać poza zakresem „kontenerów deklaracji”. Na przykład, jeśli zmienna lokalna jest zadeklarowana w metodzie, można jej użyć tylko wewnątrz tej metody.
def some_method
method_scope_var = "hi there"
p method_scope_var
end
some_method
# hi there
# => hi there
method_scope_var
# NameError: undefined local variable or method `method_scope_var'
Oczywiście zmienne lokalne nie są ograniczone do metod, z reguły można powiedzieć, że gdy tylko zadeklarujesz zmienną w bloku do ... end
lub owiniętym w nawiasy klamrowe {}
, będzie ona lokalna i będzie miała zasięg blok, w którym został zadeklarowany.
2.times do |n|
local_var = n + 1
p local_var
end
# 1
# 2
# => 2
local_var
# NameError: undefined local variable or method `local_var'
Jednak zmienne lokalne zadeklarowane w blokach if
lub case
mogą być używane w zakresie nadrzędnym:
if true
usable = "yay"
end
p usable
# yay
# => "yay"
Chociaż zmiennych lokalnych nie można używać poza jego blokiem deklaracji, zostaną one przekazane do bloków:
my_variable = "foo"
my_variable.split("").each_with_index do |char, i|
puts "The character in string '#{my_variable}' at index #{i} is #{char}"
end
# The character in string 'foo' at index 0 is f
# The character in string 'foo' at index 1 is o
# The character in string 'foo' at index 2 is o
# => ["f", "o", "o"]
Ale nie do definicji metody / klasy / modułu
my_variable = "foo"
def some_method
puts "you can't use the local variable in here, see? #{my_variable}"
end
some_method
# NameError: undefined local variable or method `my_variable'
Zmienne używane do argumentów blokowych są (oczywiście) lokalne dla bloku, ale przyćmią wcześniej zdefiniowane zmienne, nie nadpisując ich.
overshadowed = "sunlight"
["darkness"].each do |overshadowed|
p overshadowed
end
# darkness
# => ["darkness"]
p overshadowed
# "sunlight"
# => "sunlight"
Zmienne klasy
Zmienne klasy mają zakres obejmujący całą klasę, można je zadeklarować w dowolnym miejscu w klasie. Zmienna będzie uważana za zmienną klasową, gdy będzie poprzedzona @@
class Dinosaur
@@classification = "Like a Reptile, but like a bird"
def self.classification
@@classification
end
def classification
@@classification
end
end
dino = Dinosaur.new
dino.classification
# => "Like a Reptile, but like a bird"
Dinosaur.classification
# => "Like a Reptile, but like a bird"
Zmienne klas są wspólne dla pokrewnych klas i mogą zostać zastąpione klasami potomnymi
class TRex < Dinosaur
@@classification = "Big teeth bird!"
end
TRex.classification
# => "Big teeth bird!"
Dinosaur.classification
# => "Big teeth bird!"
To zachowanie jest niepożądane przez większość czasu i można je obejść, używając zmiennych instancji na poziomie klasy.
Zmienne klas zdefiniowane w module nie nadpisują ich, włączając w to zmienne klas klas:
module SomethingStrange
@@classification = "Something Strange"
end
class DuckDinosaur < Dinosaur
include SomethingStrange
end
DuckDinosaur.class_variables
# => [:@@classification]
SomethingStrange.class_variables
# => [:@@classification]
DuckDinosaur.classification
# => "Big teeth bird!"
Zmienne globalne
Zmienne globalne mają zasięg globalny i dlatego można ich używać wszędzie. Ich zakres nie zależy od tego, gdzie są zdefiniowane. Zmienna będzie uważana za globalną, jeśli będzie poprzedzona znakiem $
.
$i_am_global = "omg"
class Dinosaur
def instance_method
p "global vars can be used everywhere. See? #{$i_am_global}, #{$another_global_var}"
end
def self.class_method
$another_global_var = "srsly?"
p "global vars can be used everywhere. See? #{$i_am_global}"
end
end
Dinosaur.class_method
# "global vars can be used everywhere. See? omg"
# => "global vars can be used everywhere. See? omg"
dinosaur = Dinosaur.new
dinosaur.instance_method
# "global vars can be used everywhere. See? omg, srsly?"
# => "global vars can be used everywhere. See? omg, srsly?"
Ponieważ zmienna globalna może być zdefiniowana wszędzie i będzie widoczna wszędzie, wywołanie „niezdefiniowanej” zmiennej globalnej zwróci zero zamiast zgłaszania błędu.
p $undefined_var
# nil
# => nil
Chociaż zmienne globalne są łatwe w użyciu, zdecydowanie nie zaleca się ich używania na rzecz stałych.
Zmienne instancji
Zmienne instancji mają szeroki zakres obiektu, można je zadeklarować w dowolnym miejscu obiektu, jednak zmienna instancji zadeklarowana na poziomie klasy będzie widoczna tylko w obiekcie klasy. Zmienna będzie uważana za zmienną instancji, gdy będzie poprzedzona znakiem @
. Zmienne instancji są używane do ustawiania i pobierania atrybutów obiektów i zwracają zero, jeśli nie są zdefiniowane.
class Dinosaur
@base_sound = "rawrr"
def initialize(sound = nil)
@sound = sound || self.class.base_sound
end
def speak
@sound
end
def try_to_speak
@base_sound
end
def count_and_store_sound_length
@sound.chars.each_with_index do |char, i|
@sound_length = i + 1
p "#{char}: #{sound_length}"
end
end
def sound_length
@sound_length
end
def self.base_sound
@base_sound
end
end
dino_1 = Dinosaur.new
dino_2 = Dinosaur.new "grrr"
Dinosaur.base_sound
# => "rawrr"
dino_2.speak
# => "grrr"
Do zmiennej instancji zadeklarowanej na poziomie klasy nie można uzyskać dostępu na poziomie obiektu:
dino_1.try_to_speak
# => nil
Jednak użyliśmy zmiennej instancji @base_sound
aby utworzyć instancję dźwięku, gdy żaden dźwięk nie zostanie przekazany do nowej metody:
dino_1.speak
# => "rawwr"
Zmienne instancji można zadeklarować w dowolnym miejscu obiektu, nawet w bloku:
dino_1.count_and_store_sound_length
# "r: 1"
# "a: 2"
# "w: 3"
# "r: 4"
# "r: 5"
# => ["r", "a", "w", "r", "r"]
dino_1.sound_length
# => 5
Zmienne instancji nie są współużytkowane między instancjami tej samej klasy
dino_2.sound_length
# => nil
Można tego użyć do tworzenia zmiennych na poziomie klasy, które nie zostaną nadpisane przez klasę potomną, ponieważ klasy są również obiektami w Rubim.
class DuckDuckDinosaur < Dinosaur
@base_sound = "quack quack"
end
duck_dino = DuckDuckDinosaur.new
duck_dino.speak
# => "quack quack"
DuckDuckDinosaur.base_sound
# => "quack quack"
Dinosaur.base_sound
# => "rawrr"