- Aprender
- Informática
- Programación
- Programación en JavaScript
- Funciones en JavaScript
Funciones en JavaScript: ámbito y cierres
return. Predice dónde se puede usar una variable con los ámbitos de bloque, de función y global, usa cierres para dar a las funciones su propia memoria y pasa funciones a otras funciones como funciones de devolución de llamada para escribir código flexible de orden superior.PrincipianteUnidad 3 de 9 del curso Curso de Programación en JavaScript
Lo que aprenderás
- Organiza una parte reutilizable de código como función, llámala con argumentos y elige entre una declaración de función, una expresión de función y una función flecha.
- Escribe una declaración de función y llama a la funciónDefine una función con nombre mediante la palabra clave
function, llámala y explica por qué las funciones hacen que el código sea más corto, claro y fácil de modificar. - Almacena una función en una variable con una expresión de funciónTrata las funciones como valores: crea una expresión de función, asígnala a una variable y explica en qué se diferencia de una declaración en cuanto a elevación y nombre.
- Escribe funciones flechaUsa la sintaxis abreviada de flecha, identifica cuándo una función flecha devuelve un valor automáticamente y explica en qué se diferencia de las funciones escritas con
function.
- Escribe una declaración de función y llama a la función
- Diseña qué entra en una función y qué sale de ella: relaciona argumentos con parámetros, establece valores predeterminados, acepta cualquier número de argumentos, devuelve resultados y sigue cómo se ejecutan las llamadas anidadas.
- Relaciona los argumentos con los parámetrosExplica cómo llegan los valores de una llamada a los parámetros de la función, qué ocurre cuando hay pocos o demasiados argumentos y qué datos de quien llama puede modificar una función.
- Establece valores predeterminados y acepta cualquier número de argumentosAsigna valores predeterminados a los parámetros, recopila cualquier número de argumentos con un parámetro de resto y expande un array en argumentos separados.
- Devuelve resultados desde una funciónDevuelve un valor con
return, usa el resultado en código posterior y distingue entre devolver un valor, mostrarlo o cambiar algo fuera de la función. - Sigue las llamadas a funciones en la pila de llamadasSigue el orden en que se ejecutan y terminan las llamadas anidadas y escribe una función recursiva sencilla con un caso base.
- Relaciona los argumentos con los parámetros
- Predice dónde es visible una variable aplicando las reglas de los ámbitos de bloque, de función y global y de las funciones escritas dentro de otras funciones.
- Distingue los ámbitos de bloque, de función y globalExplica el ámbito de una variable declarada con
let,constovary usa variables locales para mantener las funciones independientes entre sí. - Predice a qué variable se refiere un nombreSigue la cadena de ámbitos para encontrar la variable detrás de un nombre, reconoce el sombreado de variables y explica la elevación de declaraciones y la zona muerta temporal.
- Distingue los ámbitos de bloque, de función y global
- Explica cómo una función conserva el acceso a las variables que la rodean después de que termine la función externa y usa esa propiedad para crear fábricas de funciones y mantener estado privado.
- Explica qué recuerda un cierreDescribe cómo una función conserva el acceso a las variables del ámbito en que se creó, incluso después de que la función externa haya terminado.
- Usa cierres para crear fábricas de funciones y mantener estado privadoConstruye fábricas de funciones, mantén datos privados detrás de funciones y evita el error clásico de cierre en un bucle.
- Explica qué recuerda un cierre
- Usa funciones como valores: pásalas a otras funciones como funciones de devolución de llamada, usa y escribe funciones de orden superior y construye funciones nuevas a partir de otras existentes.
- Pasa una función de devolución de llamada a una funciónPasa una función a otra como argumento, escribe funciones que llamen a una función de devolución de llamada y pasa la propia función en lugar de su resultado.
- Usa y escribe funciones de orden superiorReconoce funciones de orden superior, usa los métodos de array que reciben funciones de devolución de llamada y escribe tu propia función que reciba una función como entrada.
- Construye nuevas funciones a partir de otras existentesEscribe funciones de orden superior que envuelvan, combinen o completen de antemano otras funciones, usando cierres para recordar la función que recibieron.
- Pasa una función de devolución de llamada a una función
Guía de estudio de Funciones en JavaScript
1Define y llama a funciones de tres formas
Las funciones convierten instrucciones repetidas en tareas con nombre. Leerás y escribirás tres formas habituales y después decidirás si una línea crea una función, la almacena o la llama. Los pequeños cambios de sintaxis importan: una declaración está disponible antes, mientras que una expresión almacenada debe inicializarse primero, y el cuerpo de una función flecha puede cambiar si se devuelve un resultado. Estas distinciones te preparan para pasar funciones como valores ordinarios.
1.1 Escribe una declaración de función y llama a la función
Una función da nombre a una tarea y almacena las instrucciones para realizarla. Definirla no ejecuta esas instrucciones. Una llamada las ejecuta y otra llamada las ejecuta de nuevo con entradas nuevas.
Escribe la definición
Una declaración tiene la palabra clave function, un nombre, paréntesis para los parámetros y llaves para el cuerpo:
function greet(name) {
console.log("Hello, " + name + "!");
}
Aquí name es un parámetro: un nombre local para la entrada que proporciona cada llamada. El cuerpo une esa entrada al texto que la rodea y muestra el resultado. La llave de cierre marca el final de la definición; la ejecución continúa con la siguiente sentencia fuera de ella.
Una función sin entradas también necesita paréntesis. Por ejemplo, function sayHi() { console.log("Hi"); } define una tarea con una lista de parámetros vacía. Las llaves pertenecen a la definición; los paréntesis después de un nombre aparecen tanto en las definiciones como en las llamadas, con funciones distintas.
Ejecuta la tarea
greet("Amira");
greet("Li");
La primera llamada asigna a name el valor "Amira" y muestra «Hello, Amira!». La segunda inicia una nueva llamada con name igual a "Li" y muestra «Hello, Li!». La definición se comparte; la entrada pertenece a cada llamada individual.
greet por sí solo se refiere al valor de la función. No ejecuta el cuerpo. Esta distinción importa cuando una línea parece no hacer nada: sayHi; lee un valor de función y lo descarta, mientras que sayHi(); muestra «Hi».
Sustituye instrucciones copiadas
Supón que varios informes necesitan un encabezado rodeado de líneas:
function printHeading(title) {
console.log("-----");
console.log(title);
console.log("-----");
}
printHeading("Report");
printHeading("Totals");
La primera llamada muestra una línea, «Report» y otra línea. La segunda muestra el mismo marco alrededor de «Totals». Para cambiar el estilo de las líneas, edita las dos sentencias que las muestran en esta única definición. Sin la función, habría que corregir cada encabezado copiado por separado.
Elige un nombre que indique qué hace una llamada. printHeading describe una acción; isValidEmail nombra una comprobación de sí o no. Usa camelCase, con la primera palabra en minúscula y las siguientes con mayúscula inicial. Si un nombre propuesto une dos tareas distintas con «y», considera dar a cada tarea su propia función.
Lee código que llama anticipadamente a una declaración
Las declaraciones se preparan antes de que se ejecute el código que las rodea. Este comportamiento se llama elevación de declaraciones (hoisting):
sayHi();
function sayHi() {
console.log("Hi");
}
La llamada funciona dentro del ámbito de la declaración aunque la definición escrita aparezca después. La elevación no hace que el cuerpo se ejecute antes; hace que la función esté disponible para llamarla.
Comprueba la ortografía y el uso de mayúsculas cuando falle una llamada. Greet() y greet() son nombres diferentes. Llamar a greeet() sin declararla genera un ReferenceError; omitir los paréntesis de llamada puede fallar silenciosamente, porque leer una función existente es código válido.
Puntos clave
- Definir una función almacena instrucciones; llamarla las ejecuta.
- Llama con paréntesis, incluso cuando no hay argumentos.
- Una declaración está disponible antes de su posición escrita dentro del mismo ámbito.
- Da nombre a una tarea clara para que el código repetido tenga un solo lugar donde modificarse.
1.2 Almacena una función en una variable con una expresión de función
Las funciones son valores. Puedes guardar una en una variable, copiar su referencia, pasarla a otra función o devolverla. Una expresión de función crea ese valor donde se espera una expresión.
Almacena una función
const square = function (n) {
return n * n;
};
const answer = square(4);
La expresión después de = crea una función. La asignación la almacena en square; la llamada posterior devuelve 16. Esta sentencia termina con punto y coma, siguiendo la convención de formato usada aquí.
La posición de function distingue las formas. Una definición escrita como function square(n) { ... } por sí sola es una declaración. Una función escrita después de = o directamente como argumento de una llamada es una expresión. Ambas formas de función ordinaria pueden aceptar parámetros y devolver valores.
Usa const para una variable que contiene una función cuando su valor deba mantenerse igual. Se permite llamar a square(5); asignar square = 5; genera un TypeError. La constante protege la variable frente a su sustitución. No impide llamar repetidamente a la función.
Guarda una función o guarda su resultado
const sq = square;
const result = square(3);
sq se refiere a la misma función que square. Llamar a sq(3) devuelve 9. result contiene el número 9 que devolvió una llamada. No se puede llamar a ese número como función. typeof square y typeof sq son "function"; typeof result es "number".
Leer una asignación de derecha a izquierda ayuda: decide primero si su lado derecho crea una función, se refiere a una función existente o llama a una. La variable de la izquierda recibe ese valor. Copiar la referencia no crea una segunda implementación independiente.
Espera a que se ejecute la asignación
Una declaración y una expresión almacenada se pueden llamar en momentos diferentes:
| Forma | Antes de la definición o asignación escrita |
|---|---|
function square(n) { return n * n; } | Se puede llamar dentro de su ámbito |
const square = function (n) { return n * n; }; | Leer square genera un ReferenceError |
var square = function (n) { return n * n; }; | square es undefined; llamarla genera un TypeError |
Con const o let, el nombre no se puede usar antes de la inicialización. Con var, el nombre existe desde antes, pero el valor de función no se asigna hasta que la ejecución llega a la expresión. Elevar la variable no eleva la asignación.
Da a una expresión un nombre interno
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
El nombre externo es factorial. El nombre fact pertenece al interior de la función y permite que se llame a sí misma; el código externo no puede usar directamente ese nombre interno. El nombre de la expresión también puede facilitar la lectura de las trazas de la pila. Por ejemplo, factorial(3) calcula 3 por 2 por 1 y devuelve 6.
Las declaraciones funcionan bien para las principales tareas con nombre de un script. Las expresiones encajan donde el código necesita un valor de función, como en una asignación a una variable o en un argumento. Elegir una expresión cambia cómo se crea y almacena el valor, no cómo lo llaman los paréntesis.
Puntos clave
- Una expresión de función crea un valor de función dentro de otra expresión.
- Asignar
squarecopia una referencia a la función; asignarsquare(3)almacena su resultado. - Una expresión de función almacenada en
constsolo se puede llamar después de su inicialización. - Una variable de función declarada con
varcontieneundefinedantes de que se ejecute su asignación. - Una expresión con nombre puede usar su nombre interno para llamarse a sí misma.
1.3 Escribe funciones flecha
Las funciones flecha son expresiones de función con una escritura más corta. Su comportamiento de retorno depende de lo que sigue a la flecha, así que examina el cuerpo antes de decidir qué produce una llamada.
Elige la forma de los parámetros
const sayHi = () => "hi";
const double = n => n * 2;
const add = (a, b) => a + b;
Si no hay parámetros, se necesitan paréntesis vacíos. Un solo parámetro simple puede omitirlos. Dos o más los requieren. Un parámetro predeterminado o de resto también necesita paréntesis: (n = 1) => n * 2 y (...nums) => nums.length son válidos.
La flecha separa las entradas del cuerpo. const double = n => n * 2; crea la función y la almacena; double(6) la llama después y devuelve 12. Como cualquier const que contiene una expresión de función, double no se puede leer antes de que se ejecute su línea de inicialización.
Devuelve una expresión o escribe un bloque
| Cuerpo | Ejemplo | Resultado de la llamada con 2 y 3 |
|---|---|---|
| Expresión concisa | (a, b) => a + b | 5 |
Bloque con return | (a, b) => { return a + b; } | 5 |
Bloque sin return | (a, b) => { a + b; } | undefined |
Un cuerpo conciso devuelve su expresión automáticamente. Las llaves comienzan un bloque de sentencias. En un bloque, evaluar una expresión no la devuelve a quien llamó; escribe return de forma explícita.
Usa un bloque cuando la tarea necesite trabajo intermedio:
const describe = (n) => {
const kind = n % 2 === 0 ? "even" : "odd";
return n + " is " + kind;
};
Para describe(7), la condición es falsa, por lo que kind toma el valor "odd". El retorno une entonces el número y las palabras en «7 is odd». Eliminar ese retorno mantendría el cálculo local, pero daría undefined a quien llamó.
Devuelve un literal de objeto
Un literal de objeto también usa llaves. Enciérralo entre paréntesis para que el analizador lo interprete como una expresión:
const makePoint = (x, y) => ({ x: x, y: y });
makePoint(2, 5) devuelve un objeto con x igual a 2 e y igual a 5. Sin los paréntesis, la primera llave comienza un bloque. La forma con una sola propiedad () => { value: 1 } devuelve undefined; la forma similar con dos propiedades separadas por una coma produce un error de sintaxis. Los paréntesis cambian cómo se interpreta el cuerpo.
Conoce los límites de la forma abreviada
Las funciones flecha no tienen sus propios this ni arguments, y no se puede llamar a ellas con new para construir objetos. Para un cálculo sencillo que no usa ni this ni arguments, una función flecha y una expresión de función ordinaria se comportan igual. Sustituir todas las funciones ordinarias por funciones flecha puede cambiar el código que depende de esas características.
Las funciones flecha cortas son especialmente legibles como funciones de devolución de llamada. [1, 2, 3].map(n => n * 10) pasa una función que devuelve un elemento transformado y produce [10, 20, 30]. Cambiarla a n => { n * 10; } hace que se pierda silenciosamente cada valor devuelto.
Puntos clave
- Un cuerpo conciso de función flecha devuelve su expresión automáticamente.
- Un cuerpo de bloque de función flecha necesita un
returnexplícito para devolver un valor calculado. - Usa paréntesis alrededor de un literal de objeto devuelto por una función flecha concisa.
- Las listas vacías, con varios parámetros, con parámetros predeterminados y con parámetros de resto necesitan paréntesis.
- Las funciones flecha no tienen sus propios
thisniargumentsy no pueden ser constructores.
2Pasa valores a funciones y obtén resultados
Una llamada a función tiene una parte de entrada y otra de salida. Relaciona argumentos con parámetros, maneja entradas opcionales o flexibles y sigue el valor devuelto a quien llamó. También distinguirás los cambios locales de los cambios en un array compartido y seguirás las llamadas anidadas mientras su trabajo se pausa y se reanuda. El mismo modelo de llamada y retorno explica la recursión sencilla, en la que varias llamadas de una función esperan a que terminen otras llamadas más pequeñas.
2.1 Relaciona los argumentos con los parámetros
Los parámetros de una función dan nombre a los valores proporcionados por una llamada. Para seguir una llamada, relaciona primero las entradas por posición y después sigue las operaciones del cuerpo.
Relaciona posiciones y tipos
function area(width, height) {
return width * height;
}
area(4, 2.5);
width y height son parámetros; 4 y 2,5 son argumentos. Durante esta llamada, width contiene 4 y height contiene 2,5, por lo que el resultado es 10. Los nombres de los parámetros no se relacionan con los nombres de las variables de quien llama. Con divide(a, b), cambiar divide(10, 2) por divide(2, 10) cambia el resultado de 5 a 0,2.
JavaScript no exige un número concreto de argumentos. area(4) deja height como undefined; multiplicar por ese valor produce NaN. area(4, 2, 99) devuelve 8 porque este cuerpo no usa el tercer argumento. Una función puede recopilar argumentos adicionales, pero los que no se usan no causan un error automático.
Los tipos también importan aunque se acepte la llamada. area("4", 2) devuelve 8 porque la multiplicación convierte el texto. Una función de suma se comporta de otra manera: add("4", 2) devuelve «42» cuando su cuerpo es return a + b;. Si el texto representa un número, add(Number("4"), 2) devuelve 6.
Reasigna un parámetro local
Cada parámetro es local a su llamada. Asignarle un número nuevo no reasigna la variable de quien llama:
let score = 5;
function reset(s) {
s = 0;
}
reset(score);
La llamada copia el valor 5 en s. La asignación cambia s a 0 y score sigue valiendo 5. Los números, las cadenas y los booleanos siguen esta regla de copia de valores. El código externo no puede leer s directamente; una función anidada puede conservar el acceso a él mediante un cierre.
Modifica un array mediante una referencia copiada
Un valor de tipo array es una referencia a un array. Al pasar ese valor se copia la referencia, por lo que quien llama y el parámetro pueden acceder al mismo array:
const list = [1, 2];
function addItem(arr) {
arr.push(3);
}
addItem(list);
Ahora list es [1, 2, 3]. La función no reasignó list; modificó el array al que acceden ambos nombres. Una declaración const impide sustituir la referencia almacenada, no cambiar el contenido del array.
Compara con una reasignación local:
function clearLocal(arr) {
arr = [];
}
clearLocal(list);
Esto hace que solo arr apunte a un array vacío nuevo. list sigue apuntando a [1, 2, 3]. Pregúntate si una operación modifica un array existente, como hace push, o da a un nombre un valor diferente, como hace =.
Devuelve un array nuevo cuando la entrada deba permanecer intacta
function withItem(arr, item) {
return arr.concat(item);
}
withItem([1, 2], 3) produce [1, 2, 3] como un array nuevo. El array de entrada sigue siendo [1, 2]. Esto hace que la función sea útil cuando quien llama necesita tanto la lista original como la ampliada. Documenta mediante el nombre y el comportamiento de una función los cambios que se espera que haga en sus entradas, para que quienes la llamen puedan usarla de forma predecible.
Puntos clave
- Los argumentos se relacionan con los parámetros por posición.
- Un argumento ausente proporciona
undefined; se permiten argumentos adicionales sin usar. - Reasignar un parámetro no reasigna la variable de quien llama.
- Modificar un array mediante un parámetro cambia el array compartido.
- Devolver un array nuevo puede preservar la entrada.
2.2 Establece valores predeterminados y acepta cualquier número de argumentos
Los valores predeterminados hacen que una entrada sea opcional; los parámetros de resto permiten que una llamada proporcione muchas entradas. La sintaxis de propagación conecta un array existente con una función que espera argumentos separados.
Aplica un valor predeterminado solo para undefined
function greet(name = "friend") {
return "Hello, " + name;
}
Tanto greet() como greet(undefined) devuelven «Hello, friend». greet(null) devuelve «Hello, null» y greet("") devuelve «Hello, ». Un valor predeterminado se aplica a undefined, no a todos los valores que parecen vacíos.
Los valores predeterminados se evalúan cuando una llamada los necesita. Pueden usar parámetros anteriores: function makeBox(width, height = width) { return width * height; } devuelve 9 para makeBox(3) y 12 para makeBox(3, 4). El segundo argumento explícito sustituye el valor predeterminado para esa llamada.
Pon primero las entradas obligatorias. Con function total(amount, rate = 0.25) { return amount + amount * rate; }, total(80) devuelve 100 y total(80, 0.5) devuelve 120. Invertir los parámetros haría que la llamada habitual con la tasa predeterminada requiriera total(undefined, 80).
El código antiguo puede usar level = level || 50;. Esto también sustituye 0, false y una cadena vacía porque son valores falsos en contexto booleano. Un parámetro predeterminado conserva esos valores: function setVolume(level = 50) { return level; } devuelve 0 para setVolume(0) y 50 para setVolume(undefined).
Reúne argumentos con un parámetro de resto
function sum(...numbers) {
let total = 0;
for (const n of numbers) {
total += n;
}
return total;
}
sum(1, 2, 3) coloca [1, 2, 3] en numbers y devuelve 6. sum() le da un array vacío, por lo que el bucle no suma nada y devuelve 0. Un parámetro de resto es un array real, listo para usar métodos de array o recorrerlo.
Una entrada fija puede aparecer antes del parámetro de resto: en function log(label, ...values) { console.log(label, values); }, log("scores", 7, 9) da a label la cadena «scores» y a values el array [7, 9]. Solo se permite un parámetro de resto y debe ir al final; de lo contrario, JavaScript no puede asignar los argumentos restantes sin ambigüedad con esta sintaxis.
Expande argumentos con propagación
Ubicación de ... | Función | Ejemplo |
|---|---|---|
| Lista de parámetros de una definición | Reunir los argumentos restantes | function sum(...numbers) { ... } |
| Lista de argumentos de una llamada | Expandir los elementos de un array | sum(...values) |
Math.max(...[3, 7, 2]) recibe tres números y devuelve 7. Math.max([3, 7, 2]) recibe un array y devuelve NaN. Los tres puntos son idénticos en el texto, pero la sintaxis que los rodea indica qué función cumplen.
Las funciones ordinarias también tienen un objeto arguments que contiene los argumentos que se les pasaron. Es similar a un array, con entradas indexadas y una longitud, pero no es un array real y no tiene métodos propios map ni filter. Las funciones flecha no tienen su propio arguments. Para código nuevo que necesita una lista flexible de entradas, prefiere un parámetro de resto.
Puntos clave
- Los valores predeterminados se aplican a argumentos ausentes y a
undefinedexplícito, y conservan 0 ynull. - Una expresión predeterminada se evalúa en cada llamada que la necesita.
- Un parámetro de resto reúne los argumentos restantes en un array y va al final.
- La propagación expande un array en argumentos de llamada separados.
- El antiguo objeto
argumentses similar a un array; un parámetro de resto produce un array real.
2.3 Devuelve resultados desde una función
Un valor devuelto es la salida que otro código recibe de una llamada. Mostrarlo puede enseñar el mismo valor en pantalla, pero da a quien llama un resultado diferente a menos que la función también lo devuelva.
Usa la llamada como un valor
function double(n) {
return n * 2;
}
const x = double(5) + 1;
double(5) devuelve 10, por lo que la asignación se evalúa como 10 más 1 y almacena 11. Una llamada puede aparecer en cualquier lugar donde se necesite un valor adecuado. double(double(3)) obtiene primero 6 de la llamada interna y después devuelve 12 desde la externa. Math.round(double(2.3)) duplica el valor hasta 4,6 y lo redondea a 5.
return también termina inmediatamente la llamada actual. Una cláusula de guarda maneja un caso especial antes del trabajo principal:
function safeDivide(a, b) {
if (b === 0) {
return null;
}
return a / b;
}
safeDivide(6, 0) devuelve null y nunca llega a la división. safeDivide(6, 2) omite la cláusula de guarda y devuelve 3. Coloca la condición antes de la operación que protege.
Distingue el resultado de su visualización
function addLog(a, b) {
console.log(a + b);
}
addLog(2, 3) muestra 5, pero su valor de retorno es undefined. Por tanto, addLog(2, 3) * 2 muestra 5 y se evalúa como NaN. Una función que llega a su final sin un retorno, o ejecuta un return; sin valor, devuelve undefined. La visualización en la consola es un efecto secundario, no el resultado numérico utilizable de la llamada.
Si quien llama necesita una suma, escribe return a + b;. Entonces podrá elegir entre mostrarla, almacenarla o usarla en otro cálculo. Así las decisiones de visualización quedan fuera de un cálculo reutilizable.
Mantén la expresión devuelta en la misma línea
function brokenAdd(a, b) {
return
a + b;
}
JavaScript termina la sentencia return en el salto de línea. La función devuelve undefined; nunca se llega a la expresión posterior. Para una expresión larga, empieza a escribirla o abre un paréntesis en la línea de return y después continúa debajo.
Agrupa varios resultados en un valor
Una llamada devuelve un valor, pero ese valor puede ser un array:
function minMax(nums) {
return [Math.min(...nums), Math.max(...nums)];
}
const [low, high] = minMax([4, 9, 1]);
El array devuelto es [1, 9]. La desestructuración asigna su primera entrada a low y la segunda a high. Sigue habiendo un solo valor devuelto, que contiene dos resultados útiles.
Una función pura calcula su salida a partir de sus argumentos y no cambia nada fuera de sí misma. double es pura. Mostrar valores, actualizar una variable externa y modificar un array de entrada son efectos secundarios. Los cálculos puros son fáciles de comprobar con ejemplos de entradas y salidas; los efectos secundarios pertenecen a funciones con nombres claros allí donde el programa necesita cambios visibles.
Puntos clave
returnproporciona el valor de la llamada y termina la llamada actual.- Mostrar un valor sin devolverlo deja el valor de retorno de la función en
undefined. - Empieza la expresión devuelta en la línea de
return. - Un array devuelto puede contener varios resultados.
- Las funciones puras dependen de sus argumentos y no hacen cambios externos.
2.4 Sigue las llamadas a funciones en la pila de llamadas
Cuando una función llama a otra, la función que llama se pausa hasta que termina esa llamada. La pila de llamadas registra el trabajo pendiente para que la ejecución pueda reanudarse en el lugar correcto.
Añade y elimina marcos de llamadas
Cada llamada activa tiene un marco de pila que contiene sus parámetros, variables locales y lugar de retorno. Una nueva llamada se coloca en la cima de la pila; un retorno elimina ese marco superior.
function square(n) {
return n * n;
}
function sumOfSquares(a, b) {
return square(a) + square(b);
}
sumOfSquares(3, 4);
Primero, sumOfSquares comienza con a igual a 3 y b igual a 4. Llama a square(3), que devuelve 9 y termina. Después se ejecuta square(4) y devuelve 16. La suma pausada ahora puede completarse como 9 más 16, y sumOfSquares devuelve 25. Las dos llamadas a square no se solapan en este seguimiento.
Cada llamada obtiene parámetros y variables locales nuevos. Llamar dos veces a la misma función no reutiliza el estado numérico local de una llamada anterior. Incluso cuando una función se llama a sí misma, la nueva llamada tiene su propio marco.
Reduce un problema con recursión
Una función recursiva se llama a sí misma. Un caso base termina sin otra llamada a sí misma; un caso recursivo da un paso hacia ese caso base.
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
Para entradas enteras no negativas, esto multiplica los enteros hasta llegar a 1. Sigue factorial(4) en dos direcciones:
factorial(4)espera afactorial(3).- Esa llamada espera a
factorial(2). - Esa llamada espera a
factorial(1). - El caso base devuelve 1.
- Las llamadas en espera devuelven 2, después 6 y después 24.
La multiplicación en la llamada con n igual a 4 ocurre después de que termine la llamada más pequeña. Su propia n se ha mantenido en 4 mientras esperaba.
El caso base debe ser alcanzable. Usar solo n === 1 falla para una entrada inicial de 0 si cada llamada recursiva resta 1: los valores se vuelven negativos y nunca llegan a 1. n <= 1 cubre tanto 0 como 1. Sin una vía de parada, las llamadas repetidas agotan la pila. Chrome y Node.js muestran «RangeError: Maximum call stack size exceeded»; Firefox usa «InternalError: too much recursion».
Reconoce una versión más pequeña de la misma tarea
function countItems(list) {
let count = 0;
for (const item of list) {
if (Array.isArray(item)) {
count += countItems(item);
} else {
count += 1;
}
}
return count;
}
countItems([1, [2, 3], [4, [5]]]) devuelve 5. Cada array anidado plantea la misma pregunta de recuento sobre una lista más pequeña. Un bucle también es adecuado para un factorial sencillo; la recursión es especialmente útil cuando los propios datos están anidados.
Si falla una llamada, lee la traza de la pila desde la llamada más reciente, arriba. Las entradas siguientes muestran qué llamadas pendientes condujeron hasta ella. Las entradas repetidas de una función pueden revelar una recursión que no se detuvo.
Puntos clave
- La pila de llamadas registra las llamadas pendientes; los retornos eliminan el marco más reciente.
- Cada invocación tiene sus propios parámetros y variables locales.
- La recursión necesita un caso de parada y avanzar hacia él.
- Los resultados recursivos vuelven a través de las llamadas en espera en orden inverso.
- Una traza de la pila muestra la llamada que falló y las llamadas que condujeron a ella.
- Los datos anidados pueden contener versiones más pequeñas de la misma tarea.
3Explica dónde se puede usar una variable
Los nombres de las variables tienen significado dentro de ámbitos. Los límites de los bloques y las funciones, y el lugar donde se define una función, determinan a qué declaraciones puede acceder. Seguirás esos límites para predecir un valor o un error y después distinguirás el sombreado de variables de la actualización de una variable externa. La elevación de declaraciones y la zona muerta temporal explican por qué un nombre puede estar registrado antes de que la ejecución llegue a su línea y, aun así, no estar disponible para su uso.
3.1 Distingue los ámbitos de bloque, de función y global
El ámbito de una variable es la región donde se puede usar su nombre. El ámbito permite que las funciones mantengan separados sus datos de trabajo y, al mismo tiempo, usen entradas y valores del código que las rodea.
Delimita las declaraciones
let y const tienen ámbito de bloque. Un bloque es una región dentro de llaves, como el cuerpo de un if, de un bucle o de una función:
if (true) {
const msg = "hi";
console.log(msg);
}
La instrucción que muestra el valor funciona dentro del bloque. Añadir console.log(msg); después del bloque genera un ReferenceError porque ese código externo no puede acceder a esta declaración. Otra declaración externa con el mismo nombre sería una variable diferente.
Los parámetros y las variables dentro de una función son locales a esa función. En function f() { let temp = 1; }, el código externo no puede leer temp. Dos funciones pueden tener cada una una variable local total sin compartirla. Cada llamada comienza con variables locales nuevas; una función anidada devuelta puede conservar después el acceso mediante un cierre.
Lee declaraciones antiguas con var
| Declaración | Límite |
|---|---|
let o const dentro de un bloque | Ese bloque |
var dentro de una función | La función que lo contiene, incluso cuando está escrito en un bloque interno |
| Declaración de nivel superior en un script normal | El ámbito global del script |
| Declaración de nivel superior en un módulo | Ese módulo |
function test() {
if (true) {
var x = 1;
}
return x;
}
test() devuelve 1 porque var ignora el límite del if. Sustituirlo por let x = 1; hace que falle el retorno: la x del bloque no está disponible allí. Del mismo modo, for (let i = 0; i < 3; i++) {} mantiene i dentro del bucle, mientras que una var i permanece disponible en la función o el script que la contiene después del bucle y vale 3.
Busca hacia fuera desde la posición donde está escrita la función
Los ámbitos se anidan. Una función interna puede usar nombres de los ámbitos que la contienen, pero el código externo no puede buscar hacia dentro en las variables locales de una función interna. Esto es el ámbito léxico: el lugar donde está escrito el código determina los nombres disponibles.
const level = "global";
function show() {
return level;
}
function run() {
const level = "local";
return show();
}
run() devuelve «global». show se definió fuera de run, por lo que el ámbito que la contiene incluye la level externa. Llamarla desde dentro de run no le da acceso a la level local de quien llama.
Mantén el estado compartido bajo control
Una variable de nivel superior en un script normal es global y las funciones de ese script pueden acceder a ella. Las variables globales compartidas pueden hacer que tareas independientes se afecten entre sí; los scripts de una misma página también pueden entrar en conflicto por los nombres. Pasar entradas y devolver salidas mantiene visibles las dependencias.
Declara todas las variables. En modo estricto, count = 5; genera un ReferenceError si no existe una declaración accesible. Sin modo estricto, una asignación así puede crear una variable global accidental. Un script puede activar el modo estricto con "use strict"; al principio; los módulos lo activan automáticamente. Prefiere const para valores que no se reasignan y let cuando se necesita reasignación.
Puntos clave
letyconstrespetan los límites del bloque;varrespeta el límite de la función que lo contiene.- Los parámetros y las variables locales de una función no están disponibles para el código externo.
- El ámbito léxico depende de dónde se define una función, no de dónde se llama.
- Las variables de nivel superior de un módulo pertenecen al módulo.
- Declara los nombres explícitamente para evitar asignaciones globales accidentales.
3.2 Predice a qué variable se refiere un nombre
El mismo nombre puede aparecer en varios ámbitos. JavaScript encuentra la declaración correspondiente buscando hacia fuera desde el código actual y deteniéndose en la primera coincidencia.
Sigue la cadena de ámbitos
let x = 1;
function f() {
let x = 2;
return x;
}
f() devuelve 2. La declaración local oculta la x externa, por lo que la búsqueda se detiene dentro de f. El valor externo sigue siendo 1. Un parámetro puede ocultar un nombre de la misma manera. Si ningún ámbito de la cadena declara un nombre, leerlo genera un ReferenceError.
Una asignación no declara una variable nueva. Con let count = 0; function add() { count = count + 1; }, add encuentra la declaración externa y la modifica. Añadir let cambia el significado: crea un nuevo nombre local, que puede ocultar precisamente el valor que querías leer.
Distingue el registro de la inicialización
La elevación de declaraciones describe cómo se registran las declaraciones antes de que se ejecute su ámbito. Su comportamiento inicial es diferente:
| Declaración | Antes de su línea de inicialización |
|---|---|
| Declaración de función | Su función está disponible |
var | Su valor es undefined |
let o const | Acceder a ella genera un ReferenceError |
Por ejemplo, console.log(a); var a = 5; muestra undefined. La asignación del valor 5 aún no se ha producido. En cambio, console.log(b); let b = 5; falla. El intervalo durante el cual el nombre léxico existe pero no se puede acceder a él es su zona muerta temporal, que suele abreviarse como TDZ.
Sigue el sombreado antes de una línea de declaración
let n = 10;
function f() {
console.log(n);
let n = 20;
}
f();
La n interna oculta a la externa en todo el cuerpo de la función, incluso antes de la línea donde está escrita la declaración. En console.log(n), ese nombre interno sigue en su zona muerta temporal, por lo que la llamada genera un ReferenceError. JavaScript no pasa por alto una declaración local sin inicializar para recurrir al valor externo 10.
El mismo problema aparece en let count = count + 1; dentro de add. El lado derecho lee la nueva count local antes de que se haya inicializado. Si la intención es actualizar un contador externo, usa una asignación sin una nueva declaración. Si la intención es calcular un valor distinto, dale otro nombre y lee explícitamente el contador externo.
Distingue la reasignación de la mutación
let y const no pueden volver a declarar el mismo nombre en un ámbito; hacerlo produce un SyntaxError. Un ámbito interno puede declarar su propio nombre, lo que constituye sombreado. var permite declaraciones repetidas en su ámbito, lo que dificulta detectar la reutilización accidental de nombres.
const impide reasignar su variable. No congela un array:
const list = [];
list.push(1);
La referencia almacenada en list sigue siendo la misma mientras el array cambia a [1]. list = []; intentaría sustituir la referencia almacenada y fallaría.
Mantén cada declaración en el ámbito más pequeño que la necesite. Usar nombres distintos para tareas distintas reduce la necesidad de seguir mentalmente el sombreado, e inicializar antes de usar evita errores de zona muerta temporal.
Puntos clave
- La búsqueda de un nombre se detiene en la declaración más cercana de los ámbitos que lo contienen.
- Una declaración local oculta un nombre externo incluso antes de su inicialización.
varcomienza comoundefined; no se puede acceder aletni aconsten su zona muerta temporal.- Una asignación actualiza un enlace existente; una declaración crea uno.
constimpide la reasignación, pero el contenido de un array aún puede cambiar.- Volver a declarar un nombre en un ámbito es distinto de ocultarlo en un ámbito anidado.
4Usa cierres para dar a las funciones su propia memoria
Una función devuelta puede conservar el acceso a las variables que rodean su definición. Esa conexión conservada da memoria a un contador y permite que una fábrica cree funciones con su propia configuración. Seguirás llamadas separadas a una fábrica, el estado compartido y los cambios posteriores en variables capturadas, y después usarás las mismas reglas para explicar las funciones creadas en un bucle. El estado privado se deriva del ámbito léxico ordinario, incluso después de que termine la llamada externa.
4.1 Explica qué recuerda un cierre
Una función conserva un enlace al ámbito donde se creó. La función y ese acceso conservado forman un cierre. El enlace resulta especialmente útil cuando una función anidada se devuelve y se llama después de que haya terminado su llamada externa.
Da memoria a una función devuelta
function makeCounter() {
let count = 0;
return function () {
count = count + 1;
return count;
};
}
const counter = makeCounter();
La llamada a makeCounter crea count, crea una función interna y devuelve esa función. Todavía no ejecuta el cuerpo interno. Ahora counter contiene el valor de la función. Su primera llamada cambia la count conservada de 0 a 1 y devuelve 1. La siguiente cambia la misma variable a 2 y devuelve 2.
El marco de pila de la llamada externa ha terminado, pero la variable capturada sigue siendo accesible. Una variable local que ya no tiene ningún uso accesible puede liberarse; aquí la función interna, a la que aún se puede llamar, sigue necesitándola. El acceso mediante un cierre sobrevive al retorno sin convertir la variable en global.
Crea memorias independientes con llamadas separadas
const a = makeCounter();
const b = makeCounter();
a();
a();
b();
Los resultados son 1, 2 y 1. Cada llamada a la fábrica creó una count diferente. Las dos funciones devueltas por llamadas separadas tienen el mismo código, pero distintos entornos conservados.
Copiar una referencia a una función devuelta funciona de otra manera. Si const alias = a;, entonces alias() usa el contador existente de a; no llama a makeCounter ni crea otro entorno.
Conserva variables, no copias de sus valores
let greeting = "Hi";
const say = () => greeting;
greeting = "Hello";
say() devuelve «Hello». El cierre lee la variable cuando se ejecuta, por lo que una actualización posterior es visible. No almacena una copia fija de «Hi» en el momento de su creación.
El código externo no puede acceder directamente a la count local y counter.count es undefined. Solo el código que tiene acceso a ese entorno léxico puede leerla o actualizarla. Llamar al contador desde una función diferente no cambia qué contador usa.
Comparte estado dentro de una llamada externa
function makeWallet() {
let balance = 0;
return [
function deposit(n) { balance += n; return balance; },
function getBalance() { return balance; }
];
}
const [deposit, getBalance] = makeWallet();
deposit(50);
deposit(20);
getBalance() devuelve 70 porque ambas funciones devueltas se crearon en la misma llamada y se refieren a la misma balance. Otra llamada a makeWallet() crea un saldo separado.
Puntos clave
- Un cierre conserva el acceso al ámbito donde se creó su función.
- Una variable capturada puede seguir siendo utilizable después de que termine su llamada externa.
- Las llamadas separadas a una fábrica crean entornos separados.
- Los cierres leen los valores actuales de las variables, no copias fijas tomadas al crearse.
- Las funciones de una misma llamada externa pueden compartir estado privado.
4.2 Usa cierres para crear fábricas de funciones y mantener estado privado
Una fábrica de funciones usa el acceso de los cierres para crear funciones con su propia configuración o estado. Quien llama proporciona la configuración una vez y después usa muchas veces la función devuelta.
Configura una función con un parámetro
function makeMultiplier(factor) {
return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
double(5) devuelve 10 y triple(5) devuelve 15. Cada función flecha devuelta conserva el factor de su propia llamada a la fábrica. La función externa configura el comportamiento; la interna realiza el trabajo repetido.
La forma compacta es const makeMultiplier = (factor) => (n) => n * factor;. Dos flechas significan que se crean dos funciones, no que se hacen dos cálculos consecutivos sobre un número. makeMultiplier(4)(5) primero devuelve una función configurada con 4 y después la llama con 5 para obtener 20.
Aplica una regla al estado privado
function makeLimiter(max) {
let used = 0;
return function () {
if (used >= max) {
return false;
}
used = used + 1;
return true;
};
}
const tryLogin = makeLimiter(3);
Las tres primeras llamadas devuelven true e incrementan used. La cuarta devuelve false antes de incrementarla. used es local a la llamada a la fábrica, por lo que el código externo no puede restablecerla mediante ese nombre. La función devuelta controla qué cambios están disponibles. Otro limitador tendría su propia variable used y su propio máximo.
Analiza funciones creadas en bucles
const fns = [];
for (var i = 0; i < 3; i++) {
fns.push(() => i);
}
El bucle crea tres funciones, pero una sola var i. Después del bucle, esa variable contiene 3. Llamar ahora a cualquiera de las funciones almacenadas devuelve 3 porque todos los cierres leen el mismo enlace actual. Crear una función no evalúa su cuerpo ni guarda una copia del valor actual de i.
Cambiar la cabecera a let i = 0 da al bucle un enlace separado para cada iteración. Las funciones almacenadas devuelven entonces 0, 1 y 2. La misma distinción explica la impresión diferida: una función de devolución de llamada de setTimeout que lee var i después del bucle ve el valor final, mientras que la versión con let conserva el valor de cada iteración.
Reconoce una expresión invocada inmediatamente
El código antiguo suele crear un ámbito de función privado y ejecutarlo inmediatamente:
(function () {
var hidden = 1;
console.log(hidden);
})();
Esta es una expresión de función invocada inmediatamente, o IIFE. Los paréntesis exteriores crean una expresión de función; los paréntesis finales la llaman. hidden pertenece a esa función, lo que la mantiene fuera del ámbito global circundante. Es un patrón habitual en código escrito antes de las declaraciones con ámbito de bloque y los módulos.
El acceso conservado también afecta a la memoria. Un cierre al que se puede llamar y que necesita un array grande mantiene esos datos accesibles. Cuando ya no se puede acceder a la función ni a sus datos capturados, estos pueden liberarse. Conserva cierres de larga duración de forma deliberada cuando su memoria forme parte de la tarea.
Puntos clave
- Las fábricas devuelven funciones configuradas mediante sus argumentos.
- Las variables privadas se pueden cambiar mediante las operaciones devueltas.
- Los cierres de un bucle con
varcomparten un enlace; las iteraciones conletreciben enlaces separados. - Una IIFE crea un ámbito de función y lo ejecuta inmediatamente.
- Los cierres accesibles mantienen accesibles los datos capturados que necesitan.
5Pasa funciones a otras funciones
Pasar una función permite a quien llama elegir una parte del comportamiento de otra función. Examinarás cómo las funciones receptoras invocan las funciones de devolución de llamada, qué argumentos les proporcionan y cómo usan el resultado. Los métodos de array que ya conoces se convierten en ejemplos de patrones reutilizables de orden superior. Por último, las funciones devueltas envolverán llamadas, conectarán salidas con entradas o mantendrán argumentos seleccionados para usarlos más tarde, combinando valores de función con el acceso que proporcionan los cierres.
5.1 Pasa una función de devolución de llamada a una función
Una función de devolución de llamada es una función que se proporciona como argumento para que otra función la llame. Permite que la función receptora se encargue de la tarea general mientras quien llama proporciona una acción o comprobación concreta.
Pasa una acción a un bucle
function repeat(times, action) {
for (let i = 0; i < times; i++) {
action(i);
}
}
repeat(3, (i) => console.log(i));
repeat controla el bucle y elige los valores 0, 1 y 2. La función flecha determina qué hacer con cada valor: mostrarlo. Cada action(i) llama a la función proporcionada con el número de esa iteración.
Puedes pasar una función existente o escribir una en el punto de uso. repeat(2, sayHi) pasa la función con nombre. repeat(2, (i) => console.log("Round " + (i + 1))) muestra «Round 1» y «Round 2». Una función de devolución de llamada puede ignorar los argumentos que no necesita.
Pasa el valor, sin llamar antes a la función
repeat(3, sayHi()) evalúa sayHi() al preparar los argumentos. Si sayHi solo muestra texto, su valor de retorno es undefined, por lo que repeat recibe ese valor como action. La llamada posterior action(i) falla con un TypeError porque no se puede llamar a undefined como función.
Usa repeat(3, sayHi) para pasar la función. Si la llamada posterior necesita argumentos elegidos, pasa una función envolvente como () => sayHi() para que la llamada ocurra dentro del cuerpo de la función de devolución de llamada. La creación y la invocación de una función son pasos separados.
Comprueba qué argumentos proporciona la función receptora
Las funciones de devolución de llamada de los arrays suelen recibir más de un argumento: el elemento, el índice y el array completo. Pasar una función existente solo es seguro cuando esas entradas corresponden al significado de sus parámetros.
["1", "2", "3"].map(parseInt);
Esto devuelve [1, NaN, NaN]. parseInt acepta un segundo argumento para la base numérica, por lo que el índice del array cambia su comportamiento. Usa ["1", "2", "3"].map((s) => parseInt(s, 10)) para elegir explícitamente la base 10, o .map(Number) para convertir estas cadenas.
Devuelve una respuesta a la función receptora
function countIf(arr, test) {
let n = 0;
for (const x of arr) {
if (test(x)) {
n++;
}
}
return n;
}
countIf([3, 8, 12, 5], (x) => x > 4) devuelve 3. La función de devolución de llamada responde una pregunta para cada elemento; countIf usa cada respuesta para decidir si incrementa el contador. Compáralo con repeat, cuyo bucle ignora el valor de retorno de la función de devolución de llamada. El código o la documentación de la función receptora indican tanto los argumentos como la función del resultado.
Las funciones de devolución de llamada pueden ejecutarse inmediatamente o más tarde. repeat y los métodos de array las llaman de forma síncrona antes de terminar. setTimeout(sayHi, 2000) programa una para más tarde mientras el código posterior continúa inmediatamente. Ser una función de devolución de llamada describe cómo se proporciona una función, no cuándo debe ejecutarse.
Puntos clave
- Una función de devolución de llamada se pasa para que otra función la llame.
- La función receptora controla los argumentos de la función de devolución de llamada y si se usa su resultado.
- Añadir los paréntesis de llamada demasiado pronto puede pasar un resultado en lugar de una función.
- Los argumentos de una función de devolución de llamada de un array pueden cambiar el comportamiento de una función existente.
- Las funciones de devolución de llamada pueden ser síncronas o diferidas.
5.2 Usa y escribe funciones de orden superior
Una función de orden superior acepta una función, devuelve una función o hace ambas cosas. El valor de función proporciona el comportamiento, mientras que la función de orden superior gestiona un patrón reutilizable.
Escribe el patrón una vez
function myMap(arr, transform) {
const result = [];
for (const item of arr) {
result.push(transform(item));
}
return result;
}
myMap([1, 2, 3], (n) => n * n) devuelve [1, 4, 9]. El bucle y la construcción del array de salida se mantienen iguales; la función de devolución de llamada decide cómo cambia cada entrada. Un filtro correspondiente añade un elemento solo cuando test(item) es verdadero en contexto booleano. Con [4, 7, 10, 3] y (n) => n % 2 === 0, ese patrón conserva [4, 10].
Elige el contrato de devolución de llamada que necesitas
| Método | Función de la devolución de llamada | Resultado del método |
|---|---|---|
map | Devolver cada valor transformado | Un array nuevo de la misma longitud |
filter | Devolver un valor verdadero en contexto booleano para los elementos que se conservan | Un array nuevo con los elementos seleccionados |
find | Comprobar si un elemento coincide | La primera coincidencia, o undefined |
forEach | Realizar una acción para cada elemento | undefined |
reduce | Devolver el siguiente acumulador | El acumulador final |
map, filter y find no modifican por sí mismos el array de entrada, pero las funciones de devolución de llamada sí pueden hacerlo. Que un método devuelva un array nuevo no hace que todas las funciones de devolución de llamada proporcionadas sean puras.
Mantén un resultado durante una reducción
[5, 10, 15].reduce((total, n) => total + n, 0);
El acumulador inicial es 0. La función de devolución de llamada devuelve 5 para el primer elemento, 15 para el segundo y 30 para el tercero. Cada valor devuelto se convierte en el total de la siguiente llamada. El método devuelve 30 después del último elemento. Un array vacío sin acumulador inicial hace que reduce genere un TypeError; un valor inicial explícito también define un resultado útil para una entrada vacía.
Devolver arrays permite encadenar llamadas. [3, 8, 12, 5].filter((n) => n > 4).map((n) => n * 2) primero conserva [8, 12, 5] y después lo transforma en [16, 24, 10]. Lee la cadena en orden de ejecución, de izquierda a derecha.
Proporciona una regla de ordenación numérica
Sin un comparador, sort compara las entradas como cadenas. Por tanto, [10, 9, 1].sort() produce [1, 10, 9]. Para ordenar números de forma ascendente, usa .sort((a, b) => a - b). Un retorno negativo coloca a primero, uno positivo coloca b primero y cero deja el par como equivalente para la ordenación. A diferencia de map y filter, sort reorganiza su array de entrada.
Comprueba los valores devueltos
[1, 2, 3].map((n) => { n * 2; }) produce [undefined, undefined, undefined] porque el bloque no tiene retorno. Usar forEach para construir un nuevo resultado mediante una asignación también falla si esperas que el propio método devuelva un array. Elige map para transformar y forEach para acciones cuyos resultados no necesitan recopilarse.
Un método de orden superior expresa claramente un patrón conocido. Un bucle sencillo sigue siendo útil cuando el trabajo necesita break o no encaja en el contrato del método.
Puntos clave
- Las funciones de orden superior reciben funciones, devuelven funciones o hacen ambas cosas.
map,filteryfindusan de distintas maneras los resultados de las funciones de devolución de llamada.forEachdevuelveundefined;reducedevuelve el acumulador final.- Una función de devolución de llamada de reducción debe devolver el siguiente acumulador.
- La ordenación numérica necesita un comparador y modifica el array original.
- Un método que devuelve un array nuevo aún puede ejecutar funciones de devolución de llamada con efectos secundarios.
5.3 Construye nuevas funciones a partir de otras existentes
Una función puede devolver una función nueva que recuerda otra existente. Las funciones envolventes, la composición y la aplicación parcial usan este patrón para adaptar el comportamiento sin copiar el cuerpo de la función original.
Envuelve una llamada y conserva su resultado
function logged(fn) {
return (...args) => {
console.log("Calling with", args);
const result = fn(...args);
console.log("Result", result);
return result;
};
}
const add = (a, b) => a + b;
logged(add) crea una función que conserva add como fn. Llamar a logged(add)(2, 3) reúne los argumentos, los pasa a add, muestra el valor devuelto y devuelve 5. El parámetro de resto reúne las entradas de la función envolvente; la propagación las pasa. El retorno final mantiene la salida disponible para cálculos posteriores.
logged(add(2, 3)) pasa el número 5. Llamar después a esa función envolvente intenta llamar a 5 y genera un TypeError. Hay dos etapas: configurar la función envolvente con una función y después llamar a la función devuelta con entradas.
Conserva estado alrededor de una operación
function once(fn) {
let done = false;
let result;
return (...args) => {
if (!done) {
done = true;
result = fn(...args);
}
return result;
};
}
Para una operación que termina normalmente, la primera llamada ejecuta fn y almacena su resultado. Las llamadas posteriores devuelven ese valor almacenado sin volver a ejecutar fn, aunque sus argumentos sean diferentes. El booleano registra si la operación se ejecutó; comprobar si el resultado es verdadero en contexto booleano sería incorrecto cuando un resultado válido es 0 o false. Cada llamada a once crea su propio indicador y resultado almacenado.
Pasa un resultado a otra función
const compose = (f, g) => (x) => f(g(x));
const addOne = (n) => n + 1;
const square = (n) => n * n;
compose(square, addOne)(3) ejecuta primero addOne(3) y después eleva 4 al cuadrado para devolver 16. compose(addOne, square)(3) primero eleva 3 al cuadrado y después suma 1, y devuelve 10. La función más cercana a la entrada se ejecuta primero, por lo que importa el orden de los argumentos de compose.
Fija algunos argumentos de antemano
function partial(fn, ...fixed) {
return (...rest) => fn(...fixed, ...rest);
}
const greetWith = (greeting, name) => greeting + ", " + name;
const hello = partial(greetWith, "Hello");
hello("Ana") llama a greetWith("Hello", "Ana") y devuelve «Hello, Ana». Esta función auxiliar de aplicación parcial coloca primero los argumentos fijos y después los argumentos posteriores; cambiar ese orden modifica la llamada subyacente.
Puntos clave
- Una función envolvente conserva una función y añade comportamiento alrededor de su llamada.
- El resto y la propagación permiten que una función envolvente pase argumentos separados.
- Devolver el resultado de la función envuelta permite que quien llama lo use.
- Una función envolvente de ejecución única conserva tanto un indicador como el primer resultado.
compose(f, g)aplicagantes quef.- La aplicación parcial fija determinados argumentos antes de llamadas posteriores.
Ejemplos de Funciones en JavaScript
1Crea, almacena y llama a una función
11 pasos
- 1IntroducciónDistingue la receta de la llamada
Una función almacena instrucciones para que varias llamadas puedan reutilizarlas. Su definición describe una tarea; una llamada realiza esa tarea con entradas concretas.
Sigue un saludo en tres formas: una declaración, una expresión almacenada en una variable y una función flecha. Observa dos detalles a medida que cambia la sintaxis: cuándo está disponible la función y si su cuerpo devuelve un valor.
- 2EjemploDefine un saludo
function greet(name) { console.log("Hello, " + name + "!"); }La palabra clave, el nombre, la lista de parámetros y el cuerpo forman una declaración. Todavía no se ha mostrado nada: la definición almacena las instrucciones.
namees un parámetro cuyo valor llegará desde una llamada. El cuerpo se puede reutilizar porque describe cómo construir el saludo sin elegir de antemano el nombre de una persona. - 3EjemploEjecuta las instrucciones almacenadas
greet("Amira"); greet("Li");Las llamadas muestran «Hello, Amira!» y «Hello, Li!» en líneas separadas. Cada una inicia de nuevo el mismo cuerpo con su propia entrada.
La expresión
greetpor sí sola es el valor de la función. Los paréntesis degreet("Li")solicitan su ejecución. Incluso una función sin entradas necesita paréntesis de llamada, como ensayHi(). - 4DesafíoPredice la llamada anticipada
¿Qué se muestra cuando se ejecuta este script?
show(); function show() { console.log("Ready"); }---reveal---
Muestra «Ready» una vez. La declaración se eleva dentro de su ámbito, por lo que la función está disponible en la primera línea. La elevación no ejecuta el cuerpo por sí sola: lo hace la llamada explícita
show(). Una línea que contuviera soloshow;leería la función sin mostrar nada. - 5ExplicaciónConserva la función como valor
const square = function (n) { return n * n; }; const alias = square;La expresión crea una función y
squarela almacena. La segunda asignación copia su referencia enalias; no calcula un cuadrado. Tantosquare(3)comoalias(3)devuelven 9.En comparación,
const answer = square(3);llama a la función y almacena el número devuelto. - 6DesafíoLocaliza el primer fallo
¿Este script muestra 16?
console.log(square(4)); const square = function (n) { return n * n; };---reveal---
No. Leer
squareantes de su inicialización conconstgenera unReferenceError. La asignación no se ha ejecutado, por lo que el valor de función de la expresión no está disponible. Mueve la llamada debajo de la asignación o usa una declaración de función si necesitas poder llamarla antes en este ámbito. - 7EjemploUsa una función flecha concisa
const square = (n) => n * n; const add = (a, b) => a + b;El cuerpo conciso de una función flecha devuelve su expresión, por lo que
square(4)devuelve 16 yadd(2, 3)devuelve 5.Un solo parámetro simple puede omitir los paréntesis:
n => n * n. Las listas vacías, varios parámetros, los valores predeterminados y los parámetros de resto los necesitan. Las funciones flecha son expresiones, por lo que estas variablesconstsiguen necesitando inicializarse antes de usarlas. - 8DesafíoCorrige un resultado ausente
¿Por qué
add(2, 3)devuelveundefinedy qué cambio hace que devuelva 5?const add = (a, b) => { a + b; };---reveal---
Las llaves comienzan un bloque, por lo que la expresión se evalúa, pero no se devuelve. Escribe
(a, b) => { return a + b; }o elimina las llaves para usar(a, b) => a + b. Ambas correcciones devuelven la suma. Añadir solo un punto y coma después de la asignación no cambia el comportamiento de retorno del cuerpo. - 9ConsejoDevuelve una expresión de objeto
const makePoint = (x, y) => ({ x: x, y: y });Los paréntesis hacen que el literal de objeto sea una expresión devuelta por el cuerpo conciso. Una llave de apertura sin paréntesis comenzaría un bloque.
Las funciones flecha no tienen sus propios
thisniargumentsy no pueden ser constructores. La forma corta funciona bien para cálculos sencillos, pero esas diferencias importan al convertir otros tipos de funciones. - 10DesafíoSigue una función y su alias
¿Qué devuelve
copy(5)?const adjust = (n) => { return n + 2; }; const copy = adjust;---reveal---
Devuelve 7. El bloque de la función flecha contiene un retorno explícito y
copycontiene la misma función invocable queadjust. La asignación no llamó a la función ni almacenó un resultado numérico. Sustituirconst copy = adjust;porconst copy = adjust(5);almacenaría 7, que no se puede llamar como función. - 11RepasoIdentifica la operación de cada línea
Una declaración crea una tarea con nombre que está disponible desde el principio de su ámbito. Una expresión crea un valor de función cuando la ejecución llega a ella. El cuerpo conciso de una función flecha devuelve un valor automáticamente; su cuerpo de bloque necesita
return.Al seguir el código, distingue entre una definición de función, una asignación de referencia y una llamada. Son operaciones diferentes aunque usen el mismo nombre.
2Sigue entradas, salidas y llamadas en espera
16 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónSigue lo que entra y sale de una llamada
- 2EjemploRelaciona los argumentos por posición
- 3EjemploDistingue la mutación de la reasignación
- 4DesafíoConserva la entrada original
- 5ExplicaciónEl valor predeterminado se aplica a undefined
- 6EjemploReúne y después expande
- 7DesafíoUsa un valor predeterminado con un parámetro de resto
- 8EjemploDevuelve el número que necesita quien llama
- 9EjemploDetén una ruta con un retorno anticipado
- 10DesafíoEvalúa de dentro hacia fuera
- 11EjemploRecuerda la llamada pendiente
- 12ExplicaciónDa a la recursión una vía de parada
- 13EjemploResuelve los productos en espera
- 14DesafíoCorrige un límite recursivo
- 15ConexiónInterpreta un fallo mediante la pila
- 16RepasoSigue un valor a través de cada límite
3Encuentra la declaración detrás de un nombre
10 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónPregúntate a dónde pertenece el nombre
- 2EjemploCompara los límites de bloque y de función
- 3EjemploUsa la ubicación de la definición
- 4DesafíoSigue un valor local y otro externo
- 5ExplicaciónDetente en la declaración más cercana
- 6Idea claveEstar registrada no significa estar inicializada
- 7DesafíoPredice la lectura de un nombre ocultado
- 8ConsejoDistingue la reasignación de la mutación
- 9DesafíoCombina una función de devolución de llamada con el momento de búsqueda
- 10RepasoSigue la ubicación antes que el valor
4Da a las funciones devueltas su propia memoria
11 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónConserva el acceso después de un retorno
- 2EjemploCrea un contador sin llamarlo
- 3EjemploActualiza la variable conservada
- 4DesafíoCompara contadores independientes
- 5Idea claveConserva el enlace, no una copia fija
- 6EjemploCrea un cálculo configurado
- 7EjemploMantén un contador de usos privado
- 8DesafíoExplica tres resultados iguales
- 9ConexiónReconoce el ámbito privado en código antiguo
- 10DesafíoComparte una operación configurada
- 11RepasoSigue cuántos entornos existen
5Pasa comportamiento y construye funciones adaptadas
15 pasos
Guarda esta unidad para seguir cada Lección Interactiva paso a paso.
- 1IntroducciónDeja que una función proporcione el comportamiento variable
- 2EjemploLlama a la acción proporcionada
- 3DesafíoUsa el resultado de una función de devolución de llamada
- 4ConsejoExamina todos los argumentos de la función de devolución de llamada
- 5EjemploEscribe un patrón de transformación
- 6ExplicaciónElige la forma del resultado
- 7DesafíoSigue una selección y una transformación
- 8Idea claveControla el orden numérico
- 9EjemploEnvuelve y transmite una llamada
- 10ExplicaciónRecuerda si una llamada se ha ejecutado
- 11EjemploCompón de dentro hacia fuera
- 12EjemploFija entradas antes de la llamada posterior
- 13DesafíoPasa una función de devolución de llamada configurada
- 14DesafíoRecupera un resultado perdido en una función envolvente
- 15RepasoComprueba ambas etapas del código de orden superior
Ejercicios de Funciones en JavaScript
Set 1: Elige entradas y sigue el estado conservado10 Preguntas de Práctica
Pregunta 1 de 10
¿Qué ocurre cuando se ejecuta este script?
announce();
function announce() { console.log("Open"); }
Mostrar respuestaOcultar respuesta
announce ya es una función cuando la primera línea la llama. Su cuerpo muestra «Open» una vez. El momento de inicialización de una expresión de función almacenada en const sería diferente, pero este código usa una declaración.Pregunta 2 de 10
value igual a su entrada n?Mostrar respuestaOcultar respuesta
{ value: n } sea una expresión de objeto, que el cuerpo conciso de la función flecha devuelve. Sin esos paréntesis, las llaves comienzan un bloque y value: n se interpreta como una sentencia etiquetada, sin devolver ningún objeto. Un return; sin valor también devuelve undefined.Pregunta 3 de 10
¿Qué devuelve la llamada?
function combine(first, ...rest) {
return first + rest.length;
}
const values = [8, 2, 6];
combine(...values);
Mostrar respuestaOcultar respuesta
[2, 6], cuya longitud es 2, por lo que la función devuelve 10. Contar los tres valores en rest daría 11, pero el primer argumento ya se ha asignado a first.Pregunta 4 de 10
¿Qué devuelve guarded(0)?
function guarded(n) {
if (n === 0) { return "empty"; }
return "filled";
}
Mostrar respuestaOcultar respuesta
Pregunta 5 de 10
¿Qué devuelve totalDown(3)?
function totalDown(n) {
if (n === 0) { return 0; }
return n + totalDown(n - 1);
}
Mostrar respuestaOcultar respuesta
totalDown(0), que devuelve 0. Las llamadas en espera devuelven después 1, 3 y 6. El retorno del caso base no sustituye el resultado de todas las llamadas por 0; cada marco en espera sigue teniendo su propia n y debe completar su suma pendiente.Pregunta 6 de 10
¿Qué ocurre cuando se llama a read()?
let label = "outer";
function read() {
return label;
let label = "inner";
}
Mostrar respuestaOcultar respuesta
label externa en todo read, incluido el retorno anterior. Esa variable local todavía no está inicializada, por lo que leerla genera un ReferenceError. La asignación inalcanzable no hace que JavaScript omita la declaración local y use la cadena externa.Pregunta 7 de 10
¿Qué devuelve la llamada final?
let word = "first";
const read = () => word;
word = "second";
function run() {
const word = "third";
return read();
}
run();
Mostrar respuestaOcultar respuesta
read conserva acceso a la word del nivel superior, cuyo valor actual es «second». No conserva una copia fija de «first», y llamarla desde run no dirige su búsqueda a la «third» local de quien llama. Tanto la ubicación de la definición como la asignación posterior importan.Pregunta 8 de 10
¿Qué array produce la expresión final?
const readers = [];
for (let i = 1; i <= 3; i++) {
let value = i;
readers.push(() => value);
value += 10;
}
readers.map((read) => read());
Mostrar respuestaOcultar respuesta
value y su función conserva ese enlace. La suma posterior en esa misma iteración cambia el valor que leerá la función a 11, 12 o 13. [1, 2, 3] trataría la creación del cierre como una copia fija e ignoraría esas actualizaciones posteriores.Pregunta 9 de 10
¿Qué ocurre cuando se ejecuta la última línea?
function speak() { console.log("Hi"); }
function twice(action) { action(); action(); }
twice(speak());
Mostrar respuestaOcultar respuesta
speak, por lo que «Hi» se muestra una vez. Su valor de retorno es undefined, que se convierte en action; el primer intento de ejecutar action() genera entonces un TypeError. Pasar speak sin paréntesis de llamada permitiría que twice la invocara dos veces.Pregunta 10 de 10
¿Cuál es el resultado final?
const compose = (f, g) => (x) => f(g(x));
const subtract = (n) => n - 2;
const triple = (n) => n * 3;
const first = compose(triple, subtract);
const second = compose(subtract, triple);
first(second(4));
Mostrar respuestaOcultar respuesta
second(4) triplica 4 y resta 2, por lo que devuelve 10. Ese valor se convierte en la entrada de first, que resta 2 y triplica 8, y devuelve 24. Un resultado de 18 aplica first y second en el orden externo inverso; cada composición también debe mantener su propio orden de llamada, de dentro hacia fuera.Set 2: Lee funciones y sigue sus valores10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
¿Cuál es el valor final de result?
const values = [2, 7, 4, 9];
const result = values
.filter((n) => n > 3)
.map((n) => n - 1)
.reduce((total, n) => total + n, 5);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
function showLabel() { console.log("Ready"); }, ¿qué sentencia llama a la función?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
¿Qué contiene copy después de este código?
const triple = function (n) { return n * 3; };
const copy = triple;
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
¿Qué devuelve adjust(8)?
const adjust = (n) => { n - 3; };
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
¿Qué devuelve difference(12, 5)?
function difference(first, second) {
return first - second;
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
¿Qué devuelve readLevel(0)?
function readLevel(level = 40) { return level; }
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
¿Qué número se muestra y cuál es el valor de result?
function displayTotal(a, b) { console.log(a + b); }
const result = displayTotal(6, 9);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
¿Qué ocurre cuando se llama a getFlag()?
function getFlag() {
if (true) { let flag = "on"; }
return flag;
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
¿Qué valores devuelven las dos llamadas, en orden?
function makeStep() {
let value = 4;
return () => { value += 2; return value; };
}
const step = makeStep();
step();
step();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
¿Cuál es la secuencia completa de valores mostrados?
function deliver(action) { action(7); }
console.log("start");
deliver((n) => console.log(n));
console.log("end");
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 3: Relaciona llamadas, ámbito y funciones de devolución de llamada10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
¿Qué valor se asigna a out?
const describe = (n) => {
const doubled = n * 2;
return doubled + 1;
};
const out = describe(6);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
¿Qué ocurre cuando se ejecuta este script?
const saved = build;
const build = function () { return 9; };
console.log(saved());
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
¿Qué valores tienen items y answer después de este código?
const items = [5];
function change(list) {
list.push(8);
list = [1];
return list;
}
const answer = change(items);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
¿Qué devuelve countLeaves([0, [[], 2], [[3], 4]])?
function countLeaves(list) {
let count = 0;
for (const item of list) {
if (Array.isArray(item)) {
count += countLeaves(item);
} else {
count++;
}
}
return count;
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
¿Qué devuelve readValue()?
function readValue() {
if (true) { var value = 14; }
return value;
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
¿Cuáles son los dos valores mostrados?
let count = 2;
function update() {
let count = 7;
count += 1;
return count;
}
console.log(update());
console.log(count);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
¿Qué valores devuelven las tres últimas llamadas?
function makeCounter(start) {
let n = start;
return () => { n++; return n; };
}
const a = makeCounter(5);
const alias = a;
const b = makeCounter(5);
a();
alias();
b();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
¿Qué devuelve scale(7)?
const makeScale = (factor) => (n) => n * factor;
const scale = makeScale(4);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
¿Qué valores tienen values y first después?
const values = [12, 3, 20];
const first = values.sort((a, b) => a - b)[0];
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
¿Qué devuelven las llamadas read(0) y read(9), en orden?
function once(fn) {
let done = false;
let result;
return (...args) => {
if (!done) {
done = true;
result = fn(...args);
}
return result;
};
}
const read = once((n) => n * 2);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 4: Analiza el comportamiento de funciones en programas pequeños10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
function printLine() { console.log("---"); } y después ejecuta printline();. ¿Qué ocurre?Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
¿Qué devuelve la llamada final?
const climb = function step(n) {
return n === 0 ? 0 : 1 + step(n - 1);
};
const saved = climb;
saved(2);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
¿Qué valores tienen data y result al final?
const data = [2, 5];
function extend(list, item) {
const next = list.concat(item);
list.push(9);
return next;
}
const result = extend(data, 7);
result.push(1);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
¿Qué devuelve la llamada final?
function total(base = 4, ...extras) {
return extras.reduce((sum, n) => sum + n, base);
}
const values = [undefined, 3, 0, 8];
total(...values);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
¿Qué valor produce esta llamada?
function read() {
return;
}
read();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
La entrada n es un entero no negativo. ¿Qué sustitución del retorno recursivo hace que la función cuente hacia atrás hasta 0 correctamente tanto para entradas impares como pares?
function countDown(n) {
if (n === 0) { return 0; }
// recursive return goes here
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
¿Qué devuelve la llamada final?
const mode = "shared";
function readMode() { return mode; }
function run() {
const mode = "local";
return readMode();
}
run();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
¿Qué valores devuelven jobs[0]() y jobs[1]() después del bucle?
const jobs = [];
for (var i = 0; i < 2; i++) {
const offset = i * 10;
jobs.push(() => offset + i);
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
¿Qué resultado produce esta expresión?
["6", "8", "10"].map((text) => parseInt(text, 10));
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
¿Qué valores de out y seen se muestran?
let seen = 0;
const out = [3, 5].forEach((n) => {
seen += n;
return n * 2;
});
console.log(out, seen);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Set 5: Combina cierres y funciones de orden superior10 Preguntas de Práctica
Guarda esta unidad para ver todas las respuestas y practicar cada set.
Pregunta 1 de 10
¿A qué valor se evalúa add("6", 4) * 2?
function add(a, b) { return a + b; }
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 2 de 10
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 3 de 10
¿A qué valor se evalúa la expresión final?
function choose(n) {
if (n < 0) { return 0; }
return n + 2;
}
choose(choose(-4));
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 4 de 10
¿Qué ocurre cuando se llama a build()?
let amount = 6;
function build() {
const read = () => amount;
const result = read();
let amount = 9;
return result;
}
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 5 de 10
¿Qué valores devuelven change(3), read() y otherRead()?
function makePair() {
let n = 1;
return [
(step) => { n += step; return n; },
() => n
];
}
const [change, read] = makePair();
const [, otherRead] = makePair();
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 6 de 10
¿Qué devuelve la expresión final?
function makeLimit(max) {
let used = 0;
return () => {
if (used >= max) { return false; }
used++;
return true;
};
}
const allow = makeLimit(2);
[allow(), allow(), allow()];
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 7 de 10
¿Qué valores tienen result y calls después de este código?
let calls = 0;
function choose(values, test) {
for (const value of values) {
calls++;
if (test(value)) { return value; }
}
return null;
}
const result = choose([3, 8, 10, 5], (n) => n > 6 && n % 2 === 0);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 8 de 10
¿Qué valores tienen items y out después?
const items = [2, 4, 6];
const out = items.map((n, index, array) => {
array[index] = n + 1;
return n * 2;
});
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 9 de 10
¿Qué devuelve doubleSaved(9) después de la llamada anterior mostrada?
function once(fn) {
let done = false;
let saved;
return (...args) => {
if (!done) { done = true; saved = fn(...args); }
return saved;
};
}
const compose = (f, g) => (x) => f(g(x));
const saved = once((n) => n + 3);
const doubleSaved = compose((n) => n * 2, saved);
saved(4);
doubleSaved(9);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
Pregunta 10 de 10
¿Cuál es el array devuelto?
const partial = (fn, ...fixed) => (...later) => fn(...fixed, ...later);
const offset = (base, n) => base + n;
const addFive = partial(offset, 5);
const compose = (f, g) => (x) => f(g(x));
const transform = compose((n) => n * n, addFive);
[1, 3].map(transform);
Mostrar respuestaOcultar respuesta
Guarda esta unidad para ver esta respuesta.
¿Te atascas con una pregunta tuya? Ayuda con Tareas
Términos Clave
A
Acumulador
[5, 10, 15].Aplicación parcial
Argumento
area(4, 2.5), los argumentos son 4 y 2,5, independientemente de los nombres que se hayan dado a los parámetros.Á
Ámbito de bloque
let message declarada en el cuerpo de un if no puede ser leída por una sentencia posterior al final de ese bloque.Ámbito de función
var result a la que se asigna un valor en un bloque if puede devolverse más tarde desde el cuerpo de la función que la contiene.Ámbito global
count de nivel superior de un script puede convertirse en estado compartido cuando varias funciones la leen o actualizan.Ámbito léxico
C
Cadena de ámbitos
amount local antes de considerar una variable del mismo nombre en un ámbito externo.Cadena de métodos
Caso base
n <= 1 detiene las llamadas tanto en 0 como en 1.Caso recursivo
n * factorial(n - 1) espera el factorial más pequeño antes de multiplicarlo por la n de esta llamada.Cierre
count privada después de que haya terminado la fábrica que la creó.Cláusula de guarda
if (b === 0) { return null; } protege la división que aparece después en safeDivide.Comparador
(a, b) => a - b ordena números de forma ascendente mientras sort reorganiza el array de entrada.Composición
f(g(x)), cambiar el orden a g(f(x)) puede cambiar el resultado incluso cuando ambas funciones aceptan números.Cuerpo conciso
(a, b) => a + b devuelve directamente la suma de los dos argumentos proporcionados.Cuerpo de bloque
(n) => { n * 2; } calcula un producto, pero devuelve undefined porque no tiene una sentencia de retorno.Cuerpo de función
printHeading contiene tres sentencias que muestran valores, las tres se ejecutan de nuevo en cada llamada a esa función.D
Declaración de función
greet("Li") puede aparecer antes de la declaración escrita de greet en el mismo ámbito.Desestructuración de arrays
const [low, high] = minMax(values); extrae las dos entradas del array devuelto.E
Efecto secundario
Elevación de declaraciones
var elevada antes de su asignación da undefined; una llamada anticipada a una declaración de función puede ejecutarse.Enlace
Enlace por iteración
let i pueden devolver valores diferentes después de que termine el bucle.Estado compartido
balance privada.Estado privado
used sin acceso directo.Expresión de función
const square = function (n) { return n * n; };, la expresión proporciona el valor almacenado en square.Expresión de función con nombre
const factorial = function fact(n) { /* body */ };, la variable externa es factorial y el nombre interno es fact.Expresión de función invocada inmediatamente (IIFE)
(function () { var hidden = 1; })(); invocan la función recién creada.F
Fábrica de funciones
makeMultiplier(3) devuelve una función que recuerda 3 mientras acepta un número posterior que multiplicar.Función
printHeading("Report") y printHeading("Totals") para reutilizar una misma rutina de encabezado.Función anónima
function (n) { return n * n; } no tiene un nombre de función interno en su sintaxis escrita.Función de devolución de llamada
countIf llama a su función de devolución de llamada de comprobación para cada elemento y cuenta aquellos cuyos resultados son verdaderos en contexto booleano.Función de devolución de llamada de transformación
(n) => n * n convierte las entradas de [1, 2, 3] en sus cuadrados mediante una operación de transformación.Función de devolución de llamada diferida
sayHi a setTimeout permite que las sentencias siguientes se ejecuten antes de que aparezca el saludo programado.Función de devolución de llamada síncrona
Función de orden superior
Función definida en el punto de uso
repeat(3, (i) => console.log(i)) proporciona la acción sin una declaración con nombre separada.Función envolvente
Función envolvente de ejecución única
Función flecha
(n) => n * 2 proporciona una breve función de devolución de llamada de transformación a un método de array como map.Función pura
n * 2 es pura; mostrar el producto introduciría un efecto secundario.L
Literal de objeto
(n) => ({ value: n }) devuelve un objeto en lugar de tratar sus llaves como un bloque de sentencias.Llamada a función
sayHi() ejecuta su cuerpo, mientras que una línea que contiene solo sayHi lee el valor de la función.M
Marco de pila
n porque tienen marcos separados.Modo estricto
"use strict"; hace que una asignación count = 5; sin declaración genere un ReferenceError.Mutación
arr.push(3) modifica el mismo array que quien llama proporcionó como argumento.N
Notación camelCase
calculateTotal describe una acción y separa sus dos palabras sin espacios ni guiones bajos.O
Objeto arguments
arguments[0], pero no puede llamar a arguments.map como método ordinario de array.P
Parámetro
width y height son parámetros en function area(width, height) { return width * height; }.Parámetro de resto
function log(label, ...values) { /* body */ }, el primer argumento queda separado del array de resto.Parámetro predeterminado
level = 50 conserva un 0 explícito, pero proporciona 50 cuando falta el argumento.Pila de llamadas
sumOfSquares espera a square(3), la llamada a square tiene un marco por encima de la función que espera.Predicado
(n) => n > 4 selecciona qué números conserva un filtro sin cambiar por sí mismo sus valores.R
Reasignación
arr = [] cambia a qué apunta el parámetro sin sustituir la referencia al array de quien llama.Recursión
Referencia a función
const alias = square;, llamar a alias(4) invoca la función a la que también se accede mediante square.Retorno anticipado
null para un divisor cero antes de que la ejecución llegue a la sentencia de división.S
Sintaxis de propagación
Math.max(...[3, 7, 2]) pasa tres números en lugar de pasar un array a Math.max.Sombreado de variables
count oculta una count externa dentro de esa función sin cambiar el enlace externo.T
Traza de la pila
V
Valor de retorno
double(5) devuelve 10, la expresión double(5) + 1 puede usar inmediatamente ese valor para producir 11.Valor falso en contexto booleano
level || 50 sustituye 0, mientras que un valor predeterminado de parámetro conserva ese valor proporcionado.Variable capturada
greeting externa de «Hi» a «Hello» cambia el valor que devuelve una llamada posterior a una función que lee greeting.Variable local
total local sin que sus sumas se afecten entre sí.Z
Zona muerta temporal
Sobre esta unidad
Crea tus propios Materiales de Estudio
Convierte tus notas, diapositivas y documentos en Guías de Estudio, Preguntas de Práctica, Flashcards y 6 herramientas más.