Ruby Language
Variabel bereik en zichtbaarheid
Zoeken…
Syntaxis
- $ global_variable
- @@ class_variable
- @instance_variable
- local_variable
Opmerkingen
Klasse variabelen worden gedeeld in de klasse hiërarchie. Dit kan leiden tot verrassend gedrag.
class A
@@variable = :x
def self.variable
@@variable
end
end
class B < A
@@variable = :y
end
A.variable # :y
Klassen zijn objecten, dus instantievariabelen kunnen worden gebruikt om een status te geven die specifiek is voor elke klasse.
class A
@variable = :x
def self.variable
@variable
end
end
class B < A
@variable = :y
end
A.variable # :x
Lokale variabelen
Lokale variabelen hebben (in tegenstelling tot de andere variabeleklassen) geen voorvoegsel
local_variable = "local"
p local_variable
# => local
Het bereik is afhankelijk van waar het is aangegeven, het kan niet worden gebruikt buiten het bereik van de "aangiftecontainers". Als een lokale variabele bijvoorbeeld in een methode wordt gedeclareerd, kan deze alleen binnen die methode worden gebruikt.
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'
Lokale variabelen zijn natuurlijk niet beperkt tot methoden, als vuistregel zou je kunnen zeggen dat, zodra je een variabele in een do ... end
eindblok of gewikkeld in accolades {}
declareert, deze lokaal is en geschikt voor het blok waarin het is aangegeven.
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'
Lokale variabelen die zijn gedeclareerd in if
of case
blokken kunnen echter worden gebruikt in het bovenliggende bereik:
if true
usable = "yay"
end
p usable
# yay
# => "yay"
Hoewel lokale variabelen niet buiten het aangifteblok kunnen worden gebruikt, worden deze doorgegeven aan blokken:
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"]
Maar niet op de definities van methoden / klassen / modules
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'
De variabelen die worden gebruikt voor blokargumenten zijn (natuurlijk) lokaal voor het blok, maar zullen eerder gedefinieerde variabelen overschaduwen, zonder ze te overschrijven.
overshadowed = "sunlight"
["darkness"].each do |overshadowed|
p overshadowed
end
# darkness
# => ["darkness"]
p overshadowed
# "sunlight"
# => "sunlight"
Klasse variabelen
Klasse variabelen hebben een klasse breed bereik, ze kunnen overal in de klasse worden gedeclareerd. Een variabele wordt beschouwd als een klassenvariabele wanneer deze wordt voorafgegaan door @@
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"
Klasse variabelen worden gedeeld tussen gerelateerde klassen en kunnen worden overschreven vanuit een onderliggende klasse
class TRex < Dinosaur
@@classification = "Big teeth bird!"
end
TRex.classification
# => "Big teeth bird!"
Dinosaur.classification
# => "Big teeth bird!"
Dit gedrag is meestal ongewenst en kan worden omzeild door instantievariabelen op klassenniveau te gebruiken.
Klasse variabelen die in een module zijn gedefinieerd, overschrijven hun klassenklasse variabelen niet:
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!"
Globale variabelen
Globale variabelen hebben een globaal bereik en kunnen daarom overal worden gebruikt. Hun reikwijdte is niet afhankelijk van waar ze zijn gedefinieerd. Een variabele wordt als globaal beschouwd als deze wordt voorafgegaan door een $
-teken.
$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?"
Omdat een globale variabele overal kan worden gedefinieerd en overal zichtbaar zal zijn, zal het aanroepen van een "niet-gedefinieerde" globale variabele nul zijn in plaats van een fout te veroorzaken.
p $undefined_var
# nil
# => nil
Hoewel globale variabelen gemakkelijk te gebruiken zijn, wordt het gebruik ervan ten zeerste afgeraden ten gunste van constanten.
Instantievariabelen
Instantievariabelen hebben een objectbreed bereik, ze kunnen overal in het object worden gedeclareerd, maar een instantievariabele die op klassenniveau wordt gedeclareerd, is alleen zichtbaar in het klasseobject. Een variabele wordt beschouwd als een instantievariabele wanneer deze wordt voorafgegaan door @
. Instantievariabelen worden gebruikt om objectenattributen in te stellen en op te halen en retourneren nul indien niet gedefinieerd.
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"
De instantievariabele die op klassenniveau is gedeclareerd, is niet toegankelijk op objectniveau:
dino_1.try_to_speak
# => nil
We hebben echter de instantievariabele @base_sound
om het geluid te instantiëren wanneer er geen geluid wordt doorgegeven aan de nieuwe methode:
dino_1.speak
# => "rawwr"
Instantievariabelen kunnen overal in het object worden gedeclareerd, zelfs binnen een blok:
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
Instantievariabelen worden niet gedeeld tussen instanties van dezelfde klasse
dino_2.sound_length
# => nil
Dit kan worden gebruikt om variabelen op klassenniveau te maken die niet door een kindklasse worden overschreven, omdat klassen ook objecten in Ruby zijn.
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"