En JavaScript, una función no es una «estructura mágica del lenguaje», sino un tipo especial de valor.
La sintaxis que hemos utilizado antes se llama Declaración de Función:
function sayHi() { alert( "Hello" );}
Hay otra sintaxis para crear una función que se llama Expresión de Función.
Se parece a esto:
let sayHi = function() { alert( "Hello" );};
Aquí, la función se crea y se asigna a la variable explícitamente, como cualquier otro valor. No importa cómo se defina la función, es sólo un valor almacenado en la variable sayHi
.
El significado de estos ejemplos de código es el mismo: «crear una función y ponerla en la variable sayHi
«.
Incluso podemos imprimir ese valor utilizando alert
:
function sayHi() { alert( "Hello" );}alert( sayHi ); // shows the function code
Nótese que la última línea no ejecuta la función, porque no hay paréntesis después de sayHi
. Hay lenguajes de programación en los que cualquier mención del nombre de una función provoca su ejecución, pero JavaScript no es así.
En JavaScript, una función es un valor, por lo que podemos tratarla como un valor. El código anterior muestra su representación en forma de cadena, que es el código fuente.
Ciertamente, una función es un valor especial, en el sentido de que podemos llamarla como sayHi()
.
Pero sigue siendo un valor. Así que podemos trabajar con él como con otros tipos de valores.
Podemos copiar una función a otra variable:
function sayHi() { // (1) create alert( "Hello" );}let func = sayHi; // (2) copyfunc(); // Hello // (3) run the copy (it works)!sayHi(); // Hello // this still works too (why wouldn't it)
Aquí está lo que ocurre arriba en detalle:
- La declaración de función
(1)
crea la función y la pone en la variable llamadasayHi
. - La línea
(2)
la copia en la variablefunc
. Tenga en cuenta de nuevo: no hay paréntesis después desayHi
. Si los hubiera, entoncesfunc = sayHi()
escribiría el resultado de la llamadasayHi()
enfunc
, no la funciónsayHi
en sí. - Ahora la función puede ser llamada como
sayHi()
yfunc()
.
Nota que también podríamos haber usado una Expresión de Función para declarar sayHi
, en la primera línea:
let sayHi = function() { alert( "Hello" );};let func = sayHi;// ...
Todo funcionaría igual.
Te preguntarás, por qué la Expresión de Función tiene un punto y coma ;
al final, pero la Declaración de Función no:
function sayHi() { // ...}let sayHi = function() { // ...};
La respuesta es sencilla:
- No hay necesidad de
;
al final de los bloques de código y estructuras sintácticas que los utilizan comoif { ... }
,for { }
,function f { }
etc. - Una expresión de función se utiliza dentro de la declaración:
let sayHi = ...;
, como un valor. No es un bloque de código, sino una asignación. El punto y coma;
se recomienda al final de las sentencias, sea cual sea el valor. Así que el punto y coma aquí no está relacionado con la Expresión de Función en sí, sólo termina la declaración.
Funciones de devolución
Veamos más ejemplos de pasar funciones como valores y usar expresiones de función.
Escribiremos una función ask(question, yes, no)
con tres parámetros:
question
Texto de la pregunta yes
Función a ejecutar si la respuesta es «Sí» no
Función a ejecutar si la respuesta es «No»
La función debe preguntar al question
y, dependiendo de la respuesta del usuario, llamar a yes()
o no()
:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}function showOk() { alert( "You agreed." );}function showCancel() { alert( "You canceled the execution." );}// usage: functions showOk, showCancel are passed as arguments to askask("Do you agree?", showOk, showCancel);
En la práctica, estas funciones son bastante útiles. La principal diferencia entre un ask
de la vida real y el ejemplo anterior es que las funciones de la vida real utilizan formas más complejas de interactuar con el usuario que un simple confirm
. En el navegador, dicha función suele dibujar una ventana de preguntas de aspecto agradable. Pero esa es otra historia.
Los argumentos showOk
y showCancel
de ask
se llaman funciones de devolución de llamada o simplemente callbacks.
La idea es que pasemos una función y esperemos que sea «llamada de vuelta» más tarde si es necesario. En nuestro caso, showOk
se convierte en la llamada de retorno para la respuesta «sí», y showCancel
para la respuesta «no».
Podemos utilizar expresiones de función para escribir la misma función mucho más corta:
function ask(question, yes, no) { if (confirm(question)) yes() else no();}ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); });
Aquí, las funciones se declaran justo dentro de la llamada ask(...)
. No tienen nombre, por lo que se denominan anónimas. Dichas funciones no son accesibles fuera de ask
(porque no se asignan a variables), pero eso es justo lo que queremos aquí.
Este código aparece en nuestros scripts de forma muy natural, está en el espíritu de JavaScript.
Los valores regulares como las cadenas o los números representan los datos.
Una función puede ser percibida como una acción.
Podemos pasarla entre variables y ejecutarla cuando queramos.
Expresión de Función vs Declaración de Función
Formulemos las diferencias clave entre las Declaraciones de Función y las Expresiones.
Primero, la sintaxis: cómo diferenciarlas en el código.
-
Declaración de Función: una función, declarada como una declaración separada, en el flujo de código principal.
// Function Declarationfunction sum(a, b) { return a + b;}
Expresión de Función: una función, creada dentro de una expresión o dentro de otra construcción sintáctica. Aquí, la función se crea a la derecha de la «expresión de asignación»
=
:// Function Expressionlet sum = function(a, b) { return a + b;};
La diferencia más sutil es cuando una función es creada por el motor de JavaScript.
Una Expresión de Función se crea cuando la ejecución llega a ella y es utilizable sólo desde ese momento.
Una vez que el flujo de ejecución pasa a la derecha de la asignación
let sum = function…
– aquí vamos, la función es creada y puede ser utilizada (asignada, llamada, etc. ) a partir de ahora.Las Declaraciones de Función son diferentes.
Una Declaración de Función puede ser llamada antes de ser definida.
Por ejemplo, una Declaración de Función global es visible en todo el script, sin importar donde esté.
Eso se debe a algoritmos internos. Cuando JavaScript se prepara para ejecutar el script, primero busca las Declaraciones de Función globales en él y crea las funciones. Podemos pensar en ello como una «etapa de inicialización».
Y después de que se procesen todas las Declaraciones de Función, se ejecuta el código. Así que tiene acceso a estas funciones.
Por ejemplo, esto funciona:
sayHi("John"); // Hello, Johnfunction sayHi(name) { alert( `Hello, ${name}` );}
La Declaración de Función
sayHi
se crea cuando JavaScript se prepara para iniciar el script y es visible en todas partes en él.…Si fuera una Expresión de Función, entonces no funcionaría:
sayHi("John"); // error!let sayHi = function(name) { // (*) no magic any more alert( `Hello, ${name}` );};
Las Expresiones de Función se crean cuando la ejecución las alcanza. Eso ocurriría sólo en la línea
(*)
. Demasiado tarde.Otra característica especial de las Declaraciones de Función es su ámbito de bloque.
En modo estricto, cuando una Declaración de Función está dentro de un bloque de código, es visible en todas partes dentro de ese bloque. Pero no fuera de él.
Por ejemplo, imaginemos que necesitamos declarar una función
welcome()
en función de la variableage
que obtenemos en tiempo de ejecución. Y pensamos utilizarla algún tiempo después.Si utilizamos la Declaración de Función, no funcionará como se pretende:
let age = prompt("What is your age?", 18);// conditionally declare a functionif (age < 18) { function welcome() { alert("Hello!"); }} else { function welcome() { alert("Greetings!"); }}// ...use it laterwelcome(); // Error: welcome is not defined
Eso es porque una Declaración de Función sólo es visible dentro del bloque de código en el que reside.
Aquí hay otro ejemplo:
let age = 16; // take 16 as an exampleif (age < 18) { welcome(); // \ (runs) // | function welcome() { // | alert("Hello!"); // | Function Declaration is available } // | everywhere in the block where it's declared // | welcome(); // / (runs)} else { function welcome() { alert("Greetings!"); }}// Here we're out of curly braces,// so we can not see Function Declarations made inside of them.welcome(); // Error: welcome is not defined
¿Qué podemos hacer para que
welcome
sea visible fuera deif
?El enfoque correcto sería utilizar una Expresión de Función y asignar
welcome
a la variable que está declarada fuera deif
y tiene la visibilidad adecuada.Este código funciona como se pretende:
let age = prompt("What is your age?", 18);let welcome;if (age < 18) { welcome = function() { alert("Hello!"); };} else { welcome = function() { alert("Greetings!"); };}welcome(); // ok now
O podríamos simplificarlo aún más utilizando un operador de signo de interrogación
?
:let age = prompt("What is your age?", 18);let welcome = (age < 18) ? function() { alert("Hello!"); } : function() { alert("Greetings!"); };welcome(); // ok now
¿Cuándo elegir Declaración de Función frente a Expresión de Función?Como regla general, cuando necesitamos declarar una función, lo primero a considerar es la sintaxis de Declaración de Función. Da más libertad a la hora de organizar nuestro código, ya que podemos llamar a dichas funciones antes de declararlas.
También es mejor para la legibilidad, ya que es más fácil buscar
function f(…) {…}
en el código quelet f = function(…) {…};
. Las Declaraciones de Función son más «vistosas».…Pero si una Declaración de Función no nos conviene por alguna razón, o necesitamos una declaración condicional (acabamos de ver un ejemplo), entonces se debe utilizar la Expresión de Función.
Resumen
- Las funciones son valores. Pueden asignarse, copiarse o declararse en cualquier lugar del código.
- Si la función se declara como una declaración independiente en el flujo de código principal, eso se llama «Declaración de función».
- Si la función se crea como parte de una expresión, se llama «Expresión de función».
- Las Declaraciones de función se procesan antes de que se ejecute el bloque de código. Son visibles en cualquier parte del bloque.
- Las Expresiones de Función se crean cuando el flujo de ejecución llega a ellas.
En la mayoría de los casos en que necesitamos declarar una función, es preferible una Declaración de Función, porque es visible antes de la propia declaración. Eso nos da más flexibilidad en la organización del código, y suele ser más legible.
Así que deberíamos usar una Expresión de Función sólo cuando una Declaración de Función no es adecuada para la tarea. Hemos visto un par de ejemplos de esto en este capítulo, y veremos más en el futuro.