HDP115

Funciones y Scope

Aprende a crear y utilizar funciones en JavaScript, comprendiendo el ámbito de las variables y los conceptos avanzados como closures y hoisting.

CE

Cristian Escalante

Última actualización: 20 de abril de 2025

javascript
programación web
desarrollo frontend

Introducción a las funciones

Las funciones son bloques de código reutilizables diseñados para realizar una tarea específica. Son fundamentales en JavaScript, ya que permiten estructurar el código, evitar repeticiones y crear abstracciones.

Definición de funciones

Declaración de función

La forma más común de definir una función es mediante una declaración:

function saludar() {
  console.log("¡Hola, mundo!");
}

// Llamada a la función
saludar(); // ¡Hola, mundo!

Expresión de función

Una función también puede definirse como una expresión:

const saludar = function() {
  console.log("¡Hola, mundo!");
};

// Llamada a la función
saludar(); // ¡Hola, mundo!

La diferencia principal entre declaraciones y expresiones de función es el hoisting (elevación), que veremos más adelante.

Funciones flecha (Arrow Functions)

Introducidas en ES6, ofrecen una sintaxis más concisa:

const saludar = () => {
  console.log("¡Hola, mundo!");
};

// Versión aún más concisa para funciones de una sola línea
const sumar = (a, b) => a + b;

console.log(sumar(5, 3)); // 8

Funciones anónimas

Son funciones sin nombre, a menudo usadas como argumentos para otras funciones:

// Función anónima como callback
setTimeout(function() {
  console.log("Este mensaje aparece después de 2 segundos");
}, 2000);

// Lo mismo con función flecha
setTimeout(() => {
  console.log("Este mensaje aparece después de 2 segundos");
}, 2000);

Parámetros y argumentos

Los parámetros son las variables listadas en la definición de la función. Los argumentos son los valores que se pasan a la función cuando se la llama.

// a y b son parámetros
function sumar(a, b) {
  return a + b;
}

// 5 y 3 son argumentos
const resultado = sumar(5, 3);
console.log(resultado); // 8

Parámetros predeterminados

ES6 introdujo la capacidad de establecer valores predeterminados para los parámetros:

function saludar(nombre = "Invitado") {
  console.log(`¡Hola, ${nombre}!`);
}

saludar("María"); // ¡Hola, María!
saludar(); // ¡Hola, Invitado!

Parámetros rest

Permiten representar un número indefinido de argumentos como un array:

function sumar(...numeros) {
  return numeros.reduce((total, num) => total + num, 0);
}

console.log(sumar(1, 2, 3, 4, 5)); // 15
console.log(sumar(10, 20)); // 30

Desestructuración de parámetros

Permite extraer propiedades de objetos o elementos de arrays pasados como argumentos:

function mostrarPersona({ nombre, edad }) {
  console.log(`${nombre} tiene ${edad} años`);
}

const persona = {
  nombre: "Carlos",
  edad: 30,
  profesion: "Desarrollador"
};

mostrarPersona(persona); // Carlos tiene 30 años

Return y valores de retorno

Las funciones pueden devolver valores usando la declaración return:

function multiplicar(a, b) {
  return a * b;
}

const resultado = multiplicar(4, 5);
console.log(resultado); // 20

Características importantes de return:

  1. Una función devuelve undefined si no tiene una declaración return o si esta no especifica un valor
  2. La ejecución de la función se detiene cuando se encuentra una declaración return
  3. Una función puede devolver cualquier tipo de dato, incluyendo objetos, arrays y otras funciones
function crearSaludoPersonalizado(nombre) {
  return function() {
    console.log(`¡Hola, ${nombre}!`);
  };
}

const saludarAMaria = crearSaludoPersonalizado("María");
saludarAMaria(); // ¡Hola, María!

Diferencias entre tipos de funciones

Funciones declarativas vs. expresiones de función

// Función declarativa (se puede llamar antes de su definición)
console.log(sumar(2, 3)); // 5
function sumar(a, b) {
  return a + b;
}

// Expresión de función (no se puede llamar antes de su definición)
console.log(restar(5, 2)); // Error: restar is not a function
const restar = function(a, b) {
  return a - b;
};

Funciones tradicionales vs. funciones flecha

  1. Sintaxis más concisa:
    // Función tradicional
    const sumar1 = function(a, b) {
      return a + b;
    };
    
    // Función flecha
    const sumar2 = (a, b) => a + b;
    
  2. No tienen su propio this:
    // Función tradicional
    const persona1 = {
      nombre: "Juan",
      saludar: function() {
        setTimeout(function() {
          console.log(`Hola, soy ${this.nombre}`); // undefined
        }, 100);
      }
    };
    
    // Función flecha
    const persona2 = {
      nombre: "Juan",
      saludar: function() {
        setTimeout(() => {
          console.log(`Hola, soy ${this.nombre}`); // Juan
        }, 100);
      }
    };
    
  3. No pueden usarse como constructores:
    // Funciona
    const Persona = function(nombre) {
      this.nombre = nombre;
    };
    const juan = new Persona("Juan");
    
    // Error: Arrow functions cannot be used as constructors
    const Persona = (nombre) => {
      this.nombre = nombre;
    };
    const juan = new Persona("Juan");
    
  4. No tienen objeto arguments:
    function tradicional() {
      console.log(arguments);
    }
    
    const flecha = () => {
      console.log(arguments); // Error o referencia al objeto arguments del ámbito externo
    };
    

Scope (ámbito)

El scope determina la accesibilidad de variables, objetos y funciones desde diferentes partes del código.

Scope global

Las variables declaradas fuera de cualquier función o bloque tienen ámbito global:

// Variable global
const mensaje = "Hola, mundo";

function mostrarMensaje() {
  console.log(mensaje); // Accede a la variable global
}

