Symbols
Tipo primitivo Symbol, símbolos conocidos y casos de uso
¿Qué son los Symbols?
Symbol es un tipo primitivo introducido en ES6 que representa un identificador único.
A diferencia de las cadenas, cada Symbol está garantizado de ser único, lo que los hace perfectos para claves de propiedades
que no colisionarán con otras propiedades.
Características Clave
- Único — Cada llamada a Symbol() crea un símbolo único
- Inmutable — No puede cambiarse una vez creado
- No enumerable — Oculto de for...in y Object.keys()
- Descripción — Cadena opcional para propósitos de depuración
Creando Symbols
// Crear un símbolo
const sym1 = Symbol();
const sym2 = Symbol();
console.log(sym1 === sym2); // false — siempre único
// Symbol con descripción (para depuración)
const id = Symbol("id");
console.log(id.toString()); // "Symbol(id)"
console.log(id.description); // "id"
// Misma descripción, aún símbolos diferentes
const a = Symbol("nombre");
const b = Symbol("nombre");
console.log(a === b); // false
// ⚠️ No se puede usar 'new'
new Symbol(); // TypeError: Symbol no es un constructor
Symbols como Claves de Propiedades
const ID = Symbol("id");
const SECRETO = Symbol("secreto");
const usuario = {
nombre: "Alicia",
[ID]: 12345,
[SECRETO]: "contraseña123"
};
// Acceder con el símbolo
console.log(usuario[ID]); // 12345
console.log(usuario[SECRETO]); // "contraseña123"
// Las propiedades Symbol están ocultas de la enumeración normal
console.log(Object.keys(usuario)); // ["nombre"]
console.log(JSON.stringify(usuario)); // {"nombre":"Alicia"}
for (const clave in usuario) {
console.log(clave); // Solo "nombre"
}
// Pero pueden accederse con:
console.log(Object.getOwnPropertySymbols(usuario)); // [Symbol(id), Symbol(secreto)]
console.log(Reflect.ownKeys(usuario)); // ["nombre", Symbol(id), Symbol(secreto)]
Symbol.for() - Registro Global
// Crear/recuperar del registro de símbolos global
const globalSym1 = Symbol.for("app.id");
const globalSym2 = Symbol.for("app.id");
console.log(globalSym1 === globalSym2); // true — ¡mismo símbolo!
// Obtener la clave para un símbolo global
console.log(Symbol.keyFor(globalSym1)); // "app.id"
// Los símbolos regulares no están en el registro
const symLocal = Symbol("local");
console.log(Symbol.keyFor(symLocal)); // undefined
// Caso de uso: Compartir símbolos entre módulos/iframes
// En el módulo A:
const CLAVE_COMPARTIDA = Symbol.for("miApp.claveCompartida");
// En el módulo B:
const clave = Symbol.for("miApp.claveCompartida");
// clave === CLAVE_COMPARTIDA
Símbolos Conocidos (Well-Known Symbols)
JavaScript tiene símbolos integrados que personalizan el comportamiento de objetos:
// Symbol.iterator - Hacer objetos iterables
const rango = {
inicio: 1,
fin: 5,
[Symbol.iterator]() {
let actual = this.inicio;
const fin = this.fin;
return {
next() {
if (actual <= fin) {
return { value: actual++, done: false };
}
return { done: true };
}
};
}
};
console.log([...rango]); // [1, 2, 3, 4, 5]
// Symbol.toStringTag - Personalizar Object.prototype.toString
class MiClase {
get [Symbol.toStringTag]() {
return "MiClase";
}
}
console.log(Object.prototype.toString.call(new MiClase()));
// "[object MiClase]"
// Symbol.toPrimitive - Controlar conversión de tipos
const dinero = {
cantidad: 100,
moneda: "USD",
[Symbol.toPrimitive](pista) {
if (pista === "number") return this.cantidad;
if (pista === "string") return `${this.cantidad} ${this.moneda}`;
return this.cantidad; // predeterminado
}
};
console.log(+dinero); // 100
console.log(`${dinero}`); // "100 USD"
console.log(dinero + 50); // 150
Más Símbolos Conocidos
// Symbol.hasInstance - Personalizar instanceof
class MiArray {
static [Symbol.hasInstance](instancia) {
return Array.isArray(instancia);
}
}
console.log([] instanceof MiArray); // true
console.log({} instanceof MiArray); // false
// Symbol.species - Constructor para objetos derivados
class MiArrayExtendido extends Array {
static get [Symbol.species]() {
return Array; // map(), filter() retornan Arrays regulares
}
}
const arr = new MiArrayExtendido(1, 2, 3);
const mapeado = arr.map(x => x * 2);
console.log(mapeado instanceof MiArrayExtendido); // false
console.log(mapeado instanceof Array); // true
// Symbol.isConcatSpreadable
const arr1 = [1, 2];
const noExpandible = {
0: 3,
1: 4,
length: 2,
[Symbol.isConcatSpreadable]: false
};
console.log(arr1.concat(noExpandible)); // [1, 2, {0: 3, 1: 4, ...}]
noExpandible[Symbol.isConcatSpreadable] = true;
console.log(arr1.concat(noExpandible)); // [1, 2, 3, 4]
Casos de Uso Prácticos
// 1. Propiedades tipo privado (antes de la sintaxis #private)
const _saldo = Symbol("saldo");
class CuentaBancaria {
constructor(inicial) {
this[_saldo] = inicial;
}
depositar(cantidad) {
this[_saldo] += cantidad;
}
obtenerSaldo() {
return this[_saldo];
}
}
const cuenta = new CuentaBancaria(100);
console.log(cuenta.obtenerSaldo()); // 100
console.log(cuenta._saldo); // undefined
console.log(Object.keys(cuenta)); // []
// 2. Evitar colisión de propiedades en mixins
const volable = (() => {
const VELOCIDAD_VUELO = Symbol("velocidadVuelo");
return {
[VELOCIDAD_VUELO]: 100,
volar() {
console.log(`Volando a ${this[VELOCIDAD_VUELO]} mph`);
}
};
})();
// 3. Marcado de tipo (Type branding)
const TipoCadena = Symbol("Cadena");
const TipoNumero = Symbol("Numero");
function marcado(valor, tipo) {
return { valor, [tipo]: true };
}
const str = marcado("hola", TipoCadena);
console.log(str[TipoCadena]); // true
console.log(str[TipoNumero]); // undefined
💡 Puntos Clave
- • Symbol() crea identificadores únicos e inmutables
- • Usa símbolos para claves de propiedades para evitar colisiones
- • Symbol.for() crea símbolos globales compartidos
- • Los símbolos conocidos (Symbol.iterator, etc.) personalizan el comportamiento de objetos
- • Las propiedades Symbol están ocultas de la enumeración normal
- • Excelentes para bibliotecas y frameworks para evitar conflictos
Práctica y patrones de uso
- • Añade una propiedad basada en Symbol a un objeto y confirma que permanece oculta de
Object.keys. - • Implementa
[Symbol.iterator]en una colección personalizada y asegúrate de quefor...offuncione. - • Usa
Symbol.toPrimitivepara formatear valores de moneda y prueba las rutas de coerción string/number. - • Crea un registro con
Symbol.fory demuestra cómo dos archivos comparten la misma clave de símbolo.