Getters och setters är funktioner eller metoder som används för att hämta och ställa in värden på variabler. Begreppet getter-setter är vanligt inom datorprogrammering: nästan alla högnivåprogrammeringsspråk har en uppsättning syntax för att implementera getters och setters, inklusive JavaScipt.
Läs också: I det här inlägget ska vi se vad getters-setters är och hur man skapar och använder dem i JavaScript.
- Getters-setters och kapsling
- Skapa getters och setters
- 1. Med metoder
- 2. Med nyckelord
- Vilket sätt är bäst?
- Förebyggande av överskrivning
- Operationer inom getters och setters
- Skydda data med getters och setters
- Oskyddade data
- 1. Blockscoping
- 2. Funktionsscoping
- 3. Dataskydd utan scoping
- När ska du använda getters och setters?
Getters-setters och kapsling
Tanken på getters och setters nämns alltid i samband med kapsling. Inkapsling kan förstås på två sätt.
För det första är det inrättandet av data-getters-setters-trion för att få tillgång till och ändra dessa data. Denna definition är användbar när vissa operationer, t.ex. validering, måste utföras på data innan de sparas eller visas – getters och setters utgör det perfekta hemmet för detta.
För det andra finns det en strängare definition enligt vilken inkapsling görs för att dölja data, för att göra den otillgänglig för annan kod, utom genom getters och setters. På så sätt slutar vi inte med att av misstag skriva över viktiga data med någon annan kod i programmet.
Skapa getters och setters
1. Med metoder
Med tanke på att getters och setters i princip är funktioner som hämtar/ändrar ett värde finns det fler än ett sätt att skapa och använda dem. Det första sättet är:
var obj = { foo: 'this is the value of foo', getFoo: function() { return this.foo; }, setFoo: function(val) { this.foo = val; }}console.log(obj.getFoo());// "this is the value of foo"obj.setFoo('hello');console.log(obj.getFoo());// "hello"
Detta är det enklaste sättet att skapa getters och setters. Det finns en egenskap foo
och det finns två metoder: getFoo
och setFoo
för att returnera och tilldela ett värde till den egenskapen.
2. Med nyckelord
Ett mer ”officiellt” och robust sätt att skapa getters och setters är genom att använda nyckelorden get
och set
.
För att skapa en getter placerar du nyckelordet get
framför en funktionsdeklaration som kommer att fungera som gettermetod, och du använder nyckelordet set
på samma sätt för att skapa en setter. Syntaxen är följande:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}console.log(obj.foo);// "this is the value of foo"obj.foo = 'hello';console.log(obj.foo);// "hello"
Bemärk att data endast kan lagras under ett egenskapsnamn (fooVal
) som skiljer sig från namnet på getter-setter-metoderna (foo
) eftersom en egenskap som innehåller getter-settern inte kan innehålla data också.
Vilket sätt är bäst?
Om du väljer att skapa getters och setters med nyckelord kan du använda tilldelningsoperatorn för att ställa in data och punktoperatorn för att hämta data, på samma sätt som du skulle få tillgång till/ställa in värdet för en vanlig egenskap.
Om du däremot väljer det första sättet att koda getters och setters måste du anropa setter- och gettermetoderna med hjälp av syntaxen för funktionsanrop eftersom det är typiska funktioner (inget speciellt som de som skapats med nyckelorden get
och set
).
Det finns också en chans att du råkar tilldela något annat värde till egenskaperna som innehöll getter- och settermetoderna och förlora dem helt! Något som du inte behöver oroa dig för i den senare metoden.
Så du förstår varför jag sa att den andra tekniken är mer robust.
Förebyggande av överskrivning
Om du av någon anledning föredrar den första tekniken, gör du egenskaperna som innehar getter-setter-metoderna skrivskyddade genom att skapa dem med Object.defineProperties
. Egenskaper som skapas via Object.defineProperties
, Object.defineProperty
och Reflect.defineProperty
konfigureras automatiskt till writable: false
vilket betyder skrivskyddad:
/* Overwrite prevention */var obj = { foo: 'this is the value of foo'};Object.defineProperties(obj, { 'getFoo': { value: function () { return this.foo; } }, 'setFoo': { value: function (val) { this.foo = val; } }});obj.getFoo = 66;// getFoo is not going to be overwritten!console.log(obj.getFoo());// "this is the value of foo"
Operationer inom getters och setters
När du har infört getters och setters kan du gå vidare och utföra operationer på data innan du ändrar eller returnerar dem.
I koden nedan konkateneras data i getter-funktionen med en sträng innan den returneras, och i setter-funktionen utförs en validering av om värdet är ett tal eller inte innan n
uppdateras.
var obj = { n: 67, get id() { return 'The ID is: ' + this.n; }, set id(val) { if (typeof val === 'number') this.n = val; }}console.log(obj.id);// "The ID is: 67"obj.id = 893;console.log(obj.id);// "The ID is: 893"obj.id = 'hello';console.log(obj.id);// "The ID is: 893"
Skydda data med getters och setters
Så här långt har vi täckt användandet av getters och setters i det första sammanhanget av kapsling. Låt oss gå vidare till den andra, dvs. hur man döljer data från yttre kod med hjälp av getters och setters.
Oskyddade data
Insättningen av getters och setters innebär inte att data endast kan nås och ändras via dessa metoder. I följande exempel ändras den direkt utan att röra getter- och settermetoderna:
var obj = { fooVal: 'this is the value of foo', get foo() { return this.fooVal; }, set foo(val) { this.fooVal = val; }}obj.fooVal = 'hello';console.log(obj.foo);// "hello"
Vi använde inte settern utan ändrade data direkt (fooVal
). De data som vi ursprungligen ställde in inuti obj
är borta nu! För att förhindra att detta händer (av misstag) behöver du ett visst skydd för dina data. Du kan lägga till det genom att begränsa omfattningen av var dina data är tillgängliga. Det kan du göra antingen genom blockscoping eller funktionsscoping.
1. Blockscoping
Ett sätt är att använda ett blockscoping inom vilket data kommer att definieras med hjälp av nyckelordet let
som begränsar dess räckvidd till det blocket.
Ett blockscoping kan skapas genom att placera din kod innanför ett par hängande parenteser. När du skapar ett block scope ska du se till att lämna en kommentar ovanför det där du ber om att låta hängslen vara ifred, så att ingen tar bort hängslen av misstag och tror att de är några extra överflödiga hängslen i koden eller lägger till en etikett till block scope.
/* BLOCK SCOPE, leave the braces alone! */{let fooVal = 'this is the value of foo';var obj = { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect the fooVal inside the blockconsole.log(obj.foo);// "this is the value of foo"
Ändring/skapande av fooVal
utanför blocket kommer inte att påverka fooVal
som hänvisas till inne i gettersettersetters.
2. Funktionsscoping
Det vanligaste sättet att skydda data med scoping är att hålla data inne i en funktion och returnera ett objekt med getters och setters från den funktionen.
function myobj(){ var fooVal = 'this is the value of foo'; return { get foo() { return fooVal; }, set foo(val) { fooVal = val } }}fooVal = 'hello';// not going to affect our original fooValvar obj = myobj();console.log(obj.foo);// "this is the value of foo"
Objektet (med foo()
getter-setter inuti) som returneras av myobj()
-funktionen sparas i obj
, och sedan används obj
för att anropa getter och setter.
3. Dataskydd utan scoping
Det finns också ett annat sätt att skydda data från att skrivas över utan att begränsa dess omfattning. Logiken bakom det går så här: hur kan man ändra en data om man inte vet vad den heter?
Om datan har ett inte så lätt reproducerbart variabel-/egenskapsnamn är chansen stor att ingen (inte ens vi själva) kommer att sluta skriva över den genom att tilldela något värde till det variabelnamnet/egenskapsnamnet.
var obj = { s89274934764: 'this is the value of foo', get foo() { return this.s89274934764; }, set foo(val) { this.s89274934764 = val; }}console.log(obj.foo);// "this is the value of foo"
Det är ett sätt att lösa saker och ting på. Även om det namn jag valde inte är ett riktigt bra namn kan du också använda slumpmässiga värden eller symboler för att skapa egenskapsnamn, vilket föreslås av Derick Bailey i det här blogginlägget. Huvudsyftet är att hålla datan dold från annan kod och låta ett getter-setter-par få tillgång till/uppdatera den.
När ska du använda getters och setters?
Nu kommer den stora frågan: ska du börja tilldela getters och setters till all din data nu?
Om du döljer data finns det inget annat val.
Men om det är okej att andra koder kan se dina data, behöver du då fortfarande använda getters setters bara för att kombinera dem med kod som utför vissa operationer på dem? Jag skulle säga ja. Koden blir väldigt snabbt uppräknad. Att skapa mikroenheter av enskilda data med en egen getter-setter ger dig ett visst oberoende att arbeta med nämnda data utan att påverka andra delar av koden.
Läs också: 10 anledningar till varför du behöver kodoptimering
.