Muy a menudo necesitamos realizar una acción similar en muchos lugares del guión.
Por ejemplo, necesitamos mostrar un mensaje atractivo cuando un visitante inicia sesión, cierra sesión y tal vez en otro lugar.
Las funciones son los principales "bloques de construcción" del programa. Permiten llamar al código muchas veces sin repetición.
Ya hemos visto ejemplos de funciones integradas, como alert(message)
, prompt(message, default)
y confirm(question)
. Pero también podemos crear funciones propias.
Para crear una función podemos usar una declaración de función .
Se parece a esto:
función mostrarMensaje() { alerta('¡Hola a todos!'); }
La palabra clave function
va primero, luego va el nombre de la función , luego una lista de parámetros entre paréntesis (separados por comas, vacíos en el ejemplo anterior, veremos ejemplos más adelante) y finalmente el código de la función, también llamado “el cuerpo de la función”, entre llaves.
nombre de la función (parámetro1, parámetro2, ... parámetroN) { // cuerpo }
Nuestra nueva función puede llamarse por su nombre: showMessage()
.
Por ejemplo:
función mostrarMensaje() { alerta('¡Hola a todos!'); } mostrarMensaje(); mostrarMensaje();
La llamada showMessage()
ejecuta el código de la función. Aquí veremos el mensaje dos veces.
Este ejemplo demuestra claramente uno de los propósitos principales de las funciones: evitar la duplicación de código.
Si alguna vez necesitamos cambiar el mensaje o la forma en que se muestra, basta con modificar el código en un lugar: la función que lo genera.
Una variable declarada dentro de una función sólo es visible dentro de esa función.
Por ejemplo:
función mostrarMensaje() { let mensaje = "¡Hola, soy JavaScript!"; // variable local alerta (mensaje); } mostrarMensaje(); // ¡Hola, soy JavaScript! alerta (mensaje); // <-- ¡Error! La variable es local a la función.
Una función también puede acceder a una variable externa, por ejemplo:
let nombre de usuario = 'Juan'; función mostrarMensaje() { let mensaje = 'Hola,' + nombre de usuario; alerta(mensaje); } mostrarMensaje(); // Hola, Juan
La función tiene acceso completo a la variable externa. También puede modificarlo.
Por ejemplo:
let nombre de usuario = 'Juan'; función mostrarMensaje() { nombre de usuario = "Bob"; // (1) cambió la variable externa let mensaje = 'Hola,' + nombre de usuario; alerta(mensaje); } alerta (nombre de usuario); // John antes de la llamada a la función mostrarMensaje(); alerta (nombre de usuario); // Bob, el valor fue modificado por la función
La variable externa solo se usa si no hay una local.
Si se declara una variable con el mismo nombre dentro de la función, entonces ensombrece la exterior. Por ejemplo, en el código siguiente, la función utiliza el userName
local. El exterior se ignora:
let nombre de usuario = 'Juan'; función mostrarMensaje() { let nombre de usuario = "Bob"; // declarar una variable local let mensaje = 'Hola,' + nombre de usuario; // Bob alerta(mensaje); } // la función creará y utilizará su propio nombre de usuario mostrarMensaje(); alerta (nombre de usuario); // John, sin cambios, la función no accedió a la variable externa
variables globales
Las variables declaradas fuera de cualquier función, como el userName
externo en el código anterior, se denominan globales .
Las variables globales son visibles desde cualquier función (a menos que estén ocultas por las locales).
Es una buena práctica minimizar el uso de variables globales. El código moderno tiene pocos o ningún global. La mayoría de las variables residen en sus funciones. Sin embargo, a veces pueden resultar útiles para almacenar datos a nivel de proyecto.
Podemos pasar datos arbitrarios a funciones usando parámetros.
En el siguiente ejemplo, la función tiene dos parámetros: from
y text
.
función mostrarMensaje(de, texto) { // parámetros: de, texto alerta(de + ': ' + texto); } showMessage('Ann', '¡Hola!'); // Ana: ¡Hola! (*) showMessage('Ann', "¿Qué pasa?"); // Ann: ¿Qué pasa? (**)
Cuando se llama a la función en las líneas (*)
y (**)
, los valores dados se copian a las variables locales from
y text
. Entonces la función los usa.
Aquí hay un ejemplo más: from
una variable y se la pasamos a la función. Tenga en cuenta: la función cambia from
, pero el cambio no se ve afuera, porque una función siempre obtiene una copia del valor:
función mostrarMensaje(de, texto) { desde = '*' + desde + '*'; // hacer que "desde" se vea mejor alerta (de + ': ' + texto); } dejar de = "Ann"; mostrarMensaje(de, "Hola"); // *Ann*: Hola // el valor de "from" es el mismo, la función modificó una copia local alerta (de); // Ana
Cuando un valor se pasa como parámetro de función, también se denomina argumento .
En otras palabras, para aclarar estos términos:
Un parámetro es la variable que figura entre paréntesis en la declaración de función (es un término de tiempo de declaración).
Un argumento es el valor que se pasa a la función cuando se llama (es un término de tiempo de llamada).
Declaramos funciones enumerando sus parámetros y luego las llamamos pasando argumentos.
En el ejemplo anterior, se podría decir: “la función showMessage
se declara con dos parámetros y luego se llama con dos argumentos: from
y "Hello"
”.
Si se llama a una función, pero no se proporciona un argumento, entonces el valor correspondiente pasa a ser undefined
.
Por ejemplo, la función antes mencionada showMessage(from, text)
se puede llamar con un solo argumento:
mostrarMensaje("Ann");
Eso no es un error. Tal llamada generaría "*Ann*: undefined"
. Como no se pasa el valor del text
, queda undefined
.
Podemos especificar el valor llamado "predeterminado" (para usar si se omite) para un parámetro en la declaración de función, usando =
:
función mostrarMensaje(de, texto = "no se proporciona texto") { alerta( de + ": " + texto ); } mostrarMensaje("Ann"); // Ann: no se proporciona texto
Ahora, si no se pasa el parámetro text
, obtendrá el valor "no text given"
.
El valor predeterminado también salta si el parámetro existe, pero es estrictamente igual a undefined
, así:
showMessage("Ann", indefinido); // Ann: no se proporciona texto
Aquí "no text given"
es una cadena, pero puede ser una expresión más compleja, que solo se evalúa y asigna si falta el parámetro. Entonces esto también es posible:
función mostrarMensaje(de, texto = otraFunción()) { // otraFunción() sólo se ejecuta si no se proporciona ningún texto // su resultado se convierte en el valor del texto }
Evaluación de parámetros predeterminados
En JavaScript, se evalúa un parámetro predeterminado cada vez que se llama a la función sin el parámetro respectivo.
En el ejemplo anterior, no se llama anotherFunction()
en absoluto, si se proporciona el parámetro text
.
Por otro lado, se llama de forma independiente cada vez que falta text
.
Parámetros predeterminados en el código JavaScript antiguo
Hace varios años, JavaScript no admitía la sintaxis de los parámetros predeterminados. Entonces la gente usaba otras formas de especificarlos.
Hoy en día podemos encontrarlos en guiones antiguos.
Por ejemplo, una verificación explícita de undefined
:
función mostrarMensaje(de, texto) { si (texto === indefinido) { texto = 'no se proporciona texto'; } alerta( de + ": " + texto ); }
…O usando el ||
operador:
función mostrarMensaje(de, texto) { // Si el valor del texto es falso, asigna el valor predeterminado // esto supone que texto == "" es lo mismo que ningún texto texto = texto || 'no se proporciona texto'; ... }
A veces tiene sentido asignar valores predeterminados para los parámetros en una etapa posterior después de la declaración de la función.
Podemos verificar si el parámetro se pasa durante la ejecución de la función, comparándolo con undefined
:
función mostrarMensaje(texto) { //... if (texto === indefinido) { // si falta el parámetro texto = 'mensaje vacío'; } alerta(texto); } mostrarMensaje(); // mensaje vacío
…O podríamos usar el ||
operador:
función mostrarMensaje(texto) { // si el texto no está definido o es falso, configúrelo como 'vacío' texto = texto || 'vacío'; ... }
Los motores JavaScript modernos admiten el operador coalescente nulo ??
, es mejor cuando la mayoría de los valores falsos, como 0
, deben considerarse "normales":
función mostrarCount(cuenta) { // si el recuento no está definido o es nulo, muestra "desconocido" alerta(recuento ?? "desconocido"); } mostrarContar(0); // 0 mostrarCount(nulo); // desconocido mostrarContar(); // desconocido
Una función puede devolver un valor al código de llamada como resultado.
El ejemplo más simple sería una función que suma dos valores:
función suma(a, b) { devolver a + b; } deja resultado = suma(1, 2); alerta (resultado); // 3
La directiva return
puede estar en cualquier lugar de la función. Cuando la ejecución lo alcanza, la función se detiene y el valor se devuelve al código de llamada (asignado al result
anterior).
Puede haber muchas apariciones de return
en una sola función. Por ejemplo:
función verificarEdad(edad) { si (edad >= 18) { devolver verdadero; } demás { return confirm('¿Tienes permiso de tus padres?'); } } let age = Prompt('¿Cuántos años tienes?', 18); si (verificarEdad(edad)) { alerta('Acceso concedido'); } demás { alerta('Acceso denegado'); }
Es posible utilizar return
sin valor. Eso hace que la función salga inmediatamente.
Por ejemplo:
función mostrarpelícula(edad) { if (!checkAge(edad)) { devolver; } alerta("Mostrándote la película"); // (*) //... }
En el código anterior, si checkAge(age)
devuelve false
, entonces showMovie
no procederá a la alert
.
Una función con return
vacío o sin él devuelve undefined
Si una función no devuelve un valor, es lo mismo que si devuelve undefined
:
función no hacer nada() { /* vacío */ } alerta (no hacer nada () === indefinido); // verdadero
Un return
vacío también es lo mismo que return undefined
:
función no hacer nada() { devolver; } alerta (no hacer nada () === indefinido); // verdadero
Nunca agregue una nueva línea entre return
y el valor
Para una expresión return
larga, puede resultar tentador ponerla en una línea separada, como esta:
devolver (algunos + largos + expresión + o + lo que sea * f(a) + f(b))
Eso no funciona, porque JavaScript asume un punto y coma después de return
. Eso funcionará igual que:
devolver; (algunos + largos + expresión + o + lo que sea * f(a) + f(b))
Por lo tanto, efectivamente se convierte en un retorno vacío.
Si queremos que la expresión devuelta abarque varias líneas, debemos comenzar en la misma línea que return
. O al menos coloque el paréntesis inicial de la siguiente manera:
devolver ( alguna + larga + expresión + o + lo que sea * f(a) + f(b) )
Y funcionará tal como esperamos.
Las funciones son acciones. Por eso su nombre suele ser un verbo. Debe ser breve, lo más preciso posible y describir lo que hace la función, de modo que alguien que lea el código obtenga una indicación de lo que hace la función.
Es una práctica muy extendida comenzar una función con un prefijo verbal que describe vagamente la acción. Debe haber un acuerdo dentro del equipo sobre el significado de los prefijos.
Por ejemplo, las funciones que comienzan con "show"
suelen mostrar algo.
Función que comienza con…
"get…"
– devuelve un valor,
"calc…"
– calcular algo,
"create…"
– crear algo,
"check…"
– comprueba algo y devuelve un valor booleano, etc.
Ejemplos de tales nombres:
showMessage(..) // muestra un mensaje getAge(..) // devuelve la edad (la obtiene de alguna manera) calcSum(..) // calcula una suma y devuelve el resultado createForm(..) // crea un formulario (y normalmente lo devuelve) checkPermission(..) // comprueba un permiso, devuelve verdadero/falso
Con los prefijos implementados, un vistazo al nombre de una función permite comprender qué tipo de trabajo realiza y qué tipo de valor devuelve.
Una función – una acción
Una función debe hacer exactamente lo que sugiere su nombre, nada más.
Dos acciones independientes normalmente merecen dos funciones, incluso si normalmente se llaman juntas (en ese caso podemos hacer una tercera función que llame a esas dos).
Algunos ejemplos de incumplimiento de esta regla:
getAge
: sería malo si mostrara una alert
con la edad (solo debería aparecer).
createForm
– sería malo si modificara el documento, agregándole un formulario (solo debería crearlo y regresar).
checkPermission
: sería malo si mostrara el mensaje access granted/denied
(solo debería realizar la comprobación y devolver el resultado).
Estos ejemplos asumen significados comunes de prefijos. Usted y su equipo son libres de acordar otros significados, pero normalmente no son muy diferentes. En cualquier caso, debe tener una comprensión firme de lo que significa un prefijo, lo que una función con prefijo puede y no puede hacer. Todas las funciones con el mismo prefijo deben obedecer las reglas. Y el equipo debería compartir el conocimiento.
Nombres de funciones ultracortas
Las funciones que se utilizan con mucha frecuencia a veces tienen nombres ultracortos.
Por ejemplo, el marco jQuery define una función con $
. La biblioteca Lodash tiene su función principal llamada _
.
Éstas son excepciones. Generalmente los nombres de las funciones deben ser concisos y descriptivos.
Las funciones deben ser breves y hacer exactamente una cosa. Si eso es grande, tal vez valga la pena dividir la función en algunas funciones más pequeñas. A veces, seguir esta regla puede no ser tan fácil, pero definitivamente es algo bueno.
Una función separada no sólo es más fácil de probar y depurar: ¡su mera existencia es un gran comentario!
Por ejemplo, compare las dos funciones showPrimes(n)
a continuación. Cada uno genera números primos hasta n
.
La primera variante utiliza una etiqueta:
función mostrarPrimes(n) { nextPrime: for (sea i = 2; i < n; i++) { para (sea j = 2; j < i; j++) { si (i % j == 0) continúa con nextPrime; } alerta (yo); // un primo } }
La segunda variante utiliza una función adicional isPrime(n)
para probar la primalidad:
función mostrarPrimes(n) { para (sea i = 2; i < n; i++) { si (!isPrime(i)) continúa; alerta(i); // un primo } } la función esPrima(n) { para (sea i = 2; i < n; i++) { si (n % i == 0) devuelve falso; } devolver verdadero; }
La segunda variante es más fácil de entender, ¿no? En lugar del código, vemos el nombre de la acción ( isPrime
). A veces la gente se refiere a este tipo de código como autodescriptivo .
Por lo tanto, se pueden crear funciones incluso si no pretendemos reutilizarlas. Estructuran el código y lo hacen legible.
Una declaración de función se ve así:
nombre de la función (parámetros, delimitados por, coma) { /* código */ }
Los valores pasados a una función como parámetros se copian a sus variables locales.
Una función puede acceder a variables externas. Pero esto sólo funciona desde adentro hacia afuera. El código fuera de la función no ve sus variables locales.
Una función puede devolver un valor. Si no es así, entonces su resultado es undefined
.
Para que el código sea limpio y fácil de entender, se recomienda utilizar principalmente variables y parámetros locales en la función, no variables externas.
Siempre es más fácil entender una función que obtiene parámetros, trabaja con ellos y devuelve un resultado que una función que no obtiene parámetros, pero modifica variables externas como efecto secundario.
Denominación de funciones:
Un nombre debe describir claramente lo que hace la función. Cuando vemos una llamada de función en el código, un buen nombre nos permite comprender instantáneamente lo que hace y lo que devuelve.
Una función es una acción, por lo que los nombres de las funciones suelen ser verbales.
Existen muchos prefijos de funciones conocidas como create…
, show…
, get…
, check…
y así sucesivamente. Úselos para dar pistas de lo que hace una función.
Las funciones son los principales componentes básicos de los scripts. Ahora hemos cubierto los conceptos básicos, por lo que podemos comenzar a crearlos y usarlos. Pero ese es sólo el comienzo del camino. Volveremos sobre ellos muchas veces, profundizando más en sus funciones avanzadas.
importancia: 4
La siguiente función devuelve true
si el parámetro age
es mayor que 18
.
De lo contrario, solicita una confirmación y devuelve su resultado:
función verificarEdad(edad) { si (edad > 18) { devolver verdadero; } demás { //... return confirm('¿Te permitieron tus padres?'); } }
¿La función funcionará de manera diferente si se elimina else
?
función verificarEdad(edad) { si (edad > 18) { devolver verdadero; } //... return confirm('¿Te permitieron tus padres?'); }
¿Existe alguna diferencia en el comportamiento de estas dos variantes?
¡No hay diferencia!
En ambos casos, return confirm('Did parents allow you?')
se ejecuta exactamente cuando la condición if
es falsa.
importancia: 4
La siguiente función devuelve true
si el parámetro age
es mayor que 18
.
De lo contrario, solicita una confirmación y devuelve su resultado.
función verificarEdad(edad) { si (edad > 18) { devolver verdadero; } demás { return confirm('¿Te permitieron tus padres?'); } }
Vuelve a escribirlo, para realizar lo mismo, pero sin if
, en una sola línea.
Haz dos variantes de checkAge
:
¿Usando un operador de signo de interrogación ?
Usando O ||
Usando un operador de signo de interrogación '?'
:
función verificarEdad(edad) { regreso (edad > 18)? verdadero: confirm('¿Te lo permitieron tus padres?'); }
Usando O ||
(la variante más corta):
función verificarEdad(edad) { regreso (edad > 18) || confirm('¿Te permitieron tus padres?'); }
Tenga en cuenta que aquí no se requieren los paréntesis para age > 18
. Existen para una mejor legibilidad.
importancia: 1
Escribe una función min(a,b)
que devuelva el menor de dos números a
y b
.
Por ejemplo:
mín(2, 5) == 2 mín(3, -1) == -1 mín(1, 1) == 1
Una solución usando if
:
función mín(a, b) { si (a <b) { devolver un; } demás { volver b; } }
Una solución con un operador de signo de interrogación '?'
:
función mín(a, b) { devolver a < b ? a: b; }
PD: En el caso de una igualdad a == b
no importa qué devolver.
importancia: 4
Escribe una función pow(x,n)
que devuelva x
en potencia n
. O, en otras palabras, multiplica x
por sí mismo n
veces y devuelve el resultado.
poder(3, 2) = 3 * 3 = 9 poder(3, 3) = 3 * 3 * 3 = 27 poder(1, 100) = 1 * 1 * ...* 1 = 1
Cree una página web que solicite x
y n
y luego muestre el resultado de pow(x,n)
.
Ejecute la demostración
PD: En esta tarea, la función debe admitir solo valores naturales de n
: enteros a partir de 1
.
función poder(x, n) { dejar resultado = x; para (sea i = 1; i < n; i++) { resultado *= x; } resultado de devolución; } let x = mensaje("x?", ''); let n = mensaje("n?", ''); si (n < 1) { alert(`La potencia ${n} no es compatible, use un entero positivo`); } demás { alerta( pow(x, n) ); }