034.3 Lección 2
Certificación: |
Conceptos básicos de desarrollo web |
---|---|
Versión: |
1.0 |
Tema: |
034 Programación JavaScript |
Objetivo: |
034.3 Funciones y estructuras de control en JavaScript |
Lección: |
2 de 2 |
Introducción
Además del conjunto estándar de funciones integradas que proporciona el lenguaje JavaScript, los desarrolladores pueden escribir sus propias funciones personalizadas para asignar una entrada a una salida adecuada a las necesidades de la aplicación. Las funciones personalizadas son básicamente un conjunto de declaraciones encapsuladas para usarse en otros lugares como parte de una expresión.
El uso de funciones es una buena forma de evitar escribir código duplicado, ya que se pueden llamar desde diferentes ubicaciones a lo largo del programa. Además, agrupar declaraciones en funciones facilita la vinculación de acciones personalizadas a eventos, que es un aspecto central de la programación de JavaScript.
Definición de una función
A medida que un programa crece, se vuelve más difícil organizar lo que hace sin utilizar funciones. Cada función tiene su propio alcance de variable privada, por lo que las variables definidas dentro de una función estarán disponibles solo dentro de esa misma función. Por lo tanto, no se mezclarán con variables de otras funciones. Las variables globales todavía son accesibles desde dentro de las funciones, pero la forma preferible de enviar valores de entrada a una función es a través de parámetros de función. Como ejemplo, vamos a construir sobre el validador de números primos de la lección anterior:
// A naive prime number tester
// The number we want to evaluate
let candidate = 231;
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Display the result in the console window
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
Si más adelante en el código necesita verificar si un número es primo, sería necesario repetir el código que ya ha sido escrito. No se recomienda esta práctica, ya que cualquier corrección o mejora del código original deberá replicarse manualmente en todos los lugares donde se haya copiado el código. Además, la repetición de código supone una carga para el navegador y la red, lo que posiblemente ralentice la visualización de la página web. En lugar de hacer esto, mueva las declaraciones apropiadas a una función:
// A naive prime number tester function
function test_prime(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
La declaración de la función comienza con la palabra reservada function
, seguida del nombre de la función y sus parámetros. El nombre de la función debe seguir las mismas reglas que los nombres de las variables. Los parámetros de la función, también conocidos como los argumentos, están separados por comas y entre paréntesis.
Tip
|
No es obligatorio especificar los argumentos en la declaración de la función. Los argumentos pasados a una función se pueden recuperar de un objeto similar a un arreglo dentro de esa función. El índice de los argumentos comienza en 0, por lo que el primer argumento es |
En el ejemplo, la función test_prime
tiene un solo argumento: el argumento candidate
, que es el número primo candidato a ser probado. Los argumentos de función actúan como variables, pero sus valores son asignados por la declaración que llama a la función. Por ejemplo, la instrucción test_prime (231)
llamará a la función test_prime
y asignará el valor 231 al argumento candidate
, que luego estará disponible dentro del cuerpo de la función como una variable ordinaria.
Si la declaración de llamada usa variables simples para los parámetros de la función, sus valores se copiarán a los argumentos de la función. Este procedimiento — copiar los valores de los parámetros usados en la declaración de llamada a los parámetros usados dentro de la función — se llama pasar argumentos por valor. Cualquier modificación realizada al argumento por la función no afecta la variable original utilizada en la declaración de llamada. Sin embargo, si la declaración de llamada usa objetos complejos como argumentos (es decir, un objeto con propiedades y métodos adjuntos) para los parámetros de la función, serán pasados como referencia y la función podrá modificar el objeto original usado en la declaración de llamada.
Los argumentos que se pasan por valor, así como las variables declaradas dentro de la función, no son visibles fuera de ella. Es decir, su alcance está restringido al cuerpo de la función donde fueron declarados. No obstante, las funciones se emplean generalmente para crear una salida visible fuera de la función. Para compartir un valor con su función de llamada, una función define una declaración return
.
Por ejemplo, la función test_prime
en el ejemplo anterior devuelve el valor de la variable is_prime
. Por lo tanto, la función puede reemplazar la variable en cualquier lugar donde se usaría en el ejemplo original:
// The number we want to evaluate
let candidate = 231;
// Display the result in the console window
if ( test_prime(candidate) )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
La declaración return
, como su nombre lo indica, devuelve el control a la función de llamada. Por lo tanto, donde quiera que se coloque la instrucción return
en la función, no se ejecutará nada a continuación. Una función puede contener múltiples declaraciones de retorno. Esta práctica puede ser útil si algunos están dentro de bloques condicionales de declaraciones, de modo que la función pueda ejecutar o no una instrucción return
particular en cada ejecución.
Es posible que algunas funciones no devuelvan un valor, por lo que la instrucción return
no es obligatoria. Las declaraciones internas de la función se ejecutan independientemente de su presencia, por lo que las funciones también se pueden usar para cambiar los valores de las variables globales o el contenido de los objetos pasados por referencia, por ejemplo. No obstante, si la función no tiene una declaración return
, su valor de retorno predeterminado se establece en undefined
: una variable reservada que no tiene un valor y no se puede escribir.
Expresiones de funciones
En JavaScript, las funciones son solo otro tipo de objeto. Por lo tanto, las funciones se pueden emplear en el script como variables. Esta característica se vuelve explícita cuando la función se declara usando una sintaxis alternativa, llamada expresiones de función:
let test_prime = function(candidate)
{
// Auxiliary variable
let is_prime = true;
// Start with the lowest prime number after 1
let factor = 2;
// Keeps evaluating while factor is less than the candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
// Send the answer back
return is_prime;
}
La única diferencia entre este ejemplo y la declaración de función en el ejemplo anterior está en la primera línea: let test_prime = function(candidate)
en lugar de function test_prime(candidate)
. En una expresión de función, el nombre test_prime
se usa para el objeto que contiene la función y no para nombrar la función en sí. Las funciones definidas en expresiones de función se llaman de la misma forma que las funciones definidas mediante la sintaxis de declaración. Sin embargo, mientras que las funciones declaradas se pueden llamar antes o después de su declaración, las expresiones de función solo se pueden llamar después de su inicialización. Al igual que con las variables, llamar a una función definida en una expresión antes de su inicialización provocará un error de referencia.
Funciones Recursivas
Además de ejecutar declaraciones y llamar a funciones integradas, las funciones personalizadas también pueden llamar a otras funciones personalizadas, incluidas ellas mismas. Llamar a una función desde sí misma se llama función recursiva. Dependiendo del tipo de problema que intente resolver, el uso de funciones recursivas puede ser más sencillo que el uso de bucles anidados para realizar tareas repetitivas.
Hasta ahora, sabemos cómo usar una función para probar si un número dado es primo. Ahora suponga que desea encontrar el siguiente primo que sigue a un número dado. Puede emplear un ciclo while
para incrementar el número de candidate
y escribir un ciclo anidado que buscará factores enteros para ese candidato:
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "true" keeps the loop going until a prime is found
while ( true )
{
// Auxiliary control variable
let is_prime = true;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Test the next candidate
is_prime = false;
break;
}
}
// End loop and return candidate if it is prime
if ( is_prime )
{
return candidate;
}
// If prime not found yet, try the next odd number
candidate = candidate + 2;
}
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Tenga en cuenta que necesitamos usar una condición constante para el ciclo while
(la expresión true
dentro del paréntesis) y la variable auxiliar is_prime
para saber cuándo detener el ciclo. Aunque esta solución es correcta, usar bucles anidados no es tan elegante como usar la recursividad para realizar la misma tarea:
// This function returns the next prime number
// after the number given as its only argument
function next_prime(from)
{
// We are only interested in the positive primes,
// so we will consider the number 2 as the next
// prime after any number less than two.
if ( from < 2 )
{
return 2;
}
// The number 2 is the only even positive prime,
// so it will be easier to treat it separately.
if ( from == 2 )
{
return 3;
}
// Decrement "from" if it is an even number
if ( from % 2 == 0 )
{
from--;
}
// Start searching for primes greater then 3.
// The prime candidate is the next odd number
let candidate = from + 2;
// "candidate" is an odd number, so the loop will
// try only the odd factors, starting with 3
for ( let factor = 3; factor < candidate; factor = factor + 2 )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime.
// Call the next_prime function recursively, this time
// using the failed candidate as the argument.
return next_prime(candidate);
}
}
// "candidate" is not divisible by any integer factor other
// than 1 and itself, therefore it is a prime number.
return candidate;
}
let from = 1024;
console.log("The next prime after", from, "is", next_prime(from));
Ambas versiones de next_prime
devuelven el siguiente número primo después del número dado como único argumento (from
). La versión recursiva, como la versión anterior, comienza marcando los casos especiales (es decir, números menores o iguales a dos). Luego incrementa el candidato y comienza a buscar cualquier factor entero con el ciclo for
(observe que el ciclo while
ya no existe). En ese punto, ya se ha probado el único número primo par, por lo que el candidato y sus posibles factores se incrementan en dos. (Un número impar más dos es el siguiente número impar).
Solo hay dos formas de salir del ciclo for
en el ejemplo. Si se prueban todos los factores posibles y ninguno de ellos tiene un resto igual a cero al dividir candidate
, el ciclo for
se completa y la función devuelve a candidate
como el siguiente número primo después de from
. De lo contrario, si factor
es un factor entero de candidate
(candidate % factor == 0
), el valor devuelto proviene de la función next_prime
llamada recursivamente, esta vez con candidate
incrementado como su paramétro from
. Las llamadas para next_prime
se apilarán una encima de la otra, hasta que un candidato finalmente no muestre factores enteros. Luego, la última instancia de next_prime
que contiene el número primo lo devolverá a la instancia anterior de next_prime
, y así sucesivamente hasta la primera instancia de next_prime
. Aunque cada invocación de la función utiliza los mismos nombres para las variables, las invocaciones están aisladas entre sí, por lo que sus variables se mantienen separadas en la memoria.
Ejercicios guiados
-
¿Qué tipo de sobrecarga pueden mitigar los desarrolladores mediante el uso de funciones?
-
¿Cuál es la diferencia entre los argumentos de función pasados por valor y los argumentos de función pasados por referencia?
-
¿Qué valor se utilizará como resultado de una función personalizada si no tiene una declaración de retorno?
Ejercicios de exploración
-
¿Cuál es la causa probable de un Error de referencia no capturado emitido al llamar a una función declarada con la sintaxis expresión?
-
Escriba una función llamada
multiples_of
que reciba tres argumentos:factor
,from
yto
. Dentro de la función, use la instrucciónconsole.log ()
para imprimir todos los múltiplos defactor
que se encuentran entrefrom
yto
.
Resumen
Esta lección cubre cómo escribir funciones personalizadas en código JavaScript. Las funciones personalizadas permiten al desarrollador dividir la aplicación en “fragmentos” de código reutilizables, lo que facilita la escritura y el mantenimiento de programas más grandes. La lección abarca los siguientes conceptos y procedimientos:
-
Cómo definir una función personalizada: declaraciones de funciones y expresiones de funciones.
-
Uso de parámetros como entrada de función.
-
Uso de la instrucción
return
para establecer la salida de la función. -
Función recursiva.
Respuestas a los ejercicios guiados
-
¿Qué tipo de sobrecarga pueden mitigar los desarrolladores mediante el uso de funciones?
Las funciones nos permiten reutilizar código, lo que facilita su mantenimiento. Un archivo de secuencia de comandos más pequeño también ahorra memoria y tiempo de descarga.
-
¿Cuál es la diferencia entre los argumentos de función pasados por valor y los argumentos de función pasados por referencia?
Cuando se pasa por valor, el argumento se copia a la función y la función no puede modificar la variable original en la declaración de llamada. Cuando se pasa por referencia, la función puede manipular la variable original utilizada en la declaración de llamada.
-
¿Qué valor se utilizará como resultado de una función personalizada si no tiene una declaración de retorno?
El valor devuelto se establecerá en
undefined
.
Respuestas a los ejercicios de exploración
-
¿Cuál es la causa probable de un Error de referencia no capturado emitido al llamar a una función declarada con la sintaxis expresión?
La función se llamó antes de su declaración en el archivo de secuencia de comandos.
-
Escriba una función llamada
multiples_of
que reciba tres argumentos:factor
,from
yto
. Dentro de la función, usa la instrucciónconsole.log()
para imprimir todos los múltiplos defactor
que se encuentran entrefrom
yto
.function multiples_of(factor, from, to) { for ( let number = from; number <= to; number++ ) { if ( number % factor == 0 ) { console.log(factor, "*", number / factor, "=", number); } } }