Kétféle objektumtulajdonság létezik.

Az első fajta az adattulajdonságok. Már tudjuk, hogyan kell velük dolgozni. Minden tulajdonság, amit eddig használtunk, adattulajdonság volt.

A második fajta tulajdonság valami új. Ez a hozzáférési tulajdonságok. Ezek lényegében olyan függvények, amelyek egy érték megszerzésekor és beállításakor végrehajtódnak, de egy külső kód számára úgy néznek ki, mint a hagyományos tulajdonságok.

Getterek és setterek

Az accessor tulajdonságokat “getter” és “setter” metódusok képviselik. Egy objektum literálban ezeket get és set jelöli:

let obj = { get propName() { // getter, the code executed on getting obj.propName }, set propName(value) { // setter, the code executed on setting obj.propName = value }};

A getter akkor működik, amikor obj.propName beolvassuk, a setter – amikor hozzárendeljük.

Egy user objektumunk van például name és surname tulajdonságokkal:

let user = { name: "John", surname: "Smith"};

Most szeretnénk hozzáadni egy fullName tulajdonságot, aminek "John Smith"-nek kell lennie. Természetesen nem akarjuk a meglévő információt copy-paste módon beilleszteni, ezért megvalósíthatjuk accessorként:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }};alert(user.fullName); // John Smith

Kívülről nézve egy accessor tulajdonság úgy néz ki, mint egy hagyományos. Ez az accessor tulajdonságok lényege. A user.fullName-ot nem függvényként hívjuk meg, hanem normálisan olvassuk: a getter a színfalak mögött fut.

A fullName-nek mostantól csak egy gettere van. Ha megpróbáljuk hozzárendelni a user.fullName=-t, hiba fog jelentkezni:

let user = { get fullName() { return `...`; }};user.fullName = "Test"; // Error (property has only a getter)

Hozzuk helyre úgy, hogy a user.fullName-hoz hozzáadunk egy settert:

let user = { name: "John", surname: "Smith", get fullName() { return `${this.name} ${this.surname}`; }, set fullName(value) { = value.split(" "); }};// set fullName is executed with the given value.user.fullName = "Alice Cooper";alert(user.name); // Alicealert(user.surname); // Cooper

Az eredmény egy “virtuális” tulajdonság fullName lesz. Ez olvasható és írható.

Accessor leírók

A accessor tulajdonságok leírói különböznek az adat tulajdonságoktól.

A accessor tulajdonságok esetében nincs value vagy writable, helyette get és set függvények vannak.

Ez azt jelenti, hogy egy accessor leíró lehet:

  • get – egy argumentumok nélküli függvény, amely a tulajdonság olvasásakor működik,
  • set – egy argumentummal rendelkező függvény, amely a tulajdonság beállításakor hívódik,
  • enumerable – ugyanaz, mint az adattulajdonságoknál,
  • configurable – ugyanaz, mint az adattulajdonságoknál.

Egy fullName accessor defineProperty létrehozásához például átadhatunk egy get és set leírót:

let user = { name: "John", surname: "Smith"};Object.defineProperty(user, 'fullName', { get() { return `${this.name} ${this.surname}`; }, set(value) { = value.split(" "); }});alert(user.fullName); // John Smithfor(let key in user) alert(key); // name, surname

Megjegyezzük, hogy egy tulajdonság vagy accessor (get/set metódusokkal rendelkezik) vagy adattulajdonság (value-vel rendelkezik) lehet, de mindkettő nem.

Ha ugyanabban a leíróban megpróbáljuk megadni a get és a value tulajdonságot is, hiba fog jelentkezni:

// Error: Invalid property descriptor.Object.defineProperty({}, 'prop', { get() { return 1 }, value: 2});

Smarter getterek/setterek

A getterek/setterek “valódi” tulajdonságértékek feletti burkolatként használhatók, hogy nagyobb kontrollt nyerjünk a velük végzett műveletek felett.

Például, ha meg akarjuk tiltani a user túl rövid neveket, akkor lehet egy name setterünk, és az értéket egy külön _name tulajdonságban tarthatjuk:

let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { alert("Name is too short, need at least 4 characters"); return; } this._name = value; }};user.name = "Pete";alert(user.name); // Peteuser.name = ""; // Name is too short...

A nevet tehát a _name tulajdonságban tároljuk, a hozzáférés pedig getter és setter segítségével történik.

Technikailag a külső kód a user._name használatával közvetlenül hozzáférhet a névhez. De van egy széles körben ismert konvenció, miszerint az "_" aláhúzással kezdődő tulajdonságok belsőek, és nem érinthetők az objektumon kívülről.

A kompatibilitás érdekében

Az accessorok egyik nagyszerű haszna, hogy lehetővé teszik, hogy egy “rendes” adattulajdonság felett bármikor átvegyük az irányítást egy getterrel és egy setterrel való helyettesítéssel és a viselkedésének finomhangolásával.

Tegyük fel, hogy elkezdtük a felhasználói objektumok megvalósítását az name és age adattulajdonságok használatával:

function User(name, age) { this.name = name; this.age = age;}let john = new User("John", 25);alert( john.age ); // 25

…De előbb-utóbb változhatnak a dolgok. Lehet, hogy a age helyett a birthday tárolása mellett döntünk, mert ez pontosabb és kényelmesebb:

function User(name, birthday) { this.name = name; this.birthday = birthday;}let john = new User("John", new Date(1992, 6, 1));

Most mi legyen a régi kóddal, amely még mindig a age tulajdonságot használja?

Megpróbálhatjuk megtalálni az összes ilyen helyet és kijavítani őket, de ez időt vesz igénybe és nehéz lehet, ha azt a kódot sok más ember használja. És különben is, a age egy szép dolog a user-ben, nem?

Maradjon meg.

Egy getter hozzáadása a age-hez megoldja a problémát:

function User(name, birthday) { this.name = name; this.birthday = birthday; // age is calculated from the current date and birthday Object.defineProperty(this, "age", { get() { let todayYear = new Date().getFullYear(); return todayYear - this.birthday.getFullYear(); } });}let john = new User("John", new Date(1992, 6, 1));alert( john.birthday ); // birthday is availablealert( john.age ); // ...as well as the age

Most a régi kód is működik, és van egy szép további tulajdonságunk.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.