Los símbolos bien conocidos en ES6
Los símbolos bien conocidos

Los símbolos bien conocidos nos permiten cambiar el comportamiento de ciertas características internas a JavaScript. Se trata de que el programador pueda acceder a ellas para llevar a cabo modificaciones en la forma predeterminada de actuar. Hay una tabla en la especificación EcmaScript 6 Well-Known Symbols sobre los símbolos bien conocidos, donde se da una breve descripción.
Los siguientes enlaces llevan a apartados de este tema que exponen todos los símbolos bien conocidos de ES6:
- Symbol.toStringTag
- Symbol.match, search, split y replace
- Symbol.hasInstance
- Symbol.toPrimitive
- Symbol.isConcatSpreadable
- Symbol.unscopables
- Symbol.species
- Symbol.iterator
Símbolo bien conocido toStringTag
El símbolo bien conocido toStringTag
modifica el comportamiento del método toString()
. Éste es un método de Object que devuelve la cadena [object TIPO]
siendo "TIPO" el correspondiente String, Number, Boolean, etc. Pero toString()
es sobrescrito en las instancias. Así por ejemplo Object.prototype.toString.call("abc")
devuelve [object String]
mientras que "abc".toString()
devuelve "abc"
.
Consultar toString()
del prototipo ha venido sirviendo para conocer el tipo de datos de cualquier valor. Pues para los tipos primitivos string, number, boolean, null, undefined y las funciones podemos consultar el tipo con typeof
. Para el resto de valores esa consulta siempre nos devolverá object
. Así si quisiéramos saber si un valor es un Array tendríamos que usar el toString()
del prototipo y comprobar que resulta [object Array]
. En el siguiente ejemplo podrá ver todo esto en ejecución:
Ejemplo: Tipos de datos en JavaScript
Un constructor es una función. Y la propiedad name
para las funciones es algo nuevo de ES6. Podrá comprobar que consultando el nombre del constructor (val.constructor.name
) obtenemos también ese "TIPO" que aparece en la expresión [object TIPO]
que resulta al ejecutar toString()
del prototipo. Aunque no para null y undefined. Además para los objetos generados a partir de un propio constructor es la única forma de obtener el tipo correspondiente, pues incluso el toString()
del prototipo resulta [object Object]
.
Podemos utilizar el símbolo bien conocido toStringTag
para modificar el resultado del toString()
para un objeto de un constructor propio. En el siguiente código tenemos un constructor y cambiamos en su prototipo el símbolo [Symbol.toStringTag]
a una cadena de texto. En este caso usamos el nombre del constructor, pero podría ser cualquier String. Tanto toString()
del valor (la instancia) como del prototipo ahora nos devuelven [object MiConstructor]
.
//Modificando el toString() de un constructor function MiConstructor(){} MiConstructor.prototype[Symbol.toStringTag] = MiConstructor.name; let valor = new MiConstructor(); //Estas dos consultas devuelven [object MiConstructor] console.log(valor.toString()); console.log(Object.prototype.toString.call(valor));
console.log()
.Símbolos bien conocidos Symbol.match, search, split y replace
Los métodos de expresiones regulares que aplican a String son match()
, search()
, split()
y replace()
. Podemos modificar el comportamiento de estos métodos usando los símbolos bien conocidos Symbol.match
, Symbol.search
, Symbol.split
y Symbol.replace
. En este apartado sólo probaremos el símbolo para match
.
El método match()
con búsqueda global (modificador "g") busca una o todas las coincidencias de una expresión regular sobre un String, devolviéndolas en un Array. Sin búsqueda global devuelve también un Array con la primera coincidencia en la posición cero del Array y en el resto de posiciones estarán las coincidencias con los grupos de captura. En el siguiente código hay búsqueda global por lo que tenemos un Array con todos los números existentes en el siguiente texto:
let texto = `Cervantes nació el 29 de septiembre de 1547. En el año 1605 publicó El Quijote con 664 páginas.`; let arr = texto.match(/\d+/g); console.log(arr); // ["29", "1547", "1605", "664"]
Supongamos que en lugar de usar una expresión regular nos creamos nuestro propio método para buscar esos números, entendiendo un número como una secuencia de dígitos. La siguiente función usa el método reduce()
de Array aplicado a un String. Va tomando los caracteres uno a uno y los va reduciendo hasta un único resultado. Para ver si un carácter es un dígito consultamos su código ASCII con el método charCodeAt(0)
. Si lo es lo concatenamos al anterior. Si no lo es y el último carácter reducido no es un espacio le agregamos uno. Al finalizar tendremos un String como "29 1547 1605 664 "
para el texto del ejemplo anterior. Con trim()
quitamos el espacio final y acabamos devolviendo un Array sólo con los números:
function buscarNumeros(texto){ return Array.prototype.reduce.call(texto, (anterior, actual) => { let key = actual.charCodeAt(0); if ((key>47) && (key<58)){ anterior += actual; } else if (anterior && (anterior[anterior.length-1]!==" ")) { anterior += " "; } return anterior; }, "").trim().split(" "); }
Aplicado al mismo texto obtenemos igual resultado que con la expresión regular /\d+/g
usada antes:
let texto = `Cervantes nació el 29 de septiembre de 1547. En el año 1605 publicó El Quijote con 664 páginas.`; console.log( buscarNumeros(texto) ); // ["29", "1547", "1605", "664"]
Podemos usar esa función buscarNumeros()
para cambiar el comportamiento del método match()
. Sólo tenemos que declarar una expresión regular vacía y cambiar su [Symbol.match]
a nuestra función:
let reg = new RegExp(); reg[Symbol.match] = buscarNumeros; console.log( texto.match(reg) ); // ["29", "1547", "1605", "664"]
A partir de ahí vemos que texto.match(reg)
buscará con nuestra función y no con el método match
. Es posible simplificar el código anterior sabiendo que podemos enviar un argumento con un objeto con los símbolos necesarios, en este caso sólo [Symbol.match]
:
console.log( texto.match({[Symbol.match]: buscarNumeros}) ); // ["29", "1547", "1605", "664"]
En el siguiente ejemplo interactivo podrá probar este código así como con expresión regular y directamente con nuestra función buscarNumeros()
:
Ejemplo: Symbol.match
texto.match(/\d+/g)
devuelve
buscarNumeros(texto)
devuelve
Primero con let reg = new RegExp(); reg[Symbol.match] = buscarNumeros;
Entonces texto.match(reg)
devuelve
texto.match({[Symbol.match]: buscarNumeros})
devuelve
Symbol.match
(disponible en la versión 49). Compruebe el soporte de su navegador de los símbolos bien conocidos en la web kangax.github.io.Símbolo bien conocido Symbol.hasInstance
En ES6 se introduce el método Symbol.hasInstance()
para las funciones, actuando como constructores, con objeto de obtener lo mismo que con el operador instanceof
para saber si un objeto es una instancia de un constructor:
function MiConstructor(){} let instancia = new MiConstructor(); console.log(instancia instanceof MiConstructor); // true console.log(MiConstructor[Symbol.hasInstance](instancia)); // true
Podemos utilizar ese símbolo para cambiar la forma de actuar de hasInstance()
así como del operador instanceof
. Hemos de usar ObjectdefineProperty
pues la propiedad es nonwritable:
function MiConstructor(){} Object.defineProperty(MiConstructor, Symbol.hasInstance, { value: function(valor){ //Esto produce un volcado de pila en CH51 //return (valor instanceof MiConstructor)?"SÍ":"NO"; return (valor.constructor.name==="MiConstructor")?"SÍ":"NO"; } }); let instancia = new MiConstructor(); // Debería ser "SÍ", pero CH51 y FF47 nos da true console.log(instancia instanceof MiConstructor); // "SÍ" en CH51 y FF47 console.log(MiConstructor[Symbol.hasInstance](instancia)); let str = new String("abc"); // Debería ser "NO", pero es true en CH51 (?) y false en FF47 console.log(str instanceof MiConstructor); // "NO" en CH51 y FF47 console.log(MiConstructor[Symbol.hasInstance](str));
Las marcas entre paréntesis de los comentarios indican los valores obtenidos en los navegadores actuales indicados, que aún no soportan el cambio en el comportamiento de instanceof
para que resulte igual que Symbol.hasInstance()
.
Este símbolo hasInstance
aún no es soportado completamente por los navegadores actuales Chrome 51 ni Firefox 47. En Chrome 51 el código return (valor instanceof MiConstructor) ? "SÍ" : "NO"
produce un volcado de pila (RangeError: Maximum call stack size exceeded), seguramente porque se producen llamadas entre instanceof
y hasInstanceOf()
que vuelcan la pila de ejecución. Sin embargo en Firefox no sucede. En todo caso ambos no devuelven los valores esperados "SÍ" y "NO" usando instanceof
que tendría que ejecutarse llamando al símbolo que hemos modificado.
Cambiando la devolución por return (valor.constructor.name === "MiConstructor") ? "SÍ" : "NO"
tampoco devuelve lo esperado cuando usamos instanceof
. Incluso para CH51 la variable String dice erróneamente que es instancia del constructor. En resumen, que hay que seguir esperando hasta que los navegadores adopten completamente este símbolo.
Símbolo bien conocido Symbol.toPrimitive
Operaciones como la suma de números y cadenas o comparación simple (==
) coercionan los objetos a tipos primitivos iguales para poder llevar a cabo dicha operación.
//La suma de un String y un Number coerciona el número a //cadena, concatenando ambas cadenas let x = "abc" + 123; console.log(x); // "abc123" console.log(typeof x); // "string" //Si el número es un objeto también lo coerciona let num = new Number(123); x = "abc" + num; console.log(x); // "abc123" console.log(typeof x); // "string" //La comparación simple también coerciona el número console.log(123 == "123"); // true //Pero la comparación estricta no lo hace console.log(123 === "123"); // false
Con la comparación hay que tener un cuidado especial. De hecho deberíamos siempre usar la comparación estricta, como se evidencia en el siguiente ejemplo. Vemos que con la comparación simple el Array se coerciona a String como aplicándole arr.toString()
, y ahí ambas variables son iguales:
let str = "1,2"; let arr = [1, 2]; console.log(arr.toString()) // "1,2" console.log(str == arr); // true console.log(str === arr); // false
Para probar el símbolo [Symbol.toPrimitive]
primero declaremos un constructor y modifiquemos el símbolo con una función y un argumento hint
necesario para funcionar la conversión a tipo primitivo. JavaScript envía ese argumento con los valores "string", "number" o "default". Según cada valor devolveremos un resultado:
function MiConstructor(valor){ this.valor = valor; } MiConstructor.prototype[Symbol.toPrimitive] = function(hint){ console.log("hint:" + hint); if (hint === "string"){ return this.valor + " es un String"; } else if (hint === "number"){ return this.valor; } else { return this.valor + " y "; } };
A continuación creamos una instancia del constructor y comprobamos el objeto devuelto:
//Componemos un objeto con un valor String que podría //coercionar a un número 123 válido let instancia = new MiConstructor("123"); console.log(instancia); // MiConstructor {valor: "123"}
Por último haremos algunas pruebas. Cuando JavaScript ejecuta String(arg)
es que va a convertir su argumento a String. Así que JavaScript tendrá que coercionar el objeto instancia
a un String por lo que necesitará usar el método Symbol.toPrimitive(hint)
que hemos modificado, siendo ahora hint = "string"
. Observe que si no hubiésemos modificado el símbolo tendríamos "[object Object]"
pues JavaScript representa un objeto como String con [object Object]
.
//hint: string console.log(String(instancia)); // "123 es un String" //Sin modificar el símbolo hubiésemos obtenido: // "[object Object]"
Pero si hacemos una operación de multiplicación JavaScrit necesitará dos números, por lo que hint = "number"
. En la operación coercionará la instancia a un Number por medio de nuestro método Symbol.toPrimitive("number")
. Si no hubiésemos modificado el símbolo la operación no sería posible pues no puede coercionar un objeto a un número:
//hint: number console.log(instancia * 2); // 246 //Sin modificar el símbolo hubiésemos obtenido: // NaN
En otros casos como cuando usamos el operador "+" o el comparador "==" tendremos hint = "default"
. El objeto instancia
coercionará con el método Symbol.toPrimitive("default")
que nos devuelve el String "123 y "
:
//hint: default console.log(instancia + "abc"); // "123 y abc" //Sin modificar el símbolo hubiésemos obtenido: // "[object Object]abc" console.log(instancia == "123 y "); // true //Sin modificar el símbolo hubiésemos obtenido: // false
Observe que si no hubiésemos modificado el símbolo la comparación instancia == "123 y "
resulta en "[object Object]" == "123 y "
siendo por lo tanto falsa.
Símbolo bien conocido Symbol.isConcatSpreadable
El método concat()
de Array acepta como argumentos otros Arrays cuyos elementos serán agregados al final del Array sobre el que se concatena. También los argumentos podrían ser elementos sueltos y una combinación de ambos:
let arr = [1, 2]; //concatenamos elementos de un array console.log(arr.concat([3, 4])); // [1, 2, 3, 4] //concatenamos elementos sueltos console.log(arr.concat(3, 4)); // [1, 2, 3, 4] //concatenamos elementos sueltos y en arrays console.log(arr.concat(3, [4, 5], 6)); // [1, 2, 3, 4, 5, 6]
En el último caso quizás nos interese que no extienda la concatenación de los elementos del Array [4, 5]
, sino que lo concatene como un Array. Para conseguirlo modificamos el comportamiento poniendo [Symbol.isConcatSpreadable]
con valor falso:
let arr = [1, 2]; let arr2 = [3, 4]; arr2[Symbol.isConcatSpreadable] = false; console.log(arr.concat(arr2, 5)); // [1, 2, [3, 4], 5]
De hecho el símbolo Symbol.isConcatSpreadable
puede aplicarse a cualquier objeto sin ser un Array. Sólo basta que tenga claves numéricas y una propiedad length
. Es entonces algo como un Array
pues tiene una estructura equivalente. En el siguiente ejemplo si concatenamos un objeto a un array lo agregará como último elemento. Pero si hacemos que extienda la concatenación extraerá los elementos con clave numérica del objeto y los concatenará al Array:
let obj = {0: "b", 1: "c", length: 2}; console.log(obj); // Object {0: "b", 1: "c", length: 2} //Concatenamos el objeto console.log(["a"].concat(obj)); // ["a", {0: "b", 1: "c", length: 2}] //O hacemos que extienda la concatenación a los elementos del objeto obj[Symbol.isConcatSpreadable] = true; console.log(["a"].concat(obj)); // ["a", "b", "c"]
Las claves deben ser consecutivas empezando en cero. Las no numéricas son ignoradas, como la clave "key"
del siguiente ejemplo:
let obj = {0: "b", key: "x", 1: "c", length: 2}; obj[Symbol.isConcatSpreadable] = true; console.log(["a"].concat(obj)); // ["a", "b", "c"]
Se extraerá hasta la longitud indicada en length
. En este ejemplo hay tres claves y se indican sólo dos, que son las que se extraen:
let obj = {0: "b", 1: "c", 2: "d", length: 2}; obj[Symbol.isConcatSpreadable] = true; console.log(["a"].concat(obj)); // ["a", "b", "c"]
Si la longitud indicada es mayor que el número de claves disponibles no cursará error y recupera las que hayan. En este ejemplo se indica una longitud de tres pero sólo hay dos claves que se recuperan:
let obj = {0: "b", 1: "c", length: 3}; obj[Symbol.isConcatSpreadable] = true; console.log(["a"].concat(obj)); // ["a", "b", "c"]
Símbolo bien conocido Symbol.unscopables
La sentencia with
nos permite acortar las referencias a un objeto. En el siguiente código tenemos un objeto con dos propiedades que son Arrays. Podemos concatenar el segundo al primero haciendo referencia a obj.prop.numeros
y obj.prop.letras
. Usando un bloque with
podemos obviar la referencia obj.prop
puesto que numeros
y letras
se referencian en el indicador del with
.
let obj = { prop: {numeros: [1, 2], letras: ["a", "b"]} } console.log(obj.prop.numeros.concat(obj.prop.letras)); // [1, 2, "a", "b"] with (obj.prop){ console.log(numeros.concat(letras)); // [1, 2, "a", "b"] }
El bloque with
no está permitido en modo estricto y es algo que tendrá que desaparecer porque causa muchos problemas. Véase que si la finalidad es escribir menos, para acortar una cadena de referencias basta con crear otra intermedia:
let obj = { prop: {numeros: [1, 2], letras: ["a", "b"]} } //Con un acortador conseguimos ahorrarnos código let x = obj.prop; console.log(x.numeros.concat(x.letras)); // [1, 2, "a", "b"]
Con el símbolo Symbol.unscopables
podemos forzar que las propiedades no sea vistas por el alcance de un bloque with
. En el siguiente ejemplo tenemos un constructor con una propiedad conScope
que se comporta por defecto con alcance en el with
y otra sinScope
a la que se lo negaremos. Para ello asignamos un objeto con las propiedades a las que se les niega el alcance (true) o se les permite (false). Repetimos que por defecto no se les niega el alcance con objeto de permitir la compatibilidad con código existente, por lo que podríamos omitir conScope: false
.
function MiConstructor(){ this.conScope = 123; this.sinScope = 456; } MiConstructor.prototype[Symbol.unscopables] = { conScope: false, sinScope: true }; let obj = new MiConstructor(); with (obj){ console.log(conScope); // 123 console.log(sinScope); // ReferenceError: sinScope is not defined }
Símbolo bien conocido Symbol.species
El símbolo bien conocido Symbol.species
nos permite definir el constructor que se usará al crear objetos derivados. Es más o menos lo que expone la especificación EcmaScript 6: Well-Known Symbols. Aún no he visto a fondo el tema de las nuevas clases de ES6, pero creo que debo entender objeto derivado como un objeto de una clase que hereda de otra clase. El problema con Symbol.species
es que no tiene gran soporte en los navegadores actuales que puedo consultar Chrome 50 ni Firefox 46. La previsión es que Chrome 51 y Firefox 49 lo soporten (ver soportes en Kangak ES6 table).
Intentaré de todas formas exponer un ejemplo. En el siguiente código tenemos la clase MiArray
que hereda de Array al declararlo con extends
. Como ilustrativo le incorporamos un método que suma todos los elementos del Array. Es por tanto una forma práctica de incorporar nuevos métodos al built-in Array.
class MiArray extends Array { sumar(){ return this.reduce((x, y) => x+y); } } let arr = new MiArray(1, 2); console.log(arr); // [1, 2] //Hereda métodos de Array console.log(arr.join("X")); // "1X2" //Hereda métodos de MiArray console.log(arr.sumar()); // 3 //Es por tanto instancia de ambos console.log(arr instanceof MiArray); // true console.log(arr instanceof Array); // true
En la ejecución anterior tanto en Chrome 50 como en Firefox 46 vemos que la variable instanciada arr
es a la vez una instancia de MiArray
y de Array
. Hasta aquí todo va como se espera.
Cuando a un objeto MiArray
derivado de Array le aplicamos los métodos de Array que devuelven a su vez un Array, el devuelto debería ser una instancia de MiArray
y no de Array. Esos métodos son concat()
, filter()
, map()
, slice()
y splice()
. Veamos esto para concat()
:
//Obtenemos un nuevo array concatenando a la instancia anterior let conc = arr.concat([3, 4]); console.log(conc); // [1, 2, 3, 4] //El concatenado debería ser instancia de MiArray... console.log(conc instanceof MiArray); // true (CH50+FF46: false) //...pero no de Array console.log(conc instanceof Array); // false (CH50+FF46: true)
Se observa que en Chrome 50 y Firefox 46 el array devuelto por concat()
no es instancia de MiArray
y si lo es de Array
. Esto no es lo que se espera, pues aunque el método concat()
es de Array, su devolución debería ser del mismo tipo que la instancia arr
sobre la que se aplica.
En Chrome 51 ahora obtenemos que el concatenado es a la vez instancia de Array y de la clase MiArray
. En Firefox 47 da el mismo resultado que en la versión 46. El resultado que debería esperarse es que el concatenado fuera instancia de MiArray
pero no de Array (true
y false
).
En cualquier caso si estos navegadores se hubiesen comportado como se espera y quisiéramos que el nuevo Array resultante de concat()
fuera instancia de Array y no de MiArray
tendríamos que usar el símbolo [Symbol.species]
tal como sigue:
class MiArray extends Array { //Sobrescribe a Array static get [Symbol.species]() { //Agregado esto para ver si el Navegador usa //esta modificación del símbolo console.log("OK"); return Array; } //Método sumar(){ return this.reduce((x,y) => x+y); } } let arr = new MiArray(1, 2); let conc = arr.concat([3, 4]); //Ahora el concatenado no debería ser instancia de MiArray... //CH51: "OK" y false //FF47: false console.log(conc instanceof MiArray); // false (CH50+FF46: false) //CH51: true //FF47: true console.log(conc instanceof Array); // true (CH50+FF46: true)
Casualmente el resultado esperado es ahora igual que el que nos da Chrome 50 y Firefox 46. De hecho el resultado sigue siendo el mismo. Pero esto no quiere decir que la sobrescritura de [Symbol.species] se haya llevado a cabo en esos navegadores. Esperaremos a las nuevas versiones para ver si este ejemplo sigue comportándose igual.
A efectos de saber si el navegador lleva a cabo la ejecución del símbolo modificado he agregado un "OK" para que salga por la consola cuando se ejecute. Chrome 51 lo ejecuta pero Firefox 47 no.
Símbolo bien conocido Symbol.iterator

Un objeto es iterable si tiene el método Symbol.iterator
. Este es un método iterador que sirve para obtener sus elementos en un bucle for-of, entre otros usos. Entre los objetos built-in de JavaScript que son iterables encontramos String, Array (ver Figura), Set y Map así como los encuadrados en TypedArray. No son iterables los WeakSet o WeakMap.
Es importante no olvidar que los objetos Object no son iterables. En la ejecución del siguiente código vemos que [Symbol.iterator]
para un String o un Array es una función, mientras que no existe para un objeto.
//Un String es iterable let str = "abc"; console.log(typeof str[Symbol.iterator]); // "function" //Un Array es iterable let arr = [1, 2]; console.log(typeof arr[Symbol.iterator]); // "function" //Un Object no es iterable let obj = {a: 1}; console.log(typeof obj[Symbol.iterator]); // "undefined"
Podemos hacer iterable un objeto si las claves de las propiedades son numéricas, le dotamos de una propiedad length
y del método [Symbol.iterator]
:
let obj = { 0: "a", 1: "b", length: 2, [Symbol.iterator]: function* () { let index = 0; while (index < this.length) yield this[index++]; } }; console.log(typeof obj[Symbol.iterator]); // "function"
El método iterador anterior se construye con un generador (o función generadora), de tal forma que en cada ejecución devolverá el valor yield, deteniéndose la ejecución en ese punto hasta la siguiente llamada al método. Y así consecutivamente hasta finalizar todos los elementos. Sobre el objeto iterable anterior con ese método iterador podemos aplicar bucles For-of, operador propagación de Array, Destructuring, el método Array.from()
y otras operaciones.
//Iterables y bucles for-of for (let x of obj){ console.log(x); // "a" // "b" } //Iterables y Operador propagación console.log([...obj]); // ["a", "b"] //Iterables y Destructuring let [x, y] = obj; console.log(x); // "a" console.log(y); // "b" //Iterables y Array.from() let z = Array.from(obj); console.log(z); // ["a", "b"]
La ejecución de una función generadora y, en general, de un método [Symbol.iterator]
devuelven un objeto iterador. Se trata de un objeto interno en el que no vamos a manipular su contenido, sino hacer uso de alguno de sus métodos. Como next()
que sirve para ir obteniendo los elementos del objeto iterable. Mientras hayan elementos devuelve un objeto como {value: "a", done: false}
. Cuando finalice la iteración devolverá {value: undefined, done: true}
. Aplicado al iterable de los ejemplos anteriores resultará lo siguiente:
let iterador = obj[Symbol.iterator](); //El iterador es un objeto interno que permite la iteración console.log(typeof iterador); // "object" console.log(iterador); // {[[GeneratorStatus]]: "suspended", // [[GeneratorReceiver]]: Object} //El método next() del iterador nos va extrayendo elementos //en cada ejecución console.log(iterador.next()); // {value: "a", done: false} console.log(iterador.next()); // {value: "b", done: false} console.log(iterador.next()); // {value: undefined, done: true}
Este apartado sobre iterables requeriría de una exposición más extensa, pero creo que con lo anterior ya nos hacemos una idea general del asunto. En el siguiente ejemplo interactivo recopilamos los usos anteriores y otros para diversos objetos iterables.
Ejemplo: Symbol.iterator
Nota sobre la representación de objetos built-in
Un objeto o Array lo representamos textualmente tal como podría declararse literalmente. Un Object como {a: 1}
. Un Array como [1, 2]
. Pero hasta ahora no existen literales para los nuevos tipos Set, Map y otros. Al extraer los valores en el ejemplo anterior representamos un conjunto envolviéndola entre llaves. Usamos también llaves para el mapa, con las parejas clave-valor como si fuera un Array de dos posiciones (que no lo es).
En la consola de Chrome un conjunto se presenta igual, con {valor1, valor2}
. Mientras que un mapa con {clave1 => valor1, clave2 => valor2}
. No confundir esa flecha con la de las funciones flecha).
Firefox presenta un conjunto con [valor1, valor2]
y un mapa con {clave1: valor1, clave2: valor2}
. Pero no debemos olvidar que sólo son representaciones de texto de estructuras de datos, porque los conjuntos o mapas no son ni Array ni Object.