Zademy

Formas de declarar funciones en JavaScript: guía técnica y práctica

JavaScript; funciones
words palabras

Declaración de función tradicional (function declaration)

La forma clásica. La que aprendes el primer día. function seguido de un nombre obligatorio. Lo que la diferencia del resto es el hoisting: puedes llamarla antes de declararla en el archivo y funciona, porque el motor la eleva al top del scope durante la compilación.

Ejemplo con parámetros:

function saludar(nombre) {
	return `Hola, ${nombre}!`;
}
console.log(saludar("Mundo")); // "Hola, Mundo!"

Expresión de función (function expression)

Aquí la función es un valor. La asignas a una variable con let, var o const. El truco: no hay hoisting completo. La función no existe hasta que la ejecución llega a esa línea. Si la llamas antes, te llevas un TypeError.

Puede ser anónima (sin nombre propio) o nombrada. El nombre interno en una nombrada sirve para recursión y aparece en el stack trace, lo que facilita depurar.

Ejemplo:

const sumar = function (a, b) {
	return a + b;
}; // Se recomienda 'const' para evitar reasignaciones accidentales

Funciones de flecha (arrow functions)

Llegaron con ES6 y cambiaron cómo escribimos callbacks. Sintaxis compacta, sin la palabra function. Pero lo importante no es que sea más corta: las arrow functions no tienen su propio this. Lo heredan del scope léxico donde se definen. Esto soluciona de golpe el problema clásico de perder this dentro de un callback.

Si tiene un solo parámetro y una sola expresión, puedes omitir paréntesis, llaves y el return:

Ejemplo:

const cuadrado = x => x * x; // Retorno implícito

Definición de métodos en objetos y clases (shorthand)

Sintaxis abreviada para declarar funciones dentro de objetos literales o clases. Sin los dos puntos, sin la palabra function. Es la forma recomendada hoy en día, y es además la única que da acceso a super.

Ejemplo en objeto:

const calculadora = {
	valor: 10,
	duplicar() {
		return this.valor * 2;
	} // Sintaxis limpia
};

Funciones asíncronas (async)

El prefijo async hace que la función devuelva siempre una promesa, automáticamente. Dentro puedes usar await para pausar la ejecución hasta que una promesa se resuelva, sin anidar .then() hasta el infinito.

Ejemplo:

async function obtenerDatos() {
	const respuesta = await fetch("url");
	return respuesta.json();
}

Funciones generadoras (function*)

Se definen con function* y tienen algo raro: pueden pausarse. Con yield devuelven un valor y ceden el control al llamador. La próxima vez que las invoques, continúan donde se quedaron. Son la base de los iteradores y de librerías como redux-saga.

Ejemplo:

function* contador() {
	yield 1;
	yield 2;
}

Expresiones de función ejecutadas inmediatamente (IIFE)

Funciones que se ejecutan al definirse. Se usan para crear scope privado sin contaminar el global. Antes de los módulos de ES6 era la forma estándar de encapsular. Hoy se ven menos, pero sigue siendo una técnica válida.

Ejemplo:

(function () {
	const mensaje = "Privado";
	console.log(mensaje);
})();

Constructor Function (no recomendado)

Crea funciones a partir de strings en runtime. Funciona, pero no lo uses. Expone a inyección de código y los motores no pueden optimizar el resultado. Lo menciono aquí para que lo reconozcas si lo ves en código legacy, no para que lo escribas.

Ejemplo:

const sumarDinamico = new Function("a", "b", "return a + b");

Analogía para entender las diferencias

Una declaración de función es como una receta que cuelgas en la pared antes de empezar el turno: está ahí desde el principio, todos saben dónde encontrarla. Una expresión de función es una nota que escribes en el momento: solo puedes seguirla después de haberla pegado. Una arrow function es un atajo mental, algo que haces sin montar toda la cocina, usando el contexto que ya tienes a mano.