user7393973
user7393973

Reputation: 2440

Why does the temporal dead zone exist?

I know what is the temporal dead zone (TDZ), but I can't understand the purpose of its existence.

Can anyone explain to me why it was created?

What is the logic of it raising a ReferenceError or a SyntaxError instead of returning undefined?

ReferenceError:

console.log(typeof foo);
let foo;

SyntaxError:

let foo;
let foo;

Undefined:

console.log(typeof foo);

Upvotes: 9

Views: 986

Answers (4)

Chris Baker
Chris Baker

Reputation: 50592

If you examine the documentation for those exceptions you linked in your question, you can see the why, which I don't think has been addressed here.

A side note first: it bears mentioning that exceptions didn't exist in ECMAScript until edition 3. var obviously predates this language feature by a lot. why var and let are different is largely due to the fact that exceptions were available when let was introduced, and not when var was introduced. That history underlines all of this. 1999 was a busy year, also!

Anyway, into the gears. Look what happens here:

// example A
(function () {
console.log(somethingUndefined - 1);
var somethingUndefined;
console.log('another operation');
})();

An error, then the code plows on. Can we catch this and deal with it?

// example B
(function () {
try {
    console.log(somethingUndefined - 1);
    var somethingUndefined = 50;
    console.log('another operation');
} catch (e) {
    console.log('I want to deal with problems here');
    return;
}

console.log('plowing on');
})();

Nope.

Behind the scenes, this code looks like this:

// example C
(function () {
var somethingUndefined = undefined;
try {
    console.log(somethingUndefined - 1);
    somethingUndefined = 50;
    console.log('another operation');
} catch (e) {
    console.log('I want to deal with problems here');
    return;
}

console.log('plowing on');
})();

There is no "temporal dead zone" because there is no point in example B & C where the variable somethingUndefined is not something. It's a typeof "undefined", somethingUndefined === undefined, that's not nothing. Then it's 50, but too late to be useful. Useful or not, we can use it to do things because it has a value. Line 1, line 8, it always has some value. Compare the difference in output here:

// example D
(function () {
try {
    console.log(somethingUndeclared - 1);
    console.log('another operation');
} catch (e) {
    console.log('I want to deal with problems here');
    console.log(e);
}
})();

At every point in example D, somethingUndeclared is nothing. It's an always dead zone. I am not sure which ECMAScript edition the above code began to throw exceptions, but it's less breaking for it to do so because this situation is always going to be an error.

Unless in some other part of the same scope OR the parent scopes somethingUndeclared was defined, probably for some other purpose. Or maybe the code var somethingUndeclared; is laying around somewhere in the scope or the parent scopes for some reason, so there's no exception thrown in anoy of the code above. That's the argument for using let or const almost exclusively.

When you use let or const, there is a "dead zone," a point in time when it isn't anything, then it will be something. It isn't undefined, it's an exception waiting to happen. Until the let statement is reached, it acts like an undeclared variable, throwing an exception, then at the declaration line it acts like a var with a value. It is a blending of the behavior of var with the outcome of an undeclared variable.

// example D
(function () { // temporal dead zone, something is nothing
try { // temporal dead zone, something is nothing
    console.log(something - 1); // exceptional behavior! temporal dead zone
    let something  = 50; // temporal live zone begins here!
    console.log('another operation');
} catch (e) {
    console.log('I want to deal with problems here'); // you could call this a dead zone
    console.log(e); // dead
}
// dead
})(); // way dead

Which moves us on to the begged question, "why not pull off the bandaid, deal with the BC break for the sake of consistency in the language?" The answer to that is because good enough is usually good enough. There are millions of lines of javascript managing accordions and image galleries and other UI bells and whistles. One opts-in to a more mature set of language features when ECMAScript is filling mission-critical roles. Or you just don't like sloppy code for your sweet tab navigation. Consistency in the language is not worth breaking all of the code out there that is good enough to do what it needs to do most of the time. Imagine the mountains of good enough that have accumulated since it was even possible to throw an exception in this language ('99).

When we need it to do what it is supposed to do all of the time, we can harness these features. When it would be a catastrophe for undefined = 100; to be in some file in the project, we can opt-in to exceptions. When the progressive enhancement fails and we get no tooltips, we have a less-nice UX than we'd hoped for. var and let have different histories and different weight on their shoulders, so they're likely to always act differently.

Upvotes: 0

Domino
Domino

Reputation: 6768

In some languages, a variable comes into existence the moment its declaration is encountered, but in JavaScript, a variable comes into existence the moment execution enters its scope: the surrounding function for var variables and arguments, the surrounding block for let and const variables.

Consider the following code, which is intentionally poorly written:

var color = 'blue';

function hedgehog (superSonic) {
  color = 'black';
  if (superSonic) {
    var color = 'gold';
  }
  console.log(color);
}

hedgehog(false);    // black
console.log(color); // blue

You might expect the first line of the hedgehog function to set the outer variable to 'black', but because there's a var declaration for a color variable within the function, the outer variable is actually left unchanged. All var declarations inside a function are taken into account the moment the function is called. So even though the line var color = 'gold'; is never executed, the inner variable still exists (with the initial value undefined) and the first line of the function assigns to it.

It's best to place declarations for variables before you use them to avoid confusion, but JavaScript didn't originally enforce this with its var variables. It even allowed the same variable to be declared in more than one place. One benefit of these relaxed rules is illustrated here:

function setGameMode (mode) {
  if (mode === 'peaceful') {
    var count = 0;
    for (var i = 0; i < mobs.length; ++i) {
      if (mobs[i] instanceof EvilMob) {
        mobs[i].despawn();
        ++count;
      }
    }
  } else {
    var count = 0;
    for (var i = 0; i < mobs.length; ++i) {
      if (mobs[i] instanceof NiceMob) {
          mobs[i].despawn();
          ++count;
      }
    }
  }
  console.log(`Removed ${count} out of ${i} mobs.`);
}

Here, i and count are declared twice in the same function, but JavaScript won't complain. There is only one variable with each name, and we can log them even outside of the conditional block.

Block-scoped variables created with let and const were added much later to the language. They behave similarly to var in that they shadow outer scope variables the moment their scope is entered, but unlike var variables they are considered unusable before their declaration is encountered, and declaring them twice is a syntax error (which can be caught when parsing code, long before its execution).

Trying to access a let or const variable prior to its declaration throws an error, which prevents mistakenly using an inner scope variable accidentally.

let color = 'blue';
function hedgehog (superSonic) {
  if (superSonic) {
    color = 'gold'; // ReferenceError if executed!
  }
  let color = 'black';
  return color;
}

Finally, typeof someundefinedvar evaluates to "undefined" when given an identifier that doesn't correspond to an existing variable. This lets you check if a global variable was declared by an external script. If a block-scope variable with that name is declared, the special behavior of typeof doesn't trigger, and therefore it tries to check the type of the value in that variable, and throws a ReferenceError if the variable is still in TDZ.

Here's an example of code where this could help us catch a bug:

let jQuery;
if ((typeof $) !== "undefined") {
  jQuery = $;
}
var $ = s => document.querySelectorAll(s);

In this code, the var $ declaration at the bottom shadows any global variable $ that might have previously existed, so typeof $ will always return "undefined" and the block will never execute. If we had used let instead of var, then a ReferenceError would be thrown by typeof $ and we would notice our mistake.

Upvotes: 4

Bergi
Bergi

Reputation: 664346

Using a variable before initialising it is always a mistake. Making this an error is reasonable as it helps the developer to notice their mistake and be able to fix it. The behaviour of var starting as undefined caused too many problems already, and they would have gotten worse if similar semantics were used for const variables or with static type annotations.

Upvotes: 5

Sudhanshu Jain
Sudhanshu Jain

Reputation: 534

  1. console.log(typeof a); let a;

In above statement you didn't defined variable a and you are using it. In java script, it is necessary to defined a variable. that variable is a reference of your value So that's why is showing you an undefined reference error.

  1. let a; let a;

In 2nd statement you have defined a variable two time. js don't allow you to defined any variable more then one time into same block scope.

  1. console.log(typeof foo);

    In 3rd statement, As it is showing an error in first function that your variable is not defined. BY default it is undefined. and you can use a undefined variable.

Upvotes: 0

Related Questions