Jak víme, objekty mohou ukládat vlastnosti.
Doposud pro nás byla nemovitost jednoduchým párem „klíč–hodnota“. Ale vlastnost objektu je ve skutečnosti flexibilnější a výkonnější věc.
V této kapitole prostudujeme další možnosti konfigurace a v další se podíváme, jak je neviditelně přeměnit na funkce getter/setter.
Příznaky vlastností
Vlastnosti objektu, kromě value
, mají tři speciální atributy (takzvané „vlajky“):
writable
– pokudtrue
, hodnotu lze změnit, jinak je pouze pro čtení.enumerable
– pokudtrue
, pak jsou uvedeny v smyčkách, jinak nejsou uvedeny.configurable
– pokudtrue
, vlastnost lze smazat a tyto atributy upravit, jinak ne.
Ještě jsme je neviděli, protože se obecně nezobrazují. Když vytvoříme vlastnost „obvyklým způsobem“, všechny jsou true
. Ale také je můžeme kdykoli změnit.
Nejprve se podívejme, jak tyto příznaky získat.
Metoda Object.getOwnPropertyDescriptor umožňuje dotaz na úplné informace o nemovitosti.
Syntaxe je:
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
- Objekt, ze kterého se mají získat informace.
propertyName
- Název vlastnosti.
Vrácená hodnota je takzvaný objekt „deskriptoru vlastnosti“:obsahuje hodnotu a všechny příznaky.
Například:
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
Pro změnu příznaků můžeme použít Object.defineProperty.
Syntaxe je:
Object.defineProperty(obj, propertyName, descriptor)
obj
,propertyName
- Objekt a jeho vlastnost pro použití deskriptoru.
descriptor
- Objekt deskriptoru vlastnosti, který se má použít.
Pokud vlastnost existuje, defineProperty
aktualizuje své vlajky. Jinak vytvoří vlastnost s danou hodnotou a příznaky; v takovém případě, pokud příznak není zadán, předpokládá se false
.
Zde je například vlastnost name
je vytvořen se všemi falešnými příznaky:
let user = {};
Object.defineProperty(user, "name", {
value: "John"
});
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
Porovnejte jej s „normálně vytvořeným“ user.name
výše:nyní jsou všechny vlajky falešné. Pokud to není to, co chceme, měli bychom je nastavit na true
v descriptor
.
Nyní se podívejme na účinky vlajek na příkladu.
Bez možnosti zápisu
Udělejme user.name
nezapisovatelné (nelze znovu přiřadit) změnou writable
příznak:
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error: Cannot assign to read only property 'name'
Nyní nikdo nemůže změnit jméno našeho uživatele, pokud nepoužije své vlastní defineProperty
přepsat naše.
V non-strict režimu nedochází k žádným chybám při zápisu do nezapisovatelných vlastností a podobně. Operace se ale stále nepovede. Akce porušující příznak jsou pouze tiše ignorovány v non-strict.
Zde je stejný příklad, ale vlastnost je vytvořena od začátku:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
// for new properties we need to explicitly list what's true
enumerable: true,
configurable: true
});
alert(user.name); // John
user.name = "Pete"; // Error
Nevyčíslitelné
Nyní přidáme vlastní toString
na user
.
Obvykle je to vestavěný toString
pro objekty není vyčíslitelný, nezobrazuje se v for..in
. Ale pokud přidáme toString
naše vlastní, pak se ve výchozím nastavení zobrazí v for..in
, takto:
let user = {
name: "John",
toString() {
return this.name;
}
};
// By default, both our properties are listed:
for (let key in user) alert(key); // name, toString
Pokud se nám to nelíbí, můžeme nastavit enumerable:false
. Pak se nezobrazí v for..in
smyčka, stejně jako ta vestavěná:
let user = {
name: "John",
toString() {
return this.name;
}
};
Object.defineProperty(user, "toString", {
enumerable: false
});
// Now our toString disappears:
for (let key in user) alert(key); // name
Vlastnosti, které nelze vyčíslit, jsou také vyloučeny z Object.keys
:
alert(Object.keys(user)); // name
Nelze konfigurovat
Nekonfigurovatelný příznak (configurable:false
) je někdy přednastaveno pro vestavěné objekty a vlastnosti.
Nekonfigurovatelnou vlastnost nelze smazat, nelze upravit její atributy.
Například Math.PI
je nezapisovatelný, nevyčíslitelný a konfigurovatelný:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
Programátor tedy nemůže změnit hodnotu Math.PI
nebo jej přepište.
Math.PI = 3; // Error, because it has writable: false
// delete Math.PI won't work either
Nemůžeme také změnit Math.PI
být writable
znovu:
// Error, because of configurable: false
Object.defineProperty(Math, "PI", { writable: true });
S Math.PI
nemůžeme dělat absolutně nic .
Udělat nemovitost nekonfigurovatelnou je jednosměrná cesta. Nemůžeme jej změnit zpět pomocí defineProperty
.
Poznámka:configurable: false
zabraňuje změnám příznaků vlastnosti a jejímu mazání a zároveň umožňuje měnit její hodnotu.
Zde user.name
není konfigurovatelný, ale stále jej můžeme změnit (protože je zapisovatelný):
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
configurable: false
});
user.name = "Pete"; // works fine
delete user.name; // Error
A tady vytvoříme user.name
„navždy zapečetěná“ konstanta, stejně jako vestavěná Math.PI
:
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false,
configurable: false
});
// won't be able to change user.name or its flags
// all this won't work:
user.name = "Pete";
delete user.name;
Object.defineProperty(user, "name", { value: "Pete" });
Jediná možná změna atributu:zapisovatelný true → false Existuje malá výjimka ohledně změny příznaků.
Můžeme změnit writable: true
na false
pro nekonfigurovatelnou vlastnost, čímž se zabrání změně její hodnoty (pro přidání další vrstvy ochrany). Ne naopak.
Object.defineProperties
Existuje metoda Object.defineProperties(obj, deskriptory), která umožňuje definovat mnoho vlastností najednou.
Syntaxe je:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
Například:
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});
Můžeme tedy nastavit mnoho vlastností najednou.
Object.getOwnPropertyDescriptors
Chcete-li získat všechny deskriptory vlastností najednou, můžeme použít metodu Object.getOwnPropertyDescriptors(obj).
Společně s Object.defineProperties
lze jej použít jako způsob klonování objektu „s vědomím příznaků“:
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Normálně, když klonujeme objekt, používáme ke kopírování vlastností přiřazení, jako je toto:
for (let key in user) {
clone[key] = user[key]
}
…To ale nekopíruje vlajky. Pokud tedy chceme „lepší“ klon, pak Object.defineProperties
je preferováno.
Dalším rozdílem je, že for..in
ignoruje symbolické a nevyčíslitelné vlastnosti, ale Object.getOwnPropertyDescriptors
vrátí vše deskriptory vlastností včetně symbolických a nevyčíslitelných.
Globální utěsnění objektu
Deskriptory vlastností fungují na úrovni jednotlivých vlastností.
Existují také metody, které omezují přístup k celku objekt:
- Object.preventExtensions(obj)
- Zakazuje přidávání nových vlastností k objektu.
- Object.seal(obj)
- Zakazuje přidávání/odebírání vlastností. Nastaví
configurable: false
pro všechny existující vlastnosti. - Object.freeze(obj)
- Zakazuje přidávání/odebírání/změnu vlastností. Nastaví
configurable: false, writable: false
pro všechny existující vlastnosti.
A také pro ně existují testy:
- Object.isExtensible(obj)
- Vrátí
false
pokud je přidávání vlastností zakázáno, jinaktrue
. - Object.isSealed(obj)
- Vrátí
true
pokud je přidávání/odebírání vlastností zakázáno a všechny existující vlastnosti majíconfigurable: false
. - Object.isFrozen(obj)
- Vrátí
true
pokud je přidávání/odebírání/změna vlastností zakázáno a všechny aktuální vlastnosti jsouconfigurable: false, writable: false
.
Tyto metody se v praxi používají jen zřídka.