31º agosto 2020

Funciones

Muy a menudo necesitamos realizar una acción similar en muchos lugares del script.

Por ejemplo, debemos mostrar un mensaje atractivo cuando un visitante inicia sesión, cierra sesión y tal vez en otros momentos.

Las funciones son los principales “bloques de construcción” del programa. Permiten que el código se llame 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.

Declaración de funciones

Para crear una función podemos usar una declaración de función.

Se Parece a esto:

function showMessage() {
  alert( '¡Hola a todos!' );
}

La palabra clave function va primero, luego una lista de parámetros entre paréntesis (vacía en el ejemplo anterior) y finalmente el código de la función, también llamado “el cuerpo de la función” , entre llaves.

Nuestra nueva función puede ser llamada por su nombre: showMessage().

Por ejemplo:

function showMessage() {
  alert( '¡Hola a todos!' );
}

showMessage();
showMessage();

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, es suficiente modificar el código en un lugar: la función que lo genera.

Variables Locales

Una variable declarada dentro de una función solo es visible dentro de esa función.

Por ejemplo:

function showMessage() {
  let message = "Hola, ¡Soy JavaScript!"; // variable local

  alert( message );
}

showMessage(); // Hola, ¡Soy JavaScript!

alert( message ); // <-- Error! La variable es local para esta función

Variables Externas

Una función también puede acceder a una variable externa, por ejemplo:

let userName = 'Juan';

function showMessage() {
  let message = 'Hola, ' + userName;
  alert(message);
}

showMessage(); // Hola, Juan

La función tiene acceso completo a la variable externa. Puede modificarlo también.

Por ejemplo:

let userName = 'Juan';

function showMessage() {
  userName = "Bob"; // (1) Cambió la variable externa

  let message = 'Hola, ' + userName;
  alert(message);
}

alert( userName ); // Juan antes de llamar la función

showMessage();

alert( userName ); // Bob, el valor fué modificado por la función

La variable externa solo se usa si no hay una local. Por lo tanto, puede ocurrir una modificación ocasional si olvidamos colocar el let.

Si una variable con el mismo nombre se declara dentro de la función, entonces hace shadowing a la externa. Por ejemplo, en el siguiente código, la función usa el userName local. El exterior se ignora:

let userName = 'Juan';

function showMessage() {
  let userName = "Bob"; // declara variable local

  let message = 'Hello, ' + userName; // Bob
  alert(message);
}

// la función crea y utiliza su propia variable local userName
showMessage();

alert( userName ); // John, se mantiene, la función no accedió a la variable externa
Variables globales

Variables declaradas fuera de cualquier función, como la variable externa userName en el código anterior, se llaman global.

Las variables globales son visibles desde cualquier función (a menos que se haga shadowing en el contexto local).

Por lo general, una función declara todas las variables específicas de su tarea. Las variables globales solo almacenan datos a nivel de proyecto, y es importante que estas variables sean accesibles desde cualquier lugar. El código moderno tiene pocos o ninguna variable global. La mayoría de las variables residen en sus funciones.

Parámetros

Podemos pasar datos arbitrarios a funciones usando parámetros (también llamados argumentos de función).

En el siguiente ejemplo, la función tiene dos parámetros: from y text.

function showMessage(from, text) { // argumentos: from, text
  alert(from + ': ' + text);
}

showMessage('Ann', '¡Hola!'); // Ann: ¡Hola! (*)
showMessage('Ann', "¿Cómo estás?"); // Ann: ¿Cómo estás? (**)

Cuando la función se llama (*) y (**), los valores dados se copian en variables locales from y text. Y la función las utiliza.

Aquí hay un ejemplo más: tenemos una variable from y 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:

function showMessage(from, text) {

  from = '*' + from + '*'; // hace que "from" se vea mejor

  alert( from + ': ' + text );
}

let from = "Ann";

showMessage(from, "Hola"); // *Ann*: Hola

// el valor de "from" es el mismo, la función modificó una copia local
alert( from ); // Ann

Valores predeterminados

Si no se proporciona un parámetro, su valor se convierte en undefined.

Por ejemplo, la función mencionada anteriormente showMessage(from, text) se puede llamar con un solo argumento:

showMessage("Ann");

Eso no es un error. la llamada saldría "Ann: undefined". No existe el parámetro text, entonces asumimos que text === undefined.

Si quisiéramos usar un text “predeterminado” en este caso, lo podemos identificar después del =:

