Return y valores devueltos
Introducción
En el artículo anterior exploramos cómo las funciones pueden recibir información a través de parámetros. Ahora veremos la otra cara de la moneda: cómo las funciones pueden devolver valores utilizando la sentencia return
. La capacidad de retornar valores es lo que permite a las funciones no solo procesar datos sino también producir resultados que podemos utilizar en otras partes de nuestro código. Esta característica es fundamental para crear componentes de software modulares y reutilizables. En este artículo, entenderemos el propósito y funcionamiento de return
, los diferentes tipos de datos que pueden devolverse y las mejores prácticas para trabajar con valores devueltos.
Propósito de la sentencia return
La sentencia return
tiene dos funciones principales:
- Devolver un valor: Permite que la función entregue un resultado que puede ser utilizado donde se invocó la función.
- Finalizar la ejecución: Cuando JavaScript encuentra un
return
, detiene inmediatamente la ejecución de la función y devuelve el control al punto donde fue llamada.
Veamos un ejemplo sencillo:
function sumar(a, b) {
return a + b; // Devuelve la suma y finaliza la función
console.log("Este código nunca se ejecutará");
}
let resultado = sumar(5, 3);
console.log(resultado); // 8
En este ejemplo, sumar()
devuelve el valor de a + b
, que luego se asigna a la variable resultado
. Es importante notar que cualquier código que aparezca después de un return
nunca se ejecutará.
Devolución de diferentes tipos de datos
Las funciones en JavaScript pueden devolver cualquier tipo de dato, incluyendo valores primitivos, objetos, arrays, otras funciones e incluso valores indefinidos.
Devolución de valores primitivos
function obtenerNombre() {
return "Ana"; // Devuelve un string
}
function obtenerEdad() {
return 25; // Devuelve un número
}
function esMayorDeEdad(edad) {
return edad >= 18; // Devuelve un booleano
}
console.log(obtenerNombre()); // Ana
console.log(obtenerEdad()); // 25
console.log(esMayorDeEdad(20)); // true
Devolución de objetos
function crearPersona(nombre, edad) {
return {
nombre: nombre,
edad: edad,
esMayorDeEdad: edad >= 18
};
}
const persona = crearPersona("Carlos", 30);
console.log(persona.nombre); // Carlos
console.log(persona.esMayorDeEdad); // true
Devolución de arrays
function obtenerColores() {
return ["rojo", "verde", "azul"];
}
const colores = obtenerColores();
console.log(colores[0]); // rojo
console.log(colores.length); // 3
Devolución de funciones
En JavaScript, las funciones son "ciudadanos de primera clase", lo que significa que pueden ser devueltas por otras funciones:
function crearSaludador(saludo) {
// Devolvemos una nueva función
return function(nombre) {
return `${saludo}, ${nombre}!`;
};
}
const saludarFormal = crearSaludador("Buenos días");
const saludarInformal = crearSaludador("Hola");
console.log(saludarFormal("Señor García")); // Buenos días, Señor García!
console.log(saludarInformal("Ana")); // Hola, Ana!
Este patrón se conoce como "fábrica de funciones" y es muy útil para crear funciones especializadas a partir de una base común.
Múltiples returns en una función
Una función puede tener múltiples sentencias return
, aunque solo una de ellas se ejecutará en cada invocación:
function obtenerCalificacion(puntuacion) {
if (puntuacion >= 90) {
return "Sobresaliente";
} else if (puntuacion >= 70) {
return "Notable";
} else if (puntuacion >= 60) {
return "Bien";
} else if (puntuacion >= 50) {
return "Suficiente";
} else {
return "Insuficiente";
}
}
console.log(obtenerCalificacion(95)); // Sobresaliente
console.log(obtenerCalificacion(65)); // Bien
console.log(obtenerCalificacion(30)); // Insuficiente
Este patrón es muy común para funciones que necesitan devolver diferentes valores según las condiciones.
Return implícito vs. explícito
En la mayoría de las declaraciones de funciones, debemos usar la palabra clave return
explícitamente. Sin embargo, las funciones flecha tienen un comportamiento especial cuando se escriben en una sola línea sin llaves:
Return explícito (tradicional)
function duplicar(numero) {
return numero * 2;
}
Return implícito (función flecha)
const duplicar = numero => numero * 2;
En el segundo ejemplo, el valor de numero * 2
se devuelve automáticamente sin necesidad de escribir return
. Esto solo funciona cuando la función flecha se escribe como una expresión simple sin llaves.
Si añadimos llaves, debemos incluir el return
explícitamente:
const duplicar = numero => {
return numero * 2;
};
Funciones sin return (void)
No todas las funciones necesitan devolver un valor. Algunas funciones se usan principalmente por sus efectos secundarios, como modificar variables, actualizar el DOM o enviar datos a un servidor.
function saludar(nombre) {
console.log(`Hola, ${nombre}!`);
// No hay return explícito
}
const resultado = saludar("Luis");
console.log(resultado); // undefined
En este ejemplo, saludar()
no tiene una sentencia return
, por lo que implícitamente devuelve undefined
. Esto es perfectamente válido cuando la función se usa por su efecto (mostrar un mensaje) y no por un valor de retorno.
Es importante destacar que todas las funciones en JavaScript devuelven algo, aunque no tengan una sentencia return
explícita. Por defecto, devolverán undefined
:
function sinRetorno() {
let a = 5;
a = a + 10;
// Sin return explícito
}
console.log(sinRetorno()); // undefined
Return temprano para validaciones
Un patrón común en JavaScript es usar return
temprano para validar condiciones antes de ejecutar el cuerpo principal de la función:
function dividir(a, b) {
// Validación: evitar división por cero
if (b === 0) {
console.error("Error: División por cero");
return null; // Return temprano
}
// Código principal que solo se ejecuta si la validación pasa
return a / b;
}
console.log(dividir(10, 2)); // 5
console.log(dividir(10, 0)); // null (después de mostrar el error)
Este enfoque, conocido como "early return" (retorno temprano), hace que el código sea más legible y evita la anidación excesiva de bloques if/else
.
Captura y uso de valores devueltos
Los valores devueltos por las funciones se pueden usar de diversas maneras:
Asignación a variables
function obtenerPrecioFinal(precio, impuesto) {
return precio * (1 + impuesto);
}
const precioProducto = 100;
const iva = 0.21;
const precioFinal = obtenerPrecioFinal(precioProducto, iva);
console.log(`El precio final es: ${precioFinal}€`); // El precio final es: 121€
Uso directo en expresiones
function obtenerPrecio() {
return 50;
}
function obtenerImpuesto() {
return 0.21;
}
const total = obtenerPrecio() * (1 + obtenerImpuesto());
console.log(total); // 60.5
Encadenamiento de funciones
Los valores devueltos pueden pasarse directamente como argumentos a otras funciones:
function obtenerTexto() {
return " Hola Mundo ";
}
function limpiarTexto(texto) {
return texto.trim();
}
function contarCaracteres(texto) {
return texto.length;
}
// Encadenamiento de funciones
const cantidadCaracteres = contarCaracteres(limpiarTexto(obtenerTexto()));
console.log(cantidadCaracteres); // 10
Uso con métodos de arrays
Los valores devueltos son especialmente útiles con métodos funcionales de arrays:
const numeros = [1, 2, 3, 4, 5];
// Usamos una función que devuelve un booleano
function esPar(numero) {
return numero % 2 === 0;
}
const numerosPares = numeros.filter(esPar);
console.log(numerosPares); // [2, 4]
Patrones comunes y buenas prácticas
1. Coherencia en los tipos de retorno
Es recomendable que una función devuelva siempre el mismo tipo de datos, independientemente de la ruta de ejecución:
// No recomendado: devuelve diferentes tipos
function dividir(a, b) {
if (b === 0) {
return "Error: división por cero"; // String
}
return a / b; // Número
}
// Mejor enfoque: tipo de retorno consistente
function dividir(a, b) {
if (b === 0) {
return null; // O podría ser NaN
}
return a / b;
}
2. Preferir valores de retorno sobre efectos secundarios
Es mejor diseñar funciones que devuelvan valores en lugar de modificar variables externas:
// No recomendado: modifica una variable externa
let total = 0;
function sumar(a, b) {
total = a + b; // Efecto secundario
}
// Mejor enfoque: devuelve un valor
function sumar(a, b) {
return a + b; // Devuelve un valor, sin efectos secundarios
}
let total = sumar(5, 3);
3. Una única responsabilidad
Cada función debería tener una única responsabilidad clara, reflejada en su valor de retorno:
// No recomendado: hace demasiadas cosas
function procesarDatos(datos) {
// Filtra datos
const filtrados = datos.filter(item => item.activo);
// Los guarda en algún lugar
guardarEnBaseDeDatos(filtrados);
// Actualiza la interfaz
actualizarUI(filtrados);
// Devuelve algo
return filtrados.length;
}
// Mejor enfoque: funciones separadas con responsabilidades claras
function filtrarDatosActivos(datos) {
return datos.filter(item => item.activo);
}
// Luego podemos usar el valor devuelto en otras funciones
const datosFiltrados = filtrarDatosActivos(datos);
guardarEnBaseDeDatos(datosFiltrados);
actualizarUI(datosFiltrados);
4. Documentar el valor de retorno
Es una buena práctica documentar claramente qué devuelve una función:
/**
* Calcula el área de un círculo.
* @param {number} radio - El radio del círculo
* @returns {number} El área del círculo
*/
function calcularAreaCirculo(radio) {
return Math.PI * radio * radio;
}
Resumen
La sentencia return
es una herramienta fundamental en JavaScript que permite a las funciones devolver valores y finalizar su ejecución. Hemos visto cómo las funciones pueden devolver diferentes tipos de datos, desde valores primitivos hasta objetos complejos y otras funciones. También exploramos patrones comunes como múltiples returns, returns tempranos y la diferencia entre returns implícitos y explícitos.
Entender cómo trabajar con valores devueltos es esencial para crear código modular, reutilizable y fácil de mantener. Las funciones bien diseñadas que reciben parámetros claros y devuelven valores consistentes forman la base de una arquitectura de software sólida.
En el próximo artículo, exploraremos las diferentes formas de invocar funciones en JavaScript, completando nuestra comprensión sobre las funciones, que son bloques de construcción fundamentales en este lenguaje.