Existují dva druhy vlastností objektu.
Prvním druhem jsou vlastnosti dat . Už víme, jak s nimi pracovat. Všechny vlastnosti, které jsme dosud používali, byly datové vlastnosti.
Druhý typ nemovitosti je něco nového. Je to vlastnost přístupového nástroje . Jsou to v podstatě funkce, které se provádějí při získávání a nastavení hodnoty, ale vypadají jako běžné vlastnosti externího kódu.
Getters and setters
Vlastnosti přístupového objektu jsou reprezentovány metodami „getter“ a „setter“. V objektovém literálu jsou označeny get
a set
:
let obj = {
get propName() {
// getter, the code executed on getting obj.propName
},
set propName(value) {
// setter, the code executed on setting obj.propName = value
}
};
Getter funguje, když obj.propName
je přečten, nastavovač – když je přiřazen.
Například máme user
objekt s name
a surname
:
let user = {
name: "John",
surname: "Smith"
};
Nyní chceme přidat fullName
vlastnost, která by měla být "John Smith"
. Samozřejmě nechceme kopírovat a vkládat existující informace, takže je můžeme implementovat jako přístupový objekt:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
}
};
alert(user.fullName); // John Smith
Zvenčí vypadá vlastnost přístupového objektu jako běžná. To je myšlenka vlastností přístupového prvku. Nevoláme user.fullName
jako funkci čteme normálně:getter běží za scénou.
Od této chvíle fullName
má pouze getr. Pokud se pokusíme přiřadit user.fullName=
, dojde k chybě:
let user = {
get fullName() {
return `...`;
}
};
user.fullName = "Test"; // Error (property has only a getter)
Pojďme to opravit přidáním setteru pro user.fullName
:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
},
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};
// set fullName is executed with the given value.
user.fullName = "Alice Cooper";
alert(user.name); // Alice
alert(user.surname); // Cooper
Výsledkem je „virtuální“ vlastnost fullName
. Je čitelný a zapisovatelný.
Deskriptory přístupových práv
Deskriptory pro vlastnosti přístupového objektu se liší od deskriptorů pro vlastnosti dat.
Pro vlastnosti přístupového objektu neexistuje value
nebo writable
, ale místo toho existuje get
a set
funkce.
To znamená, že popisovač přístupového objektu může mít:
get
– funkce bez argumentů, která funguje při čtení vlastnosti,set
– funkce s jedním argumentem, která se volá při nastavení vlastnosti,enumerable
– stejně jako u vlastností dat,configurable
– stejné jako u vlastností dat.
Chcete-li například vytvořit přístupový objekt fullName
s defineProperty
, můžeme předat deskriptor s get
a set
:
let user = {
name: "John",
surname: "Smith"
};
Object.defineProperty(user, 'fullName', {
get() {
return `${this.name} ${this.surname}`;
},
set(value) {
[this.name, this.surname] = value.split(" ");
}
});
alert(user.fullName); // John Smith
for(let key in user) alert(key); // name, surname
Upozorňujeme, že vlastnost může být buď přístupovým objektem (má get/set
metody) nebo vlastnost data (má value
), ne obojí.
Pokud se pokusíme dodat obě get
a value
ve stejném deskriptoru bude chyba:
// Error: Invalid property descriptor.
Object.defineProperty({}, 'prop', {
get() {
return 1
},
value: 2
});
Chytřejší getters/setters
Getters/setters lze použít jako obaly nad „skutečnými“ hodnotami vlastností, abyste získali větší kontrolu nad operacemi s nimi.
Například pokud chceme zakázat příliš krátké názvy pro user
, můžeme mít setter name
a ponechte hodnotu v samostatné vlastnosti _name
:
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); // Pete
user.name = ""; // Name is too short...
Jméno je tedy uloženo v _name
vlastnost a přístup se provádí pomocí getter a setter.
Technicky je externí kód schopen přistupovat k názvu přímo pomocí user._name
. Existuje však široce známá konvence, že vlastnosti začínající podtržítkem "_"
jsou vnitřní a neměli byste se jich dotýkat zvenčí objektu.
Použití pro kompatibilitu
Jedním z velkých využití přístupových prvků je to, že umožňují kdykoli převzít kontrolu nad „běžnou“ datovou vlastností tím, že ji nahradí getter a setter a vyladí její chování.
Představte si, že jsme začali implementovat uživatelské objekty pomocí datových vlastností name
a age
:
function User(name, age) {
this.name = name;
this.age = age;
}
let john = new User("John", 25);
alert( john.age ); // 25
…Ale dříve nebo později se věci mohou změnit. Místo age
můžeme se rozhodnout uložit birthday
, protože je přesnější a pohodlnější:
function User(name, birthday) {
this.name = name;
this.birthday = birthday;
}
let john = new User("John", new Date(1992, 6, 1));
Co dělat se starým kódem, který stále používá age
majetek?
Můžeme se pokusit najít všechna taková místa a opravit je, ale to vyžaduje čas a může to být těžké, pokud tento kód používá mnoho dalších lidí. A kromě toho age
je hezké mít v user
, že?
Nechme to.
Přidání getteru pro age
řeší problém:
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 available
alert( john.age ); // ...as well as the age
Nyní funguje i starý kód a máme hezkou další vlastnost.