function showMessage(from, text = "sin texto") {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: sin texto

Ahora, si no existe el parámetro text, obtendrá el valor "sin texto"

Aquí "sin texto" es un string, pero puede ser una expresión más compleja, la cual solo es evaluada y asignada si el parámetro falta. Entonces, esto es posible:

function showMessage(from, text = anotherFunction()) {
  // anotherFunction() solo se ejecuta si el parámetro texto no fué asignado
  // y su resultado se convierte en el valor de texto
}
Evaluación de parámetros predeterminado

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, se llama a anotherFunction() cada vez que se llama a showMessage() sin el parámetro text. Esto contrasta con otros lenguajes como Python, donde los parámetros predeterminados se evalúan solo una vez durante la interpretación inicial.

Parámetros predeterminados de estilo antiguo

Las ediciones anteriores de JavaScript no admitían parámetros predeterminados. Por lo tanto, hay formas alternativas de admitirlos, que se pueden encontrar principalmente en los scripts antiguos.

Por ejemplo, una comprobación explícita de ser undefined:

function showMessage(from, text) {
  if (text === undefined) {
    text = 'sin texto';
  }

  alert( from + ": " + text );
}

…O el operador ||

function showMessage(from, text) {
  // si text es falso entonces text se convierte en el valor "predeterminado"
  text = text || 'sin texto';
  ...
}

Devolviendo un valor

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:

function sum(a, b) {
  return a + b;
}

let result = sum(1, 2);
alert( result ); // 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 muchos casos de return en una sola función. Por ejemplo:

function checkAge(age) {
  if (age > 18) {
    return true;
  } else {
    return confirm('¿Tienes permiso de tus padres?');
  }
}

let age = prompt('¿Qué edad tienes?', 18);

if ( checkAge(age) ) {
  alert( 'Acceso otorgado' );
} else {
  alert( 'Acceso denegado' );
}

Es posible utilizar return sin ningún valor. Eso hace que la función salga o termine inmediatamente.

Por ejemplo:

function showMovie(age) {
  if ( !checkAge(age) ) {
    return;
  }

  alert( "Mostrándote la película" ); // (*)
  // ...
}

En el código de arriba, si checkAge(age) devuelve false, entonces showMovie no mostrará la alert.

Una función con un return vacio o sin el devuelve undefined

Si una función no devuelve un valor, es lo mismo que si devuelve undefined:

function doNothing() { /* empty */ }

alert( doNothing() === undefined ); // true

Un return vacío también es lo mismo que return undefined:

function doNothing() {
  return;
}

alert( doNothing() === undefined ); // true
Nunca agregue una nueva línea entre return y el valor

Para una expresión larga de return, puede ser tentador ponerlo en una línea separada, como esta:

return
 (una + expresion + o + cualquier + cosa * f(a) + f(b))

Eso no funciona, porque JavaScript asume un punto y coma después del return. Eso funcionará igual que:

return;
 (una + expresion + o + cualquier + cosa * f(a) + f(b))

Entonces, efectivamente se convierte en un return vacío. Deberíamos poner el valor en la misma línea.

Nomenclatura de funciones

Las funciones son acciones. Entonces su nombre suele ser un verbo. Debe ser breve, lo más preciso posible y describir lo que hace la función, para que alguien que lea el código obtenga una indicación de lo que hace la función.

Es una práctica generalizada 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, funciones que comienzan con "show" usualmente muestran algo.

Funciones que comienza con…

  • "get…" – devuelven un valor,
  • "calc…" – calculan algo,
  • "create…" – crean algo,
  • "check…" – revisan algo y devuelven un boolean, etc.

Ejemplos de este tipo de 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 usualmente lo devuelve)
checkPermission(..) // revisa permisos, y devuelve true/false

Con los prefijos en su lugar, 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, no más.

Dos acciones independientes por lo general merecen dos funciones, incluso si generalmente se convocan juntas (en ese caso, podemos hacer una tercera función que llame a esas dos).

Algunos ejemplos de cómo se rompen estas reglas:

  • getAge – sería malo si mostrara una alert con la edad (solo debería obtener).
  • createForm – sería malo si modifica el documento y lo agrega (solo debe crearlo y devolverlo).
  • checkPermission – sería malo si muestra el mensaje acceso otorgsado/denegado(solo debe realizar la verificación y devolver el resultado).

Estos ejemplos asumen significados comunes de prefijos. Lo que significan para ti está determinado por ti y tu equipo. Tal vez es bastante normal que su código se comporte de manera diferente. Pero 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 debe compartir el conocimiento.

Nombres de funciones ultracortos

Las funciones que se utilizan muy a menudo algunas veces tienen nombres ultracortos.

Por ejemplo, el framework jQuery define una función con $. La librería LoDash tiene como nombre de funccion principal _.

Estas son excepciones. En general, los nombres de las funciones deben ser concisos y descriptivos.

Funciones == Comentarios

Las funciones deben ser cortas y hacer exactamente una cosa. Si esa cosa 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 solo es más fácil de probar y depurar, – ¡su existencia es un gran comentario!

Por ejemplo, comparemos las dos funciones showPrimes(n) siguientes. Cada una devuelve números primos hasta n.

La primera variante usa una etiqueta:

