Az ECMAScript 6 olyan hatékony új API-kat tartalmaz, mint a Proxy() és hasznos társ, a Reflect. Itt szeretném megvizsgálni, hogyan használhatjuk ki a proxy-k és szimbólumok kombinációját, hogy „titkos” mix-ineket hozzunk létre az objektumaihoz!
Ha még nem használta a szimbólumok előnyeit, javaslom, hogy olvassa el őket az MDN dokumentációban. Röviden:
- a szimbólumok primitívek
- objektumok, például karakterláncok kulcsaként használhatók
- egy szimbólum csak azon hivatkozáson keresztül használható, amellyel létrehozták – vagyis egy szimbólum csak önmagával egyenlő
- az előző miatt nem szerializálhatók JSON-ba, illetve nem eleveníthetők fel onnan
- ha nem fér hozzá a hatókörében lévő szimbólumhoz, nem tudja elolvasni!
Használhatunk szimbólumokat a meglévő objektumok kiegészítésére (vagy terjesztésére), és olyan tulajdonságokat adhatunk hozzá, amelyekről tudjuk, hogy nem ütköznek az objektum meglévő tulajdonságaival. Más szóval, a szimbólumok hatékony eszközt jelenthetnek biztonságos „mix-in” létrehozásához vagy „branding” objektumok létrehozásához.
const entityTypeSym: unique symbol = Symbol(); type WithEntityType< T extends object, S extends symbol > = T & { readonly [entityTypeSym]: S; }; const USER: unique symbol = Symbol(); type WithUserType<T extends object> = WithEntityType<T, typeof USER>; function withUserType<T extends object>( object: T ): WithUserType<T> { return { ...object, [entityTypeSym]: USER, }; } function isUserType<T>(value: T): value is WithUserType<T> { return value !== null && typeof value === "object" && value[entityTypeSym] === USER; } const data = { username: "foo" } as const; // data: { username: string } const user = withUserType(data); // user: { [entityTypeSym]: USER, username: string }
Ez az egyszerű megvalósítás meglehetősen ügyes, de van néhány mellékhatása – nevezetesen, hogy az objektum feletti iteráció felsorolja a szimbólumot, és azt szeretnénk, hogy a mix-in átláthatatlanabb legyen. Ezt megkerülhetjük Object.defineProperty()
-el, de az objektumokhoz kapcsolódó bármely tulajdonság általában elérhetővé válik a Object.getOwnPropertyDescriptors()
belső implementációjával.
// :( for (const prop in user) { alert(prop); // Uncaught TypeError: Cannot convert a Symbol value to a string }
Mi lenne, ha a tulajdonunkat elérhetővé tudnánk tenni szimbólumokkal, de anélkül, hogy az valójában kapcsolódna a tárgyhoz? Ekkor, ha a entityTypeSym
kulcsunk privát marad (a mi modulunkra terjed ki), akkor csak a modulunkban lévő függvények tudják olvasni vagy írni az értékét.
Ez valójában a Proxy()-nak köszönhetően lehetséges!
function withUserType<T extends object>( object: T ): WithUserType<T> { return new Proxy(object, { get: (target, prop, receiver) => { if (prop === entityTypeSym) { return USER; } return Reflect.get(target, prop, receiver); }, }) as WithUserType<T>; }
A withUserType
visszatérési értéke egy homlokzati objektum, amely beburkolja az adott object
-et, és ténylegesen elfogja az objektumon lévő tulajdonság eléréséhez szükséges beépített viselkedést! Tehát míg a többi proxy függvény nincs csapdában, és egyszerűen továbbít az objektumra, elkapjuk a hozzáférést a privát entityTypeSym
szimbólumunkhoz, és visszaadjuk a típusát.
Mivel a proxy objektumunk többi metódusa nincs csapdában, az olyan funkciók, mint a Object.getOwnPropertyDescriptors()
, soha nem adják vissza a szimbólumunkat – ami azt jelenti, hogy a modulunkon kívül a kód áthaladhat a proxy objektumunkon, és nem fér hozzá a személyes adatainkhoz!
Azonban a modulunkban található minden olyan funkció, amely nem hozzáfér a szimbólumhoz, olvashatja és írhatja azt, azzal a biztossággal, hogy más kód nem manipulálhatta az értékeket.
function getType<T>(value: T): symbol | undefined { return value !== null && typeof value === "object" && value[entityTypeSym]; } getType(user); // Code outside this module can never read // entityTypeSym nor value[entityTypeSym] // without our getType() function
Mire használhatnánk ezt?
Van egy tárgyam. Gyakran hivatkoznom kell a kulcsok számára; ennek általános módja azonban, a Object.keys(object).length
egy kissé költséges művelet, tekintve, hogy az összes felsorolható kulcsból álló tömb létrehozására van szükség, csak hogy megkapja a méretét.
Mi lenne, ha egy proxy segítségével nyomon követhetnénk egy objektumon lévő tulajdonságok számát, és egy privát szimbólumot az érték eléréséhez?
Elég ügyes, igaz?
Mondja el nekem a megjegyzésekben, hogy milyen más nagyszerű alkalmazásokat talált ki proxykhoz és szimbólumokhoz!