Funciones de Node.js

Una función es un bloque de código reutilizable que se utiliza para realizar una tarea específica.

En Node.js, las funciones son uno de los componentes principales de JavaScript y son la unidad básica para construir aplicaciones.

Node.js hereda todas las características de funciones de JavaScript y juega un papel importante en su modelo de programación asíncrona.

En JavaScript, una función puede usarse como argumento de otra función. Podemos definir primero una función y pasarla, o podemos definirla directamente en el lugar del argumento.

Importancia de las funciones

  • Reutilización de código: evita escribir repetidamente la misma lógica
  • Modularidad: descompone problemas complejos en funciones pequeñas
  • Mantenibilidad: facilita la depuración y modificación de unidades funcionales independientes

Declaración de funciones

Usafunctionla palabra clave para declarar una función.

function greet(name) {
    console.log(`Hello, ${name}!`);
}

Expresiones de función

Asignar la función a una variable.

const greet = function(name) {
    console.log(`Hello, ${name}!`);
};

Funciones flecha

Expresión de función concisa introducida por ES6.

const greet = (name) => {
    console.log(`Hello, ${name}!`);
};

// 单行箭头函数
const greet = name => console.log(`Hello, ${name}!`);

Tipos de funciones

1. Funciones normales

La forma más común de función; puede tener parámetros y valor de retorno.

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

2. Funciones anónimas

Funciones sin nombre, generalmente se pasan como argumentos a otras funciones.

setTimeout(function() {
    console.log('This is an anonymous function.');
}, 1000);

3. Funciones callback

Se pasan como argumento a otra función y se llaman después de que se complete alguna operación.

function fetchData(callback) {
    setTimeout(() => {
        const data = 'Some data';
        callback(data);
    }, 1000);
}

fetchData((data) => {
    console.log(data);
});

Funciones asíncronas

De callbacks a Promise.

Ejemplo

function readFilePromise(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err);
      else resolve(data);
    });
  });
}

Usa las palabras clave async y await para manejar operaciones asíncronas.

Ejemplo

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

fetchUser(1).then(user => console.log(user));

Comparación de formas de invocación

Forma de invocación Ejemplo Características
Llamada directa greet('Alice') La forma más común
Llamada como método obj.method() this apunta al objeto que llama
Llamada como constructor new Constructor() Crea una nueva instancia
Llamada indirecta greet.call(null, 'Bob') Puede cambiar a qué apunta this

Uso avanzado

1. Cierres

Un cierre es una función que puede recordar y acceder a su ámbito léxico, incluso cuando se ejecuta fuera de dicho ámbito.

Ejemplo

function createCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

2. Funciones de orden superior

Funciones que aceptan funciones como argumentos o devuelven funciones.

Ejemplo

function applyOperation(a, b, operation) {
    return operation(a, b);
}

const sum = applyOperation(5, 3, (x, y) => x + y);
const product = applyOperation(5, 3, (x, y) => x * y);

console.log(sum); // 8
console.log(product); // 15

Manejo de parámetros de función

Parámetros por defecto

Proporcionar valores por defecto para los parámetros al declarar la función.

function greet(name = 'Guest') {
    console.log(`Hello, ${name}!`);
}

greet(); // Hello, Guest!
greet('Alice'); // Hello, Alice!

Parámetros rest

Permite representar un número indefinido de argumentos como un array.

Ejemplo

function sum(...numbers) {
    return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sum(1, 2, 3, 4)); // 10

Desestructuración de parámetros

Extraer datos de un objeto o array y asignarlos a variables.

Ejemplo

function getUserInfo({ name, age }) {
    console.log(`Name: ${name}, Age: ${age}`);
}

const user = { name: 'Alice', age: 30 };
getUserInfo(user); // Name: Alice, Age: 30

Ejemplo

Responsabilidad única de la función:Cada función debería hacer solo una cosa, así es más fácil de probar y mantener.

Ejemplo

function calculateArea(width, height) {
    return width * height;
}

Evitar variables globales:Reducir al mínimo el uso de variables globales; usar variables locales y parámetros de función.

Ejemplo

function calculateTotal(items) {
    let total = 0;
    for (const item of items) {
        total += item.price * item.quantity;
    }
    return total;
}

Usar funciones flecha:Las funciones flecha son concisas y claras, especialmente al manejar callbacks.

Ejemplo

const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
];

const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob']

Manejo de errores:Usa la declaración try...catch para manejar errores que puedan lanzarse.

Ejemplo

async function fetchUser(id) {
    try {
        const response = await fetch(`https://api.example.com/users/${id}`);
        if (!response.ok) {
            throw new Error('Network response was not ok');
        }
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
    }
}

Funciones como parámetros:Una función se usa como argumento.

Ejemplo

function say(word) {
  console.log(word);
}

function execute(someFunction, value) {
  someFunction(value);
}

execute(say, "Hello");

En el código anterior, pasamos la función say como el primer argumento de la función execute. Lo que se pasa no es el valor de retorno de say, sino la propia say.

De esta manera, say se convierte en la variable local someFunction dentro de execute, y execute puede usar la función say llamando a someFunction() (con paréntesis).

Por supuesto, como say tiene un parámetro, execute puede pasar ese parámetro al llamar a someFunction.

Funciones anónimas:Podemos pasar una función como variable. Pero no es necesario rodear el círculo de “definir primero, luego pasar”; podemos definir y pasar la función directamente entre los paréntesis de otra función:

Ejemplo

function execute(someFunction, value) {
  someFunction(value);
}

execute(function(word){ console.log(word) }, "Hello");

Definimos la función que vamos a pasar a execute directamente en el lugar donde execute recibe su primer argumento.

De esta manera, ni siquiera necesitamos darle un nombre a la función; por eso se llama función anónima.


Cómo el paso de funciones hace funcionar al servidor HTTP

Con estos conocimientos, echemos un vistazo a nuestro servidor HTTP sencillo pero no trivial:

Ejemplo

var http = require("http");

http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}).listen(8888);

Ahora debería verse mucho más claro: pasamos una función anónima a la función createServer.

Con un código como este también se puede lograr el mismo propósito:

Ejemplo

var http = require("http");

function onRequest(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
}

http.createServer(onRequest).listen(8888);

Mejores prácticas para funciones

Principios de diseño de funciones

  1. Responsabilidad única: una función hace solo una cosa
  2. Nombres adecuados: usar la forma verbo + sustantivo (por ejemplo,getUserInfo)
  3. Control de longitud: se recomienda no exceder las 20 líneas de código
  4. Evitar efectos secundarios: las funciones puras son más fáciles de probar y mantener

Consejos de optimización del rendimiento

Ejemplo

// Usar una clausura para almacenar en caché el resultado
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

Ejercicios prácticos

Ejercicio 1: Crear una función de conversión de temperatura

Ejemplo

/**
* Implementa la conversión mutua entre grados Celsius y Fahrenheit
* @param {number} temp - Valor de temperatura
* @param {string} unit - Unidad original ('C' o 'F')
* @returns {number} Temperatura convertida
 */

function convertTemperature(temp, unit) {
  // Tu código...
}

Ejercicio 2: Implementar un emisor de eventos simple

Ejemplo

class EventEmitter {
  constructor() {
    this.events = {};
  }
 
  // Implementa los métodos on/emit/off
}
Otras extensiones