function showPrimes(n) {
  nextPrime: for (let i = 2; i < n; i++) {

    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert( i ); // un número primo
  }
}

La segunda variante usa una función adicional isPrime(n) para probar la primalidad:

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);  // a prime
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if ( n % i == 0) return false;
  }
  return true;
}

La segunda variante es más fácil de entender, ¿no? En lugar del código, vemos un nombre de la acción. (isPrime). A veces las personas se refieren a dicho código como autodescriptivo.

Por lo tanto, las funciones se pueden crear incluso si no tenemos la intención de reutilizarlas. Estructuran el código y lo hacen legible.

Resumen

Una declaración de función se ve así:

function name(parámetros, delimitados, por, coma) {
  /* code */
}
  • 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 funciona solo de 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 lo hace, 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 las variables externas como un efecto secundario.

Nomenclatura de funciones:

  • Un nombre debe describir claramente lo que hace la función. Cuando vemos una llamada a la función en el código, un buen nombre nos da al instante una comprensión de lo que hace y devuelve.
  • Una función es una acción, por lo que los nombres de las funciones suelen ser verbales.
  • Existen muchos prefijos de funciones bien conocidos como create…, show…, get…, check… y así. Úsalos para insinuar lo que hace una función.

Las funciones son los principales bloques de construcción de los scripts. Ahora hemos cubierto los conceptos básicos, por lo que en realidad podemos comenzar a crearlos y usarlos. Pero ese es solo el comienzo del camino. Volveremos a ellos muchas veces, profundizando en sus funciones avanzadas.

Tareas

importancia: 4

La siguiente función devuelve true si el parámetro age es mayor a 18.

De lo contrario, solicita una confirmación y devuelve su resultado:

function checkAge(age) {
  if (age > 18) {
    return true;
  } else {
    // ...
    return confirm('¿Tus padres te permitieron?');
  }
}

¿Funcionará la función de manera diferente si se borra else?

function checkAge(age) {
  if (age > 18) {
    return true;
  }
  // ...
  return confirm('¿Tus padres te permitieron?');
}

¿Hay alguna diferencia en el comportamiento de estas dos variantes?

Ninguna diferencia.

importancia: 4

La siguiente función devuelve true si el parametro age es mayor que 18.

De lo contrario, solicita una confirmación y devuelve su resultado.

function checkAge(age) {
  if (age > 18) {
    return true;
  } else {
    return confirm('¿Tienes permiso de tus padres?');
  }
}

Reescríbalo, para realizar lo mismo, pero sin if, en una sola linea.

Haz dos variantes de checkAge:

  1. Usando un operador de signo de interrogación ?
  2. Usando OR ||

Using a question mark operator '?':

function checkAge(age) {
  return (age > 18) ? true : confirm('¿Tús padres te lo permitieron?');
}

Usando Ó || (la variante más corta):

function checkAge(age) {
  return (age > 18) || confirm('¿Tús padres te lo permitieron?');
}

Tenga en cuenta que los paréntesis alrededor de age > 18 no son requeridos aca. Existen para una mejor legibilidad.

importancia: 1

Escriba una función min(a,b) la cual devuelva el menor de dos números a y b.

Por ejemplo:

min(2, 5) == 2
min(3, -1) == -1
min(1, 1) == 1

A solution using if:

function min(a, b) {
  if (a < b) {
    return a;
  } else {
    return b;
  }
}

Una solución con un operador de signo de interrogación '?':

function min(a, b) {
  return a < b ? a : b;
}

P.D: En el caso de una igualdad a == b No importa qué devuelva.

importancia: 4

Escriba la función pow(x,n) que devuelva x como potencia de n. O, en otras palabras, multiplique x por si mismo n veces y devuelva el resultado.

pow(3, 2) = 3 * 3 = 9
pow(3, 3) = 3 * 3 * 3 = 27
pow(1, 100) = 1 * 1 * ...* 1 = 1

Cree una página web que solicite x y n, y luego muestra el resultado de pow(x,n).

Ejecutar el demo

PD: En esta tarea, la función solo debe admitir valores naturales de n: enteros hacia por encima de 1.

function pow(x, n) {
  let result = x;

  for (let i = 1; i < n; i++) {
    result *= x;
  }

  return result;
}

let x = prompt("x?", '');
let n = prompt("n?", '');

if (n < 1) {
  alert(`Potencia ${n} no soportada,
    use un entero mayor a 0`);
} else {
  alert( pow(x, n) );
}
Mapa del Tutorial

Comentarios

lea esto antes de comentar…
  • Si tiene sugerencias sobre qué mejorar, por favor enviar una propuesta de GitHub o una solicitud de extracción en lugar de comentar.
  • Si no puede entender algo en el artículo, por favor explique.
  • Para insertar algunas palabras de código, use la etiqueta <code>, para varias líneas – envolverlas en la etiqueta <pre>, para más de 10 líneas – utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen…)