La Zona de Muerte Temporal (TDZ) es un nombre dramático para un concepto muy simple en programación.
*A veces también se le llama Temporal Data Zone.
Es un periodo de tiempo muy breve en el que no se puede utilizar una variable. Este período inicia en el momento en el que se entra al scope de la variable y termina cuando la autoinicialización ocurre. Por supuesto para nosotros como desarrolladores eso es prácticamente lo mismo, porque el 99% de las veces no es un comportamiento observable.
Pero para poder ver el TDZ en acción, primero tienes que tener muy clara la diferencia entre un error de definición y uno de inicialización.
Error de definición
¿Qué crees que pase al ejecutar el siguient bloque de código?
showData()
function showData() {
console.log(`Querying data from ${databaseName}`)
}
Si tu respuesta fue: “Lanzará un error porque databaseName
no ha sido declarada”. ¡Estás en lo correcto! Si no…pues la verdad no importa. No te estoy haciendo examen.
showData()
function showData() {
console.log(`Querying data from ${databaseName}`) // ReferenceError: databaseName is not defined
}
Error de inicialización – Inicialización con let
…Ok simple entonces para arreglar el error sólo tienes que agregar la declaración de databaseName
. ¿cierto? Intenta comprobarlo corriendo este nuevo bloque.
* Es importante que tengas en cuenta que let y const son intercambiables en los ejemplos que verás en este post.
showData()
let databaseName = 'computersDB'
function showData() {
console.log(`Querying data from ${databaseName}`) // ReferenceError: Cannot access 'databaseName' before initialization
}
Si, obvio era un trampa. Si hubiera funcionado entonces este post no tendría sentido.
La razón por la que Javascript lanzá un error es que estás intentando acceder a la variable de databaseName
antes de que esta haya sido inicializada. (Precisamente como lo indica el mensaje)
¿Por que pasa esto? Si declaras la variable Javascript lo reconoce, pero por alguna razón no reconoce la asignación y por eso lanza el error anterior. Un misterio…
El tema se vuelve aún mas complejo si traemos a var a la jugada. Intenta reemplzar la declaración con let
por un var
.
Inicialización con var
showData()
var databaseName = 'computersDB'
function showData() {
console.log(`Querying data from ${databaseName}`)
}
En este caso Javascript no lanzá un error. Pero… tampoco asignó el valor correctamente. En su lugar inicalizó a databaseName
con undefined
.
showData()
var databaseName = 'computersDB'
function showData() {
console.log(`Querying data from ${databaseName}`) // Querying data from undefined
}
La explicación
El misterio se resuelve al tener claros 3 conceptos: hoisting, autoinicialización y scope. Así como tambén al tener en cuenta las diferencias de comportamiento de los mismos al declarar con var
o let
.
Hoisting
Hoisting es un concepto simple pero importante. En términos simples es un proceso en el que Javascript lee todas las declaraciones de variables y funciones y las guarda como referencias antes de ejecutar el código. Si quieres ver el tema con un ejemplo puedes revisar que es hoisting en solo 5 minutos.
Autoinicialización
La autoinicialización por su parte es precisamente como su nombre lo dice el proceso en el que se le asigna un valor inicial de manera automática a una variable declarada.
Scopes
Javascript maneja 3 scopes diferentes: global, local y de bloque. No ahóndare en este tema en este artículo pero si no estas familiarizado deberías revisar estos artículos: Las 3 partes del Lexical Scope y 2 ejemplos para entender la diferencia entre var
y let
.
Dicho esto todo lo que debes saber por el momento es que lo que se declara con var
se asigna al scope global y lo que esta declarada con let
/const
no.
let vs var
Hoisting | Auto Inicialización | Scope | |
var | Si | Si | Global |
let | Si | Si | Bloque |
var
fue creado dentro del objeto global e inicializado con el valor de undefined
. Esto es lo que hace que el código se ejecute correctamente pero se imprima el valor con undefined
.
let
por su parte no fie creado dentro del objeto global y por lo tanto aunque si haya sido incializado con undefined
, Javascript no lo encuentra en esa etapa de la ejecución. Esto es comportamiento esperado ya que let
/const
fueron hechos para lanzar un error en estos casos.
La asignación al texto “computersDB” se hace hasta después de haber mandado a llamar a imprimir en consola. Por esto mismo ninguno de los ejemplos imprime ese valor.
…¿y TDZ?
Todo este rollo y estos conceptos para explicar que es el TDZ.
Entonces..¿qué es? Pues de hecho ya lo viste, solo que no te dije. ¿Te diste cuenta?
showData()
let databaseName = 'computersDB'
function showData() {
console.log(`Querying data from ${databaseName}`) // ReferenceError: Cannot access 'databaseName' before initialization
}
El 2do ejemplo el error de inicialización con let es el ejemplo de lo que es un TDZ. La variable estaba declarada y era reconocida por Javascript pero su valor era inaccesible y por eso nos arrojó el error.
Si lo piensas a pesar de lo estrafalario del nombre es también bastante apropiado. Precisamente puedes imaginarte que durante ese breve período de tiempo la variable esta en una zona que es como una especie de limbo en el que no puedes acceder a ella.
¿Y qué no pudimos solo poner ese ejemplo y ya? Hubiera sido mejor que perder el tiempo con lo de hoisting y la auto incialización. Bueno la respuesta es NO.
Para entender bien que es el TDZ también debes tener muy claro que no lo es y en que escenarios puede o no ocurrir. Eso es lo qué te hará un buen desarrollador.
Porsupuesto nunca deberías escribir código como el de los ejemplos anteriores. Estos solo sirven para demostrar precisamente que es el TDZ.
Por claridad tanto para los demás miembros de tu equipo como para tu “yo” futuro deberías apegarte a las buenas prácticas. Y en esas buenas prácticas las declaraciones de variables deben estar antes del bloque de código que va a hacer uso de ellas.
Obviamente la solución para arreglar nuestro código es extremadamente compleja y laboriosa. Por lo que la pondré aquí en caso de que no se te ocurra como hacerle.
let databaseName = 'computersDB'
function showData() {
console.log(`Querying data from ${databaseName}`) // Querying data from computersDB
}
showData()
¡Perfecto! Ahora tal vez sea bueno que sepas exactamente como funcionan let
/const
y var
a la hora de que JS asigna los valores y ejecuta el código. Pero para eso tienes que meterte al tema de los Execution Context.