Un Symbol
es un tipo de dato primitivo en JavaScript que representa un identificador único y persistente.
A diferencia de otros tipos de datos primitivos como number
, string
o boolean
, los símbolos son siempre únicos.
Esto significa que cada vez que creas un nuevo símbolo, obtienes un valor distinto que no se puede duplicar
- Unicidad: Cada
Symbol
es único e inmutable, lo que significa que dos símbolos nunca serán iguales. - No ennumerabilidad: Las propiedades de tipo
Symbol
no son enumerables por defecto, lo que significa que no aparecerán en operaciones comofor...in
.
El tipo de dato Symbol
es uno de los tipos más avanzados y menos comprendidos, pero ofrece características útiles en ciertos contextos.
Creación de un Symbol
Para crear un Symbol
, simplemente utilizamos la función constructora Symbol()
sin la palabra clave new
.
const mySymbol = Symbol();
En este ejemplo, id
es un nuevo Symbol
único e inmutable.
Descripción de un Symbol
Es posible proporcionar una descripción opcional al crear un Symbol
. Esta descripción puede ser útil para identificar el propósito del Symbol
.
const nombre = Symbol('Nombre del símbolo');
En este caso, nombre
es un Symbol
con una descripción opcional.
La descripción de un símbolo no afecta su unicidad, pero puede ser útil para la depuración.
let simbolo = Symbol('mi simbolo');
console.log(simbolo.toString()); // Imprime: Symbol(mi simbolo)
La descripción se puede obtener a través del método toString()
del símbolo.
Unicidad de los Symbols
La principal característica de los símbolos su valor es siempre único y diferente del de todos los demás (incluso si dos símbolos tienen la misma descripción).
let simbolo1 = Symbol('descripcion');
let simbolo2 = Symbol('descripcion');
console.log(simbolo1 === simbolo2); // Imprime: false
En el ejemplo anterior,
simbolo1
ysimbolo2
son dos símbolos distintos, a pesar de que ambos tienen la misma descripción.- Es decir que cada llamada a
Symbol()
ha producido un identificador único.
Usos del Tipo Symbol
Evitar sobreescritura de propiedades
Uno de los usos más comunes de los Symbols
es como claves para las propiedades de objetos, para evitar que sean sobreescritas por accidente.
Vamos a verlo con un ejemplo
let objeto = {
propiedad: 'valor 1'
};
// Más adelante, otra parte del código añade una propiedad con el mismo nombre:
objeto.propiedad = 'valor 2';
console.log(objeto.propiedad); // Imprime: 'valor 2'
En este caso,
- Hemos definido un objeto literal, con la
propiedad
que valor valor 1 - Posteriormente, alguien por error, sobreescribe con el valor valor 2
// Creamos un Symbol con una descripción
let simboloPropiedad = Symbol('propiedad');
let objeto = {};
// Usamos el Symbol como clave para la propiedad
objeto[simboloPropiedad] = 'valor 1';
// Más adelante, intentamos añadir otra propiedad con el mismo nombre,
// pero como usamos un Symbol, no hay sobrescritura
let otroSimbolo = Symbol('propiedad');
objeto[otroSimbolo] = 'valor 2';
// Accedemos a las propiedades usando los Symbols
console.log(objeto[simboloPropiedad]); // Imprime: 'valor 1'
console.log(objeto[otroSimbolo]); // Imprime: 'valor 2'
En este ejemplo,
- Creamos un
Symbol
llamadosimboloPropiedad
y lo usamos como clave para una propiedad en el objetoobjeto
. - Luego, en otra parte del código, creamos un nuevo
Symbol
llamadootroSimbolo
. Aunque ambos símbolos tienen la misma descripción (‘propiedad’), son diferentes entre sí. - Por lo tanto, cuando asignamos
'valor 2'
aobjeto[otroSimbolo]
, no sobrescribe el valor deobjeto[simboloPropiedad]
, sino que crea una nueva propiedad.
Métodos y propiedades simbólicas
JavaScript incluye varios símbolos integrados que se usan como claves para métodos y propiedades especiales. Estos símbolos están definidos en el objeto Symbol
.
Símbolo | Descripción |
---|---|
Symbol.iterator | Este símbolo se usa para definir el iterador de un objeto. |
Symbol.toStringTag | Este símbolo se usa para definir el valor de la propiedad Symbol.toStringTag |
Symbol.toStringTag
que se usa para personalizar el resultado del método Object.prototype.toString()
.
Símbolos globales con Symbol.for()
Otro uso diferente de los Symbos ocurre con el método Symbol.for()
. Este permite crear un símbolo global que es accesible en todo el entorno de ejecución.
Si intentas crear un símbolo con la misma clave, devolverá el mismo símbolo. Es decir, Symbol.for()
lleva una especie de “registro global” de símbolos.
const simboloGlobal1 = Symbol.for('miSimbolo');
const simboloGlobal2 = Symbol.for('miSimbolo');
console.log(simboloGlobal1 === simboloGlobal2); // Imprime: true
En este caso,
- Creamos un símbolo global con la clave
'miSimbolo'
conSymbol.for('miSimbolo')
. - Al llamar nuevamente a
Symbol.for('miSimbolo')
, JavaScript no crea un nuevo símbolo. - En lugar de eso, busca en el registro global de símbolos y, si ya existe un símbolo con esa clave (como
'miSimbolo'
), te devuelve el mismo símbolo. - Como son el mismo símbolo, la comparación
simboloGlobal1 === simboloGlobal2
devuelvetrue
.
Esto es diferente de crear símbolos con Symbol()
, que siempre genera un símbolo único, incluso si usas la misma descripción.
En cambio, Symbol.for()
asegura que el símbolo creado sea único a nivel global, y si ya existe un símbolo con esa clave, te devolverá el mismo símbolo.
Ejemplos prácticos
Propiedades pseudo-privadas
JavaScript incorpora el concepto de propiedades privadas en 2022. Pero anteriormente, era frecuente usar Symbols
como una solución “parcial” para encapsulamiento.
const propiedadPrivada = Symbol('privado');
class MiClase {
constructor(valor) {
this[propiedadPrivada] = valor;
}
obtenerValor() {
return this[propiedadPrivada];
}
}
let instancia = new MiClase(123);
console.log(instancia.obtenerValor()); // Imprime: 123
En este ejemplo,
propiedadPrivada
es un símbolo que actúa como una “pseudo”-propiedad privada de la claseMiClase
.- Solo podríamos acceder a la propiedad si tenemos el símbolo accesible.
Evitar colisiones de nombres de propiedades
Los símbolos pueden ser útiles para evitar colisiones de nombres de propiedades en objetos, ya que son únicos e inmutables.
const NOMBRE = Symbol('nombre');
const persona = {
[NOMBRE]: 'Juan',
edad: 30,
};
console.log(persona[NOMBRE]); // Resultado: Juan
En este caso, NOMBRE
es un Symbol
utilizado como clave para el nombre de una persona en el objeto persona
.
Iteración de propiedades
Aunque las propiedades de tipo Symbol
no son enumerables por defecto. Es decir, que no son accesibles a través de métodos estándar como Object.keys()
o for...in
.
Sin embargo, podemos utilizar el método Object.getOwnPropertySymbols()
para obtener todas las propiedades de tipo Symbol
de un objeto.
const clave1 = Symbol('clave1');
const clave2 = Symbol('clave2');
const miObjeto = {
[clave1]: 'Valor 1',
[clave2]: 'Valor 2',
nombre: 'Objeto',
};
const symbols = Object.getOwnPropertySymbols(miObjeto);
console.log(symbols); // Resultado: [Symbol(clave1), Symbol(clave2)]
En este ejemplo, symbols
contiene los Symbol
utilizados como claves en el objeto miObjeto
.