10. Desestructuración
10. Desestructuración #
- 10.1. Descripción general
- 10.1.1. Desestructuración de objetos
- 10.1.2. Desestructuración de matrices
- 10.1.3. ¿Dónde se puede utilizar la desestructuración?
- 10.2. Antecedentes: Construcción de datos versus extracción de datos
- 10.3. Patrones de desestructuración
- 10.3.1. Elige lo que necesitas
- 10.4. ¿Cómo acceden los patrones a las entrañas de los valores?
- 10.4.1. Patrones de objetos coaccionan valores a objetos
- 10.4.2. Los patrones de matriz funcionan con iterables
- 10.5. Valores predeterminados
- 10.5.1.
undefined
activa valores predeterminados - 10.5.2. Los valores predeterminados se calculan bajo demanda
- 10.5.3. Los valores predeterminados pueden referirse a otras variables en el patrón
- 10.5.4. Valores predeterminados para patrones
- 10.5.5. Valores predeterminados más complejos
- 10.5.1.
- 10.6. Más funciones de desestructuración de objetos
- 10.6.1. Abreviaturas de valor de propiedad
- 10.6.2. Claves de propiedad calculadas
- 10.7. Más funciones de desestructuración de matrices
- 10.7.1. Elision
- 10.7.2. Operador de descanso(
...
)
- 10.8. Puede asignar a más de variables
- 10.9. Trampas de desestructuración
- 10.9.1. No empieces una declaración con un corsé rizado
- 10.10. Ejemplos de desestructuración
- 10.10.1. Desestructuración de matrices devueltas
- 10.10.2. Desestructuración de objetos devueltos
- 10.10.3. Valores iterables de desestructuración de matrices
- 10.10.4. Varios valores de retorno
- 10.11. El algoritmo de desestructuración
- 10.11.1. El algoritmo
- 10.11.2. Aplicación del algoritmo
10.1 Descripción general #
La desestructuración es una forma conveniente de extraer múltiples valores de datos almacenados en objetos y matrices (posiblemente anidados). Se puede usar en ubicaciones que reciben datos (como el lado izquierdo de una asignación). Cómo extraer los valores se especifica a través de patrones (siga leyendo para ver ejemplos).
10.1.1 Desestructuración de objetos #
Desestructuración de objetos:
La desestructuración ayuda a procesar los valores de retorno:
10.1.2 Desestructuración de matrices #
Desestructuración de matrices (funciona para todos los valores iterables):
La desestructuración ayuda a procesar los valores de retorno:
10.1.3 ¿Dónde se puede utilizar la desestructuración? #
La desestructuración se puede usar en las siguientes ubicaciones (estoy mostrando patrones de matriz para demostrar; los patrones de objetos funcionan igual de bien):
También puede desestructurarse en un bucle for-of
:
10.2 Antecedentes: Construir datos versus extraer datos #
Para comprender completamente qué es la desestructuración, examinemos primero su contexto más amplio.
JavaScript tiene operaciones para construir datos, una propiedad a la vez:
Se puede utilizar la misma sintaxis para extraer datos. Una vez más, una propiedad a la vez:
Además, hay sintaxis para construir varias propiedades al mismo tiempo, a través de un literal de objeto:
Antes de ES6, no había un mecanismo correspondiente para extraer datos. Eso es lo que es la desestructuración: le permite extraer múltiples propiedades de un objeto a través de un patrón de objeto. Por ejemplo, en el lado izquierdo de una asignación:
también puede desestructurados Matrices a través de los patrones de:
10.3 Patrones de desestructuración #
Las dos partes siguientes participan en la desestructuración:
- Fuente de desestructuración: los datos a desestructurar. Por ejemplo, el lado derecho de una asignación de desestructuración.
- Objetivo de desestructuración: el patrón utilizado para la desestructuración. Por ejemplo, el lado izquierdo de una asignación de desestructuración.
El objetivo de desestructuración es uno de los tres patrones:
- Objetivo asignado. Por ejemplo:
x
- Un destino de asignación suele ser una variable. Pero en la asignación de desestructuración, tienes más opciones, como explicaré más adelante.
- Patrón de objetos. Por ejemplo:
{ first: "pattern", last: "pattern" }
- Las partes de un patrón de objeto son propiedades, los valores de propiedad son de nuevo patrones (recursivamente).
- Patrón de matriz. Por ejemplo:
- Las partes de un patrón de matriz son elementos, los elementos son de nuevo patrones (recursivamente).
Eso significa que puedes anidar patrones, arbitrariamente profundamente:
10.3.1 Elija lo que necesita #
Si desestructura un objeto, mencione solo las propiedades que le interesan:
Si desestructura una matriz, puede optar por extraer solo un prefijo:
10.4 ¿Cómo acceden los patrones a las entrañas de los valores? #
En una tarea pattern = someValue
, ¿cómo accede pattern
a lo que hay dentro de someValue
?
10.4.1 Patrones de objeto coaccionan valores a objetos #
El patrón de objeto coacciona fuentes de desestructuración a objetos antes de acceder a propiedades. Eso significa que funciona con valores primitivos:
10.4.1.1 Falla en la destrucción de objetos un valor #
La coerción al objeto no se realiza a través de Object()
, sino a través de la operación interna ToObject()
. Las dos operaciones manejan undefined
y null
de manera diferente.
Object()
convierte valores primitivos en objetos de envoltura y deja los objetos intactos:
It also converts undefined
and null
to empty objects:
In contrast, ToObject()
throws a TypeError
if it encounters undefined
or null
. Therefore, the following destructurings fail, even before destructuring accesses any properties:
Como consecuencia, puede usar el patrón de objeto vacío {}
para comprobar si un valor es coercitivo para un objeto. Como hemos visto, solo undefined
y null
no lo son:
Los paréntesis alrededor de las expresiones son necesarios porque las instrucciones no deben comenzar con llaves en JavaScript (los detalles se explican más adelante).
10.4.2 Los patrones de matriz funcionan con iterables #
La desestructuración de matrices utiliza un iterador para acceder a los elementos de una fuente. Por lo tanto, puede desestructurar cualquier valor iterable. Veamos ejemplos de valores iterables.
Las cadenas son iterables:
No olvide que el iterador sobre cadenas devuelve puntos de código («caracteres Unicode», 21 bits), no unidades de código («caracteres JavaScript», 16 bits). (Para obtener más información sobre Unicode, consulte el capítulo » Capítulo 24. Unicode y JavaScript » en «Lenguaje JavaScript».) Por ejemplo:
No puede acceder a los elementos de un Conjunto a través de índices, pero puede hacerlo a través de un iterador. Por lo tanto, la desestructuración de matrices funciona para Conjuntos:
El iterador Set
siempre devuelve los elementos en el orden en que se insertaron, por lo que el resultado de la desestructuración previa es siempre el mismo.
10.4.2.1 No se puede desestructurar un valor #
Un valor es iterable si tiene un método cuya clave es Symbol.iterator
que devuelve un objeto. La desestructuración de matrices arroja un TypeError
si el valor a desestructurar no es iterable:
El TypeError
se lanza incluso antes de acceder a los elementos del iterable, lo que significa que puede usar el patrón de matriz vacía para verificar si un valor es iterable:
10.5 Valores predeterminados #
Los valores predeterminados son una característica opcional de los patrones. Proporcionan una alternativa si no se encuentra nada en la fuente. Si una parte (una propiedad de objeto o un elemento de matriz) no tiene coincidencia en el origen, se compara con:
- su valor predeterminado (si se especifica; es opcional)
-
undefined
(de lo contrario,)
veamos un ejemplo. En la siguiente desestructuración, el elemento en el índice 0 no tiene coincidencia en el lado derecho. Por lo tanto, la desestructuración continúa haciendo coincidir x
con 3, lo que lleva a que x
se establezca en 3.
También puede usar valores predeterminados en patrones de objetos:
10.5.1 undefined
disparadores valores predeterminados #
Los valores predeterminados también se usan si una parte tiene una coincidencia y esa coincidencia es undefined
:
La justificación de este comportamiento se explica en el siguiente capítulo, en la sección sobre valores predeterminados de los parámetros.
10.5.2 Los valores predeterminados se calculan a pedido #
Los valores predeterminados en sí solo se calculan cuando se necesitan. En otras palabras, esta desestructuración:
es equivalente a:
Se puede observar que si se utiliza console.log()
:
In the second destructuring, the default value is not triggered and log()
is not called.
10.5.3 Default values can refer to other variables in the pattern #
A default value can refer to any variable, including other variables in the same pattern:
sin Embargo, el orden importa: las variables x
y y
se declaran de izquierda a derecha y producen un ReferenceError
si se accede a ellas antes de sus declaraciones:
10.5.4 Valores predeterminados para patrones #
Hasta ahora solo hemos visto valores predeterminados para variables, pero también puede asociarlos con patrones:
¿Qué significa esto? Recordar la regla para los valores predeterminados: Si una parte no tiene coincidencia en el origen, la desestructuración continúa con el valor predeterminado.
El elemento en el índice 0 no tiene coincidencia, por lo que la desestructuración continúa con:
Puede ver más fácilmente por qué las cosas funcionan de esta manera si reemplaza el patrón { prop: x }
con la variable pattern
:
10.5.5 Valores predeterminados más complejos #
Exploremos más a fondo los valores predeterminados de los patrones. En el siguiente ejemplo, asignamos un valor a x
a través del valor predeterminado { prop: 123 }
:
Debido a que el elemento de matriz en el índice 0 no tiene coincidencia en el lado derecho, la desestructuración continúa de la siguiente manera y x
se establece en 123.
Sin embargo, a x
no se le asigna un valor de esta manera si el lado derecho tiene un elemento en el índice 0, porque entonces el valor predeterminado no se activa.
En este caso, desestructuración continúa con:
Por lo tanto, si desea que x
sea 123 si falta el objeto o la propiedad, debe especificar un valor predeterminado para x
:
Aquí, la desestructuración continúa de la siguiente manera, independientemente de si el lado derecho es o
.
10.6 Más funciones de desestructuración de objetos #
10.6.1 Abreviaturas de valor de propiedad #
Las abreviaturas de valor de propiedad son una característica de los literales de objetos: Si el valor de la propiedad es una variable que tiene el mismo nombre que la clave de propiedad, puede omitir la clave. Esto también funciona para la desestructuración:
También puede combinar abreviaturas de valor de propiedad con valores predeterminados:
10.6.2 Claves de propiedad computadas #
Las claves de propiedad computadas son otra característica literal de objeto que también funciona para la desestructuración. Puede especificar la clave de una propiedad a través de una expresión, si la pone entre corchetes:
Las claves de propiedad calculadas le permiten desestructurar propiedades cuyas claves son símbolos:
10.7 Más funciones de desestructuración de matrices #
10.7.1 Elision #
Elision le permite usar la sintaxis de «agujeros» de matrices para omitir elementos durante la desestructuración:
10.7.2 Operador de descanso(...
) #
El operador rest le permite extraer los elementos restantes de un iterable en una matriz. Si este operador se utiliza dentro de un patrón de matriz, debe ser el último:
Si el operador no puede encontrar ningún elemento, hace coincidir su operando con el Array vacío. Es decir, nunca produce undefined
o null
. Por ejemplo:
El operando del resto operador no tiene que ser una variable, se pueden utilizar los patrones, demasiado:
El resto del operador activa el siguiente desestructuración:
10.8 Puede asignar a más que solo variables #
Si asigna mediante desestructuración, cada destino de asignación puede ser todo lo que se permite en el lado izquierdo de una asignación normal.
Por ejemplo, una referencia a una propiedad (obj.prop
):
O una referencia a un elemento de matriz(arr
):
También puede asignar propiedades de objeto y elementos de matriz a través del operador rest (...
):
Si declara variables o define parámetros a través de la desestructuración, debe usar identificadores simples, no puede hacer referencia a propiedades de objetos y elementos de matriz.
10.9 Trampas de la desestructuración #
Hay dos cosas a tener en cuenta al usar la desestructuración:
- No puedes empezar una declaración con un corsé rizado.
- Durante la desestructuración, puede declarar variables o asignarlas, pero no ambas.
Las dos secciones siguientes contienen los detalles.
10.9.1 No comience una instrucción con un corsé rizado #
Dado que los bloques de código comienzan con un corsé rizado, las instrucciones no deben comenzar con uno. Esto es desafortunado cuando se utiliza objeto de desestructuración en una asignación:
La solución es poner la completa expresión en paréntesis:
La siguiente sintaxis no funciona:
Con let
, var
y const
, las llaves nunca causa problemas:
10.10 Ejemplos de desestructuración #
Comencemos con algunos ejemplos más pequeños.
El bucle for-of
admite la desestructuración:
Puede usar desestructuración para intercambiar valores. Eso es algo que los motores podrían optimizar, para que no se creara ninguna matriz.
Puede usar desestructuración para dividir una matriz:
10.10.1 Desestructuración de matrices devueltas #
Algunas operaciones de JavaScript integradas devuelven matrices. La desestructuración ayuda a procesarlos:
Si solo está interesado en los grupos (y no en la coincidencia completa, all
), puede usar elision para omitir el elemento array en index 0:
exec()
devuelve null
si la expresión regular no coincide. Desafortunadamente, no puede manejar null
a través de valores predeterminados, por lo que debe usar el operador Or (||
) en este caso:
Array.prototype.split()
devuelve una matriz. Por lo tanto, la desestructuración es útil si está interesado en los elementos, no en el Array:
10.10.2 Desestructuración de objetos devueltos #
La desestructuración también es útil para extraer datos de objetos que son devueltos por funciones o métodos. Por ejemplo, el método iterador next()
devuelve un objeto con dos propiedades, done
y value
. El siguiente código registra todos los elementos del Array arr
a través del iterador iter
. La desestructuración se utiliza en la línea A.
10.10.3 Valores iterables de desestructuración de matrices #
La desestructuración de matrices funciona con cualquier valor iterable. Eso es ocasionalmente útil:
10.10.4 Múltiples valores de retorno #
Para ver la utilidad de múltiples valores de retorno, implementemos una función findElement(a, p)
que busque el primer elemento del Array a
para el que la función p
devuelve true
. La pregunta es: ¿qué debe devolver findElement()
? A veces uno está interesado en el elemento en sí, a veces en su índice, a veces en ambos. La siguiente implementación devuelve.
La función itera sobre todos los elementos de array
, a través del método Array entries()
, que devuelve pares iterables sobre (línea A). Se accede a las partes de los pares mediante desestructuración.
Vamos a usar findElement()
:
Varias características de ECMAScript 6 nos permitieron escribir código más conciso: La devolución de llamada es una función de flecha; el valor de retorno se desestructura a través de un patrón de objeto con abreviaturas de valor de propiedad.
Debido a que index
y element
también se refieren a las claves de propiedad, el orden en el que las mencionamos no importa. Podemos intercambiarlos y nada cambia:
Hemos manejado con éxito el caso de necesitar tanto index como element. ¿Y si solo estamos interesados en uno de ellos? Resulta que, gracias a ECMAScript 6, nuestra implementación también puede encargarse de eso. Y la sobrecarga sintáctica en comparación con las funciones con valores de retorno únicos es mínima.
Cada momento, sólo extraer el valor de la propiedad que necesitamos.
10.11 El algoritmo de desestructuración #
Esta sección analiza la desestructuración desde un ángulo diferente: como un algoritmo de coincidencia de patrones recursivos.
Al final, usaré el algoritmo para explicar la diferencia entre las siguientes dos declaraciones de funciones.
10.11.1 El algoritmo #
Una asignación de desestructuración se ve así:
queremos usar pattern
para extraer los datos de value
. Ahora describiré un algoritmo para hacerlo, que se conoce en programación funcional como coincidencia de patrones (abreviado: coincidencia). El algoritmo especifica el operador ←
(«coincidencia contra») para la asignación de desestructuración que coincide con un pattern
contra un value
y asigna a las variables mientras lo hace:
El algoritmo se especifica mediante reglas recursivas que separan ambos operandos del operador ←
. La notación declarativa puede llevar un tiempo acostumbrarse, pero hace que la especificación del algoritmo sea más concisa. Cada regla tiene dos partes:
- El encabezado (primera línea) describe la condición que desencadena la regla.
- El cuerpo (líneas restantes) describe lo que sucede si se activa la regla.
veamos un ejemplo:
- (2c)
{key: "pattern", "properties"} ← obj
- (2e)
{} ← obj
(no hay propiedades a la izquierda)
En la regla (2c), la cabeza, significa que esta regla se ejecuta si hay un objeto patrón con al menos una propiedad, y cero o más propiedades restantes. Ese patrón se compara con un valor obj
. El efecto de esta regla es que la ejecución continúa con el patrón de valor de propiedad coincidiendo con obj.key
y las propiedades restantes coincidiendo con obj
.
En regla (2e), el encabezado significa que esta regla se ejecuta si el patrón de objeto vacío {}
se compara con un valor obj
. Entonces no hay nada que hacer.
Cada vez que se invoca el algoritmo, las reglas se comprueban de arriba a abajo y solo se ejecuta la primera regla aplicable.
Solo muestro el algoritmo para la asignación de desestructuración. Las declaraciones de variables de desestructuración y las definiciones de parámetros de desestructuración funcionan de manera similar.
No cubro funciones avanzadas (claves de propiedad calculadas; abreviaturas de valor de propiedad; propiedades de objeto y elementos de matriz como destinos de asignación). Solo lo básico.
10.11.1.1 Patrones #
Un patrón es:
- Una variable:
x
- Un patrón de objeto:
{"properties"}
- Un patrón de matriz:
Cada una de las secciones siguientes describe uno de estos tres casos.
Las tres secciones siguientes especifican cómo manejar estos tres casos. Cada sección contiene una o más reglas numeradas.
10.11.1.2 Variable #
- (1)
x ← value
(la inclusión deundefined
ynull
)
10.11.1.3 Objeto patrón #
- (2a)
{"properties"} ← undefined
- (2b)
{"properties"} ← null
- (2c)
{key: "pattern", "properties"} ← obj
- (2d)
{key: "pattern" = default_value, "properties"} ← obj
- (2e)
{} ← obj
(no hay propiedades a la izquierda)
10.11.1.4 Matriz de patrón #
Array patrón y iterable. El algoritmo para la desestructuración de matrices comienza con un patrón de matrices y un:
- (3a)
← non_iterable
assert(!isIterable(non_iterable))
- (3b)
← iterable
assert(isIterable(iterable))
Función auxiliar:
Elementos de matriz e iterador. El algoritmo continúa con los elementos del patrón (lado izquierdo de la flecha) y el iterador que se obtuvo del iterable (lado derecho de la flecha).
- (3c)
"pattern", "elements" ← iterator
- (3d)
"pattern" = default_value, "elements" ← iterator
- (3e)
, "elements" ← iterator
(agujero, elisión)
- (3f)
..."pattern" ← iterator
(siempre la última parte!)
- (3g)
← iterator
(no quedan elementos)
Función auxiliar:
10.11.2 Aplicando el algoritmo #
En ECMAScript 6, puede simular parámetros con nombre si la persona que llama usa un literal de objeto y la persona que llama usa desestructuración. Esta simulación se explica en detalle en el capítulo sobre manejo de parámetros. El siguiente código muestra un ejemplo: la función move1()
tiene dos parámetros con nombre, x
y y
:
Hay tres valores predeterminados en la línea A:
- Los dos primeros valores predeterminados le permiten omitir
x
yy
. - El tercer valor predeterminado le permite llamar a
move1()
sin parámetros (como en la última línea).
Pero, ¿por qué definirías los parámetros como en el fragmento de código anterior? ¿Por qué no de la siguiente manera, que también es el código ES6 completamente legal?
Para ver por qué move1()
es correcto, usemos ambas funciones para dos ejemplos. Antes de hacer eso, veamos cómo se puede explicar el paso de parámetros a través de la coincidencia.
10.11.2.1 Fondo: pasar parámetros a través de coincidencias #
Para llamadas de función, los parámetros formales (definiciones de función internas) se comparan con los parámetros reales (llamadas de función internas). Como ejemplo, tome la siguiente definición de función y la siguiente llamada a función.
Los parámetros a
y b
se configuran de manera similar a la siguiente desestructuración.
10.11.2.2 Uso move2()
#
Examinemos cómo funciona la desestructuración para move2()
.
Ejemplo 1. move2()
conduce a esta desestructuración:
El elemento de matriz única en el lado izquierdo no tiene una coincidencia en el lado derecho, por lo que {x,y}
se compara con el valor predeterminado y no con los datos del lado derecho (reglas 3b, 3d):
El lado izquierdo contiene el valor de la propiedad shorthands, es una abreviatura para:
Esta desestructuración conduce a las siguientes dos tareas (reglas 2c, 1):
Ejemplo 2. Examinemos la llamada a la función move2({z:3})
que conduce a la siguiente desestructuración:
Hay un elemento de matriz en el índice 0 en el lado derecho. Por lo tanto, el valor predeterminado se ignora y el siguiente paso es (regla 3d):
Eso lleva a que x
y y
se establezcan en undefined
, que no es lo que queremos.
10.11.2.3 Usar move1()
#
Probemos move1()
.
Ejemplo 1: move1()
no Tenemos un elemento de la Matriz en el índice 0 en el lado derecho y utilice el valor predeterminado (regla 3d):
El lado izquierdo contiene el valor de la propiedad shorthands, lo que significa que esta desestructuración es equivalente a:
Ni la propiedad x
ni la propiedad y
tienen una coincidencia en el lado derecho. Por lo tanto, se utilizan los valores predeterminados y se realizan las siguientes desestructuraciones (regla 2d):
Que conduce a las siguientes asignaciones (regla 1):
Ejemplo 2: move1({z:3})
El primer elemento del patrón de matriz tiene una coincidencia en el lado derecho y esa coincidencia se usa para continuar la desestructuración (regla 3d):
Como en el ejemplo 1, no hay propiedades x
y y
en el lado derecho y se utilizan los valores predeterminados:
10.11.2.4 Conclusión #
Los ejemplos demuestran que los valores predeterminados son una característica de partes de patrones (propiedades de objetos o elementos de matriz). Si una pieza no tiene coincidencia o se compara con undefined
, se utiliza el valor predeterminado. Es decir, el patrón se compara con el valor predeterminado, en su lugar.