Javascript. ¿Conoces el tipo Symbol?
Una introducción al tipo Symbol que introdujo ES2015
English version: https://medium.com/@ger86/javascript-do-you-know-the-symbol-type-9dfbf40b6eef
Entre las numerosas novedades que trajo ES6 (también conocido como ECMAScript 2015) hay una que diría que ha pasado bastante desapercibida, sobre todo si comparamos su popularidad con otras características como el spread operator o los tagged templates de los cuales ya hablé en otros artículos:
Tal y como sugiere el título, me estoy refiriendo a la definición del nuevo tipo Symbol que viene a unirse al resto de tipos primitivos: string
, number
, boolean
, null
y undefined
. Así que en este artículo pretendo enumerar las principales características de este nuevo tipo así como sus principales usos.
¡Vamos a verlas!
El tipo Symbol
Lo primero de todo para entender el tipo Symbol es acudir a la definición que se hace de él en la documentación de Mozilla:
Symbol es un tipo de datos cuyos valores son únicos e immutables. Dichos valores pueden ser utilizados como identificadores (claves) de las propiedades de los objetos. Cada valor del tipo Symbol tiene asociado un valor del tipo String o Undefined que sirve únicamente como descripción del símbolo.
Para seros sinceros, la primera vez que leí esta definición la verdad es que me quedé igual que estaba por lo que añadiré una que nos dará una idea mejor de lo que en realidad es el tipo Symbol:
El tipo Symbol nos permite obtener valores que no pueden volver a ser creados, es decir, son identificadores únicos e inmutables.
Atendiendo a esta definición tiene más sentido la que proporciona MDN ya que puesto que los valores creados como Symbol serán únicos, podremos emplearlos para identificar propiedades de objetos.
Creando un Symbol
Del mismo modo que podemos crear tipos primitivos empleando funciones factorías ( Boolean(false)
) la forma con la que crearemos valores del tipo Symbol será exactamente igual:
const myFirstSymbol = Symbol();
Aunque también podremos pasar un string
para crearlo:
const foo = Symbol('foo');
que no tiene más uso que poder identificar dicho Symbol cuando estemos depurando el código, ya que al imprimirlo por pantalla tendremos:
console.log(foo); // Symbol(foo);
En cualquier caso, cada vez que invoquemos a la función Symbol()
obtendremos un identificador único que será diferente de cualquier otro Symbol creado anterior o posteriormente.
Por ejemplo:
Symbol() === Symbol() // false
O por ejemplo:
const a = Symbol('a');const otherA = Symbol('a');a === otherA // false
Características del tipo Symbol
Entre las principales del tipo Symbol podemos mencionar las siguientes:
- Todo los valores de tipo Symbol creados mediante la función factoría
Symbol()
son únicos de modo que jamás colisionarán unos con otros. - Las propiedades de un objeto cuya clave sea un Symbol no son enumeradas mediante las funciones
Object.getOwnPropertyNames()
oObject.keys()
o en los bucles de tipofor...of
ofor...in
. - En el caso de que queramos enumerar las propiedades de un objeto cuya clave sea un Symbol deberemos emplear la función de ES6
Object.getOwnPropertySymbols()
. - Los valores de tipo Symbol no “sufren” casting de tipos, es decir, el siguiente código daría un error:
const foo = Symbol('foo');console.log('This is the symbol foo: ' + foo); // Error
Accediendo a Symbols creados anteriormente
Si bien es cierto que cada vez que empleamos la función Symbol()
obtenemos un identificador único, Javascript nos proporciona el método Symbol.for(key)
para o bien crear un Symbol asociado a la clave pasada, o bien recuperar el mismo del registro de Symbols.
Gracias a esta función, si por ejemplo creamos un Symbol asignándole una clave:
const foo = Symbol.for('foo');
Posteriormente podremos recuperar dicho Symbol del siguiente modo:
const bar = Symbol.for('foo');foo === bar; // true
❗❗❗ Cuidado. Para obtener el mismo símbolo es necesario haberlo creado empleando la función Symbol.for
por lo que el siguiente código daría como resultado false
:
const foo = Symbol('foo');const bar = Symbol.for('foo');foo === bar; // false
Symbols predefinidos
Javascript trae definidos unos cuantos Symbols dentro de la clase Symbol que son empleados por otros tipos de cara a proporcionar diferentes características que no eran accesibles a los desarrolladores antes de la llegada de ES6.
Probablemente el más conocido sea Symbol.iterator
, el cual nos permite acceder al iterador por defecto de un objeto:
const arr = [1, 2, 3];const iterator = arr[Symbol.iterator]();console.log(iterator.next()); // {value: 'a', done: false}
Usos de los Symbols
Bien, ahora que ya sabemos crear Symbols, veamos algunos de sus principales usos.
Symbols como claves de propiedades de objetos
Tal y como menciona la documentación de Mozilla, el uso más característico para los Symbol es ser empleados como identificadores para las propiedades de los objetos:
const foo = Symbol();const myObject = {};myObject[foo] = 'foo';myObject['bar'] = 'bar';console.log(myObject); // {bar: "bar", Symbol(): "foo"}console.log(foo in myObject); // trueconsole.log(myObject[foo]); // 'foo'console.log(Object.getOwnPropertyNames(myObject)); // ['bar']console.log(Object.keys(myObject)); ['bar']
Dado que los métodos Object.getOwnPropertyNames
y Object.keys
no devuelven las claves declaradas mediante Symbols, podría parecer que otro de sus usos podría ser permitirnos declarar propiedades privadas dentro de objetos. Ya que… una vez empleado el Symbol()
para una propiedad no habría manera de recuperarlo.
Sin embargo, existen alternativas los métodos anteriores para enumerar las propiedades de los objetos. Por ejemplo, el método Reflect.ownKeys()
del objeto Reflect
sí que es capaz de enumerar las propiedades declaradas mediante Symbols:
console.log(Reflect.ownKeys(myObject)); ['bar', Symbol]
Prevenir colisiones de nombres de propiedades
Otra de las características que cubren los Symbol es la posibilidad de que distintos desarrolladores o librerías añadan propiedades a objetos sin modificar las ya existentes.
Por ejemplo, se me ocurre ese problema tan de 2009 que provocaba que distintas librerías sobrescribiesen el objeto jQuery
, dando lugar a multitud de fallos y errores. Gracias a los Symbol, esto es tan sencillo como:
// Library Aconst jQuerySymbolA = Symbol('library a');
window[jQuerySymbolA] = $;// Library Bconst jQuerySymbolB = Symbol('library b');
window[jQuerySymbolB] = $;
De este modo estaremos asegurando que el objeto jQuery no es sobrescrito por otras librerías y que siempre accederemos al nuestro.
Conclusiones
Espero haber resumido en este artículo las características y casos de uso más importantes de los Symbol. Por supuesto, esto tan sólo es la punta del iceberg por lo que a partir de ahora es tarea nuestra familiarizarnos con su uso o buscar nuevas formas de aprovechar todo lo que nos ofrecen.
Referencias
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