mostrarMensaje(); // Hola, mundo

Scope local o de función

Las variables declaradas dentro de una función solo son accesibles dentro de esa función:

function mostrarMensaje() {
  // Variable local
  const mensaje = "Hola desde la función";
  console.log(mensaje);
}

mostrarMensaje(); // Hola desde la función
console.log(mensaje); // Error: mensaje is not defined

Scope de bloque (con let y const)

Las variables declaradas con let y const tienen ámbito de bloque:

if (true) {
  // Variable de bloque
  let mensaje = "Hola desde el bloque";
  const saludo = "Bienvenido";
  var antiguo = "Variable con var"; // No respeta el ámbito de bloque
  
  console.log(mensaje); // Hola desde el bloque
}

console.log(mensaje); // Error: mensaje is not defined
console.log(saludo); // Error: saludo is not defined
console.log(antiguo); // Variable con var (¡accesible fuera del bloque!)

Anidamiento de scopes

Los ámbitos internos pueden acceder a variables de ámbitos externos, pero no al revés:

const global = "Variable global";

function externa() {
  const externa = "Variable externa";
  
  function interna() {
    const interna = "Variable interna";
    console.log(global);   // Variable global
    console.log(externa);  // Variable externa
    console.log(interna);  // Variable interna
  }
  
  interna();
  console.log(global);   // Variable global
  console.log(externa);  // Variable externa
  console.log(interna);  // Error: interna is not defined
}

externa();

Closures (Cierres)

Un closure es una función que recuerda el entorno en el que fue creada, incluso después de que la función externa haya terminado de ejecutarse.

function crearContador() {
  let contador = 0;
  
  return function() {
    contador++;
    return contador;
  };
}

const incrementar = crearContador();
console.log(incrementar()); // 1
console.log(incrementar()); // 2
console.log(incrementar()); // 3

En este ejemplo, la función interna forma un closure que "recuerda" la variable contador, incluso después de que crearContador() haya terminado su ejecución.

Usos comunes de closures

  1. Datos privados:
    function crearCuenta(saldoInicial) {
      let saldo = saldoInicial;
      
      return {
        consultar: function() {
          return saldo;
        },
        depositar: function(cantidad) {
          saldo += cantidad;
          return saldo;
        },
        retirar: function(cantidad) {
          if (cantidad <= saldo) {
            saldo -= cantidad;
            return saldo;
          } else {
            return "Fondos insuficientes";
          }
        }
      };
    }
    
    const cuenta = crearCuenta(100);
    console.log(cuenta.consultar()); // 100
    console.log(cuenta.depositar(50)); // 150
    console.log(cuenta.retirar(70)); // 80
    console.log(cuenta.saldo); // undefined (no se puede acceder directamente)
    
  2. Funciones de fábrica:
    function crearMultiplicador(factor) {
      return function(numero) {
        return numero * factor;
      };
    }
    
    const duplicar = crearMultiplicador(2);
    const triplicar = crearMultiplicador(3);
    
    console.log(duplicar(5)); // 10
    console.log(triplicar(5)); // 15
    

Hoisting (elevación)

Hoisting es el comportamiento de JavaScript de mover las declaraciones al principio del ámbito actual antes de la ejecución del código.

Hoisting de variables

console.log(x); // undefined (no Error)
var x = 5;
console.log(x); // 5

// Es equivalente a:
var x;
console.log(x); // undefined
x = 5;
console.log(x); // 5

Con let y const, las variables también se elevan pero entran en una "zona muerta temporal" (TDZ) hasta su declaración:

console.log(y); // Error: Cannot access 'y' before initialization
let y = 10;

Hoisting de funciones

Las declaraciones de funciones se elevan completamente:

saludar(); // "Hola, mundo"

function saludar() {
  console.log("Hola, mundo");
}

Pero las expresiones de función no:

despedir(); // Error: despedir is not a function

var despedir = function() {
  console.log("Adiós, mundo");
};

Mejores prácticas

  1. Preferir let y const sobre var:
    // Evitar
    var contador = 0;
    
    // Preferir
    const PI = 3.14159;
    let contador = 0;
    
  2. Funciones pequeñas y con un solo propósito:
    // Mejor tener varias funciones pequeñas que una grande
    function validarFormulario() {
      return validarNombre() && validarEmail() && validarContraseña();
    }
    
  3. Usar parámetros predeterminados:
    // En lugar de
    function saludar(nombre) {
      nombre = nombre || "Invitado";
      console.log(`Hola, ${nombre}`);
    }
    
    // Usar
    function saludar(nombre = "Invitado") {
      console.log(`Hola, ${nombre}`);
    }
    
  4. Evitar variables globales:
    // Evitar
    const contador = 0; // global
    
    // Preferir
    (function() {
      const contador = 0; // local al IIFE
    })();
    
  5. Usar funciones flecha para callbacks cortos:
    // En lugar de
    [1, 2, 3].map(function(num) {
      return num * 2;
    });
    
    // Usar
    [1, 2, 3].map(num => num * 2);
    
Estructuras de Control
Aprende a controlar el flujo de ejecución en JavaScript medi...
Objetos y Propiedades
Aprende a trabajar con objetos en JavaScript, desde su creac...

Conceptos Básicos de HTML

Aprende los conceptos básicos de HTML

Conceptos Básicos de CSS

Aprende los conceptos básicos de CSS

Conceptos Básicos SQL

Aprende los conceptos básicos de SQL

Conceptos Básicos de GIT

Aprende los conceptos básicos de GIT

Conceptos Básicos de Python

Aprende los conceptos básicos de Python

Conceptos Básicos de UML

Aprende los conceptos básicos de UML

Refuerzo Academico de Herramientas de Productividad 2025