Jack Franklin

23. december 2013

For det meste er det, hvad du ser, hvad du får, i JavaScript. En værdi er en værdi; der er ingen tricks. Nogle gange vil du dog have en værdi, der er baseret på nogle andre værdier: En persons fulde navn er f.eks. en sammenkædning af deres for- og efternavn. Hvis du har et person-objekt, og du ønsker, at brugerne af dette objekt skal kunne indstille det fulde navn, fornavn eller efternavn, og se denne ændring straks afspejlet i de andre værdier, ville du konventionelt opbygge det med funktioner:

person.setLastName('Smith');person.setFirstName('Jimmy');person.getFullName(); // Jimmy Smith

Men dette er grimt og kræver, at brugerne af dit objekt skal bekymre sig om, at egenskaberne er relateret; i et mere komplekst eksempel er det måske ikke så indlysende som med navne. Heldigvis er der en bedre måde, som blev tilføjet i ECMAScript 5.

Mød getters og setters.

Hvordan

Lad os lave dette personobjekt. Vi vil gerne kunne indstille fornavnet, efternavnet eller det fulde navn, og få den til at opdatere de to andre automatisk.

var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words || ''; this.lastName = words || ''; }}

person.fullName = 'Jack Franklin';console.log(person.firstName); // Jackconsole.log(person.lastName) // Franklin

Så hvad sker der her?

Nøgleordene get og set er vigtige. Efter dem følger den egenskab, de vedrører (fullName), og en funktionskrop, der definerer adfærden, når der er adgang til egenskaben (name = person.fullName) eller den ændres (person.fullName = 'Some Name').

Disse to nøgleord definerer accessor-funktioner: en getter og en setter for fullName-egenskaben. Når der er adgang til egenskaben, anvendes returværdien fra getteren. Når en værdi indstilles, kaldes setteren og får overdraget den værdi, der blev indstillet. Det er op til dig, hvad du gør med den værdi, men det, der returneres fra setteren, er den værdi, der blev overgivet – så du behøver ikke at returnere noget.

Den officielle måde: Object.defineProperty

Sammen med inline-metoden til at deklarere getters og setters, kan det også gøres mere eksplicit via Object.defineProperty (MDN-dokumentation). Denne metode tager tre argumenter. Det første er det objekt, der skal tilføjes egenskaben til, det andet er navnet på egenskaben, og det tredje er et objekt, der beskriver egenskaben (kendt som egenskabens deskriptor). Her er et eksempel, der replikerer ovenstående eksempel:

var person = { firstName: 'Jimmy', lastName: 'Smith'};

Object.defineProperty(person, 'fullName', {get: function() {return firstName + ' ' + lastName;},set: function(name) {var words = name.split(' ');this.firstName = words || '';this.lastName = words || '';}});

Fordelen her er ikke umiddelbart synlig. Ud over at kunne tilføje egenskaber efter oprettelsen af det oprindelige objekt, er der så en reel fordel?

Når du definerer en egenskab på denne måde, kan du gøre meget mere end blot at definere en setter eller getter. Du kan også videregive følgende nøgler:

  • configurable (false som standard): Hvis dette er sandt, vil egenskabens konfiguration kunne ændres i fremtiden.
  • enumerable (false som standard): Hvis dette er sandt, vil egenskaben blive vist, når der løbes over objektet (for (var key in obj)).

Vi kan også definere egenskaber, der ikke har eksplicitte getters eller setters:

Object.defineProperty(person, 'age', { value: 42});

Dette vil oprette person.age og indstille den til værdien 42. Det er vigtigt at bemærke, at denne egenskab ikke er skrivbar. Hvis du kalder person.age = 99, har det ingen virkning. På denne måde kan du oprette skrivebeskyttede egenskaber. Hvis en egenskab har en value-nøgle indstillet, kan den ikke have en getter eller setter. Egenskaber kan have værdier eller accessorer, ikke begge dele.

Men ikke nok med det, men da enumerable-egenskaben har standardværdien false, vil denne egenskab ikke blive vist, når vi gennemløber objektets nøgler.

Hvis vi ønskede at gøre en egenskab skrivbar, skulle vi indstille writable-egenskaben:

Object.defineProperty(person, 'age', { value: 42, writable: true});

Nu vil person.age = 99; have den ønskede effekt.

Overuse

Husk: Bare fordi en funktion findes, behøver den ikke at blive brugt hele tiden. Getters og Setters har deres anvendelsesmuligheder, men gå ikke for vidt, ellers ender du højst sandsynligt med et design, der er forvirrende for dem, der interagerer med dine objekter. Hvis de bruges med omtanke, er de meget effektive. Men med stor magt følger også stort ansvar.

Browserunderstøttelse?

IE9 og derover har fuld understøttelse for Object.defineProperty, sammen med Safari 5+, Firefox 4+, Chrome 5+ og Opera 12+. Hvis du arbejder med Node.js, er der fuld understøttelse. Er du ikke bare vild med Node?!

Denne artikel er skrevet i samarbejde med Tom Ashworth. Tak til Tom for al hans hjælp med at sammensætte dette.

Hvis du nød dette indlæg, så tilmeld dig nyhedsbrevet for at få opdateringer om nyt indhold 😎.

Alle blogindlæg

Hvis du nød dette indlæg, så husk at følge mig på Twitter.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.