Jack Franklin

December 23, 2013

För det mesta är det du ser som du får i JavaScript. Ett värde är ett värde, det finns inga knep. Ibland vill du dock ha ett värde som är baserat på några andra värden: någons fullständiga namn är till exempel en sammanlänkning av för- och efternamn. Om du har ett person-objekt och du vill att användarna av det objektet ska kunna ställa in det fullständiga namnet, förnamnet eller efternamnet och att ändringen omedelbart ska återspeglas i de andra värdena, skulle du konventionellt bygga det med funktioner:

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

Men det här är fult och kräver att användarna av objektet bryr sig om att egenskaperna är relaterade; i ett mer komplext exempel är det kanske inte lika uppenbart som med namn. Som tur är finns det ett bättre sätt, som lades till i ECMAScript 5.

Möt getters och setters.

Hur

Låt oss skapa det där personobjektet. Vi vill kunna ställa in förnamn, efternamn eller fullständigt namn och få det att uppdatera de andra två automatiskt.

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å vad händer här?

Storckelorden get och set är viktiga. Efter dem följer egenskapen de avser (fullName) och en funktionskropp som definierar beteendet när egenskapen nås (name = person.fullName) eller ändras (person.fullName = 'Some Name').

De här två nyckelorden definierar accessor-funktioner: en getter och en setter för egenskapen fullName. När egenskapen nås används returvärdet från getter-funktionen. När ett värde sätts anropas settern och det värde som sattes överförs. Det är upp till dig vad du gör med det värdet, men det som returneras från settern är det värde som överlämnades – så du behöver inte returnera något.

Det officiella sättet: Object.defineProperty

Länge med inline-metoden för att deklarera getters och setters kan det också göras mer explicit via Object.defineProperty (MDN-dokumentation). Den här metoden tar emot tre argument. Det första är det objekt som egenskapen ska läggas till i, det andra är namnet på egenskapen och det tredje är ett objekt som beskriver egenskapen (kallas egenskapsbeskrivare). Här är ett exempel som replikerar exemplet ovan:

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 || '';}});

Fördelen här är inte omedelbart uppenbar. Förutom att kunna lägga till egenskaper efter att ha skapat det första objektet, finns det någon verklig fördel?

När du definierar en egenskap på det här sättet kan du göra mycket mer än att bara definiera en setter eller getter. Du kan också lämna över följande nycklar:

  • configurable (false som standard): om detta är sant kommer egenskapens konfiguration att kunna ändras i framtiden.
  • enumerable (false som standard): om detta är sant kommer egenskapen att visas när man loopar över objektet (for (var key in obj)).

Vi kan också definiera egenskaper som inte har explicita getters eller setters:

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

Detta kommer att skapa person.age och sätta den till värdet 42. Det är viktigt att notera att den här egenskapen inte är skrivbar. Att anropa person.age = 99 kommer inte att ha någon effekt. På det här sättet kan du skapa skrivskyddade egenskaper. Om en egenskap har en value-nyckel inställd kan den inte ha en getter eller setter. Egenskaper kan ha värden eller accessorer, inte både och.

Inte bara det, men eftersom enumerable-egenskapen har standardvärdet false kommer den här egenskapen inte att visas när vi loopar över objektets nycklar.

Om vi ville göra en egenskap skrivbar skulle vi behöva ställa in egenskapen writable:

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

Nu får person.age = 99; önskad effekt.

Överanvändning

Håll dig i minnet: bara för att en funktion finns behöver den inte användas hela tiden. Getters och Setters har sina användningsområden, men överdriver inte, annars kommer du med största sannolikhet att få en design som är förvirrande för dem som interagerar med dina objekt. När de används med försiktighet är de mycket kraftfulla. Men med stor makt följer stort ansvar.

Stöd för webbläsare?

IE9 och senare har fullt stöd för Object.defineProperty, tillsammans med Safari 5+, Firefox 4+, Chrome 5+ och Opera 12+. Om du arbetar med Node.js finns det fullt stöd. Visst älskar du Node!

Denna artikel har skrivits tillsammans med Tom Ashworth. Tack till Tom för all hans hjälp med att sammanställa detta.

Om du gillade det här inlägget, gå med i nyhetsbrevet för att få uppdateringar om nytt innehåll 😎.

Alla blogginlägg

Om du gillade det här inlägget, se till att följa mig på Twitter.

Lämna ett svar

Din e-postadress kommer inte publiceras.