Command Palette

Search for a command to run...

Variables con var y Hoisting en JavaScript

Aprende cómo funciona var, qué es el hoisting y por qué estos comportamientos causan bugs difíciles de rastrear en tu código JavaScript.

Lectura: 10 min
Nivel: Principiante

TL;DR - Resumen rápido

  • var tiene function scope, no block scope - ignora if, for, while y otros bloques
  • Hoisting mueve las declaraciones var al inicio de la función (pero no las inicializaciones)
  • Puedes usar una variable var antes de declararla (será undefined, no error)
  • var permite re-declarar la misma variable múltiples veces sin errores
  • En el scope global, var crea propiedades en el objeto window (navegadores)
  • Los bucles for con var tienen comportamiento confuso - todas las iteraciones comparten la misma variable
  • let y const (ES6) solucionan estos problemas - úsalos en lugar de var en código moderno

¿Qué es var?

var fue la única forma de declarar variables en JavaScript desde su creación en 1995 hasta ES6 (2015). Aunque sigue funcionando por retrocompatibilidad, tiene comportamientos confusos que causan bugs difíciles de detectar. Por eso, las versiones modernas de JavaScript introdujeron let y const como alternativas más seguras.

Las variables declaradas con var tienen dos características problemáticas principales: function scope (en lugar de block scope) y hoisting (elevación de declaraciones). Estos comportamientos fueron decisiones de diseño originales de JavaScript que se mantienen por compatibilidad, pero se consideran errores en la evolución del lenguaje.

¿Por qué aprender sobre var?

Aunque no debes usar var en código nuevo, es importante entender cómo funciona porque: (1) encontrarás var en código legacy que necesites mantener, (2) entender hoisting te ayuda a comprender mejor cómo funciona JavaScript internamente, y (3) algunos errores con let/const también involucran conceptos de hoisting.

El Concepto de Hoisting

Hoisting (elevación) es el comportamiento por el cual JavaScript mueve las declaraciones de variables al inicio de su scope durante la fase de compilación, antes de ejecutar el código. Esto significa que puedes usar una variable antes de la línea donde la declaraste, algo que no es posible en la mayoría de lenguajes de programación.

hoisting-basico.js
Loading code...

Este comportamiento sorprendente ocurre porque JavaScript, durante la fase de compilación, "eleva" todas las declaraciones var al inicio de su scope (la función). Por eso puedes usar userName antes de declararla sin obtener un error - la variable ya existe, simplemente tiene el valor undefined. Este es uno de los comportamientos más confusos de JavaScript y una de las razones principales por las que var fue reemplazado por let y const.

Declaración vs Inicialización

Es crucial entender que solo la declaración se eleva (hoisting), no la inicialización. La declaración (crear la variable) se mueve al inicio, pero la asignación (darle un valor) permanece en su lugar original. Por eso, si accedes a una variable antes de su inicialización, obtienes undefined en lugar del valor esperado.

declaracion-vs-inicializacion.js
Loading code...

El ejemplo muestra un caso particularmente confuso: una variable declarada dentro de un if que nunca se ejecuta. Aunque la línea de inicialización nunca corre, la declaración sí se eleva al inicio de la función, por lo que status existe con valor undefined. JavaScript separa completamente la declaración (crear la variable) de la inicialización (asignarle un valor), y solo la primera se mueve al inicio.

Cómo funciona internamente

Cuando JavaScript ejecuta tu código, primero hace un "pase de compilación" donde identifica todas las declaraciones de variables y funciones, y las registra en memoria. Luego, en el "pase de ejecución", ejecuta el código línea por línea. Por eso las variables var están "disponibles" desde el inicio de la función, aunque con valor undefined hasta que se ejecuta la línea de asignación.

Function Scope (No Block Scope)

A diferencia de la mayoría de lenguajes modernos donde las variables tienen block scope (limitadas al bloque donde se declaran), las variables var tienen function scope. Esto significa que una variable var declarada dentro de un bloque (if, for, while, etc.) es visible en TODA la función, no solo dentro del bloque.

function-scope.js
Loading code...

Este ejemplo demuestra que var no respeta los límites de bloques (if, for, while). Las variables declaradas dentro de estos bloques son accesibles en toda la función, incluso fuera del bloque donde se declararon. Nota cómo la variable i del bucle for sigue existiendo después del bucle con el valor final (3). Sin embargo, estas variables NO son accesibles fuera de la función - var sí respeta los límites de funciones, solo ignora los bloques.

El Problema con Bloques if/while

El function scope de var causa confusión especialmente con bloques condicionales. Una variable declarada dentro de un if es accesible fuera de él, lo que va en contra de la intuición de la mayoría de programadores y puede causar bugs sutiles.

problema-bloques.js
Loading code...

Este es uno de los bugs más comunes con var. En calculatePrice, declaras total = 100 al inicio, pero luego dentro del if declaras var total de nuevo. Como var ignora el bloque del if, esta segunda declaración en realidad está modificando la misma variable, sobrescribiendo el valor original. El resultado final es 90, no 100 como esperarías. Con let, cada total sería una variable completamente diferente gracias al block scope.

Bug Real: Variable Escapada

Un error común es declarar una variable dentro de un if pensando que solo existe ahí. Si luego reutilizas ese nombre de variable en otra parte de la función, estarás modificando la misma variable, causando comportamientos inesperados. Con let/const esto no sucede porque tienen block scope.

El Problema Clásico con Bucles

Uno de los bugs más famosos con var ocurre en bucles for. Cuando usas var en la declaración del bucle, todas las iteraciones comparten la misma variable. Esto causa problemas especialmente cuando creas funciones dentro del bucle que intentan "capturar" el valor de la variable de iteración.

problema-bucles.js
Loading code...

Este es probablemente el bug más famoso de var. Con var, solo existe UNA variable i compartida por todas las iteraciones del bucle. Cuando los setTimeout se ejecutan (100ms después), el bucle ya terminó y i vale 3. Por eso las tres funciones imprimen 3. Con let, cada iteración del bucle crea su propia copia de la variable, por lo que cada setTimeout captura el valor correcto (0, 1, 2). Este problema era tan común que muchos desarrolladores usaban patrones complejos (IIFE) para solucionarlo antes de ES6.

Por Qué Sucede Esto

Con var, solo hay UNA variable i compartida por todas las iteraciones del bucle. Cuando las funciones se ejecutan después (en los setTimeout), el bucle ya terminó y i tiene el valor final (3 en el ejemplo). Todas las funciones ven la misma variable i con el mismo valor. Con let, cada iteración tiene su PROPIA copia de i.

Re-declaración Permitida

Con var puedes declarar la misma variable múltiples veces en el mismo scope sin obtener ningún error. La segunda declaración simplemente se ignora (a menos que incluya una inicialización). Este comportamiento hace muy fácil sobrescribir variables accidentalmente, especialmente en archivos grandes o cuando múltiples desarrolladores trabajan en el mismo código.

redeclaracion.js
Loading code...

JavaScript permite declarar la misma variable var múltiples veces sin lanzar ningún error. La segunda declaración (sin inicialización) simplemente se ignora, pero si incluye una asignación, esta sí se ejecuta. Esto hace extremadamente fácil sobrescribir variables accidentalmente en archivos grandes. let y const solucionan este problema: si intentas re-declarar una variable con el mismo nombre, obtienes un SyntaxError inmediato.

Peligro en Código Real

Imagina que declaras var userEmail al inicio de una función, y 200 líneas después, otro desarrollador (o tú mismo) declara var userEmail nuevamente sin darse cuenta. No hay error, pero ahora tienes dos partes del código modificando la misma variable, causando bugs extremadamente difíciles de rastrear. let y const lanzan error si intentas re-declarar, previniendo este problema.

Variables Globales con var

Cuando usas var en el scope global (fuera de cualquier función), la variable se convierte en una propiedad del objeto global (window en navegadores, global en Node.js). Esto contamina el namespace global y puede causar conflictos con código de terceros o librerías que usen los mismos nombres de variables.

variables-globales.js
Loading code...

Cuando declaras una variable var en el scope global (fuera de cualquier función), esta se convierte en una propiedad del objeto window en navegadores. Esto es extremadamente peligroso porque puedes sobrescribir funciones nativas del navegador accidentalmente, como se muestra con setTimeout. Una vez sobrescrito, setTimeout ya no funciona en toda la aplicación. let y const no tienen este problema: aunque las variables sean globales, NO se agregan a window, manteniéndolo limpio y seguro.

Solución Moderna

let y const en el scope global NO crean propiedades en window. Las variables siguen siendo globales, pero están en un espacio separado, evitando contaminar window. Mejor aún, usa módulos ES6 (import/export) donde no hay scope global - cada módulo tiene su propio scope privado.

Errores Comunes con var

Estos son los errores más frecuentes que cometen los desarrolladores cuando trabajan con var, especialmente quienes vienen de otros lenguajes de programación o están aprendiendo JavaScript.

errores-comunes.js
Loading code...

Estos cuatro errores resumen los problemas principales de var. El primero muestra que las variables escapan de los bucles. El segundo demuestra hoisting confuso. El tercero ilustra re-declaración silenciosa que sobrescribe valores. El cuarto es el problema clásico de callbacks en bucles. Todos estos errores son extremadamente comunes en código legacy y pueden causar bugs difíciles de rastrear. La solución universal es simple: usa let y const, nunca var.

Por Qué Evitar var en Código Moderno

Desde ES6 (2015), JavaScript tiene let y const que solucionan todos los problemas de var. No hay ninguna ventaja en usar var en código nuevo. Los navegadores y entornos modernos (Node.js 4+) soportan let y const completamente, y si necesitas compatibilidad con navegadores antiguos, herramientas como Babel pueden transpilar tu código.

Las razones principales para evitar var son: (1) function scope causa confusión y bugs, (2) hoisting hace el código menos predecible, (3) re-declaración silenciosa permite errores no detectados, (4) contamina el objeto global window, y (5) comportamiento confuso en bucles. let y const tienen block scope, lanzan errores en re-declaraciones, y tienen un comportamiento más intuitivo y predecible.

Regla Simple

Usa const por defecto para todas las variables. Si necesitas reasignar el valor, usa let. Nunca uses var. Esta regla simple te evitará la mayoría de bugs comunes relacionados con scope y mutabilidad en JavaScript. Los linters como ESLint pueden configurarse para advertir o prohibir el uso de var automáticamente.

Resumen: var y Hoisting

Problemas de var:

  • Function scope en lugar de block scope - las variables escapan de bloques if/for/while
  • Hoisting confuso - puedes usar variables antes de declararlas (valor undefined)
  • Re-declaración silenciosa - puedes declarar la misma variable múltiples veces
  • Contamina window en scope global
  • Comportamiento confuso en bucles - todas las iteraciones comparten la misma variable

Conceptos clave:

  • Hoisting mueve declaraciones (no inicializaciones) al inicio del scope
  • var solo respeta function scope, ignora todos los demás bloques
  • En código moderno, SIEMPRE usa let o const en lugar de var
  • const por defecto, let si necesitas reasignar
  • ESLint puede prohibir var automáticamente con la regla 'no-var'