Förstå ’själv’ i Ruby

idag skulle jag vilja prata om self. Om du har programmerat Ruby ett tag har du sannolikt internaliserat tanken på self. När du läser eller skriver ett program, self finns det bakom dig.

men för mindre erfarna Rubyister kan self vara förvirrande. Det förändras alltid, men det visas aldrig uttryckligen i koden. Du förväntas bara veta.

många problem nybörjare möter orsakas av att inte förstå self. Om du någonsin har” förlorat ” en instansvariabel eller förbryllad över vilka data som är synliga för en mixin, beror det på att du inte förstod self i det sammanhanget.

i det här inlägget kommer vi att titta på self i en mängd olika vardagssituationer.

vad är själv?

du kanske har hört folk säga att allt i Ruby är ett objekt. Om det är sant betyder det att varje kod du skriver ”tillhör” något objekt.

self är en speciell variabel som pekar på objektet som ”äger” den nuvarande exekveringskoden. Ruby använder self everwhere:

  • till exempel variabler: @myvar
  • för metod och konstant sökning
  • vid definition av metoder, klasser och moduler.

i teorin är self ganska uppenbart. Men i praktiken är det lätt för knepiga situationer att dyka upp. Det är därför jag skrev det här inlägget.

exempel på själv

vi kommer att gå igenom flera exempel nu. Om de första verkar för grundläggande för dig, fortsätt bara läsa. De blir mer avancerade.

inuti en instansmetod

i koden nedan är reflect en instansmetod. Det tillhör objektet Vi skapade via Ghost.new. Så self pekar på det objektet.

class Ghost def reflect self endendg = Ghost.newg.reflect == g # => true

inuti en klassmetod

för detta exempel är reflect en klassmetod av Ghost. Med klassmetoder ”äger” klassen själv metoden. self pekar på klassen.

class Ghost def self.reflect self endendGhost.reflect == Ghost # => true

det fungerar på samma sätt med” klass ” metoder inuti moduler. Till exempel:

module Ghost def self.reflect self endend Ghost.reflect == Ghost # => true

kom ihåg att klasser och moduler behandlas som objekt i Ruby. Så detta beteende är inte så annorlunda än det förekomstmetodbeteende vi såg i det första exemplet.

inuti en klass-eller moduldefinition

en funktion i Ruby som gör det så bra för ramar som Rails är att du kan köra godtycklig kod i klass-och moduldefinitioner. När du sätter kod inuti en klass / modul definition, det körs precis som alla andra Ruby kod. Den enda verkliga skillnaden är värdet på self.

som du kan se nedan, self pekar på klassen eller modulen som håller på att definieras.

class Ghost self == Ghost # => trueend module Mummy self == Mummy # => trueend 

inuti mixin-metoder

blandade metoder beter sig precis som ”normal” instans eller klassmetoder när det gäller self. Det här är vettigt. Annars skulle mixin inte kunna interagera med den klass du blandade den i.

Instansmetoder

även om reflect – metoden definierades i modulen är dess self instansen för den klass den blandades in i.

module Reflection def reflect self endend class Ghost include Reflectionendg = Ghost.newg.reflect == g # => true

klassmetoder

när vi extend en klass att blanda i klassmetoder, beter sig self precis som det gör i normala klassmetoder.

module Reflection def reflect self endend class Ghost extend ReflectionendGhost.reflect == Ghost # => true

inuti metaclass

chansen är att du har sett den här populära genvägen för att definiera massor av klassmetoder på en gång.

class Ghost class << self def method1 end def method2 end endend

class << foo syntaxen är faktiskt ganska intressant. Det låter dig komma åt ett objekts metaklass – som också kallas ”singleton class” eller ”eigenclass.”Jag planerar att täcka metaclasses djupare i ett framtida inlägg. Men för tillfället behöver du bara veta att metaclass är där Ruby lagrar metoder som är unika för ett specifikt objekt.

om du öppnar self från insidan av class << foo – blocket får du metaklassen.

class << "test" puts self.inspectend# => #<Class:#<String:0x007f8de283bd88>

utanför någon klass

om du kör kod utanför någon klass, ger Ruby fortfarande self. Det pekar på ”main”, vilket är en instans av Object:

puts self.inspect # => main