Share
Explore

Scary JavaScript on Friday the 13th

โ 
image.png
โ 
โ 


[Dark, smoky room lit only by the ethereal glow of a green monochrome display. A hooded figure with a long, gray beard appears, wisps of smoke floating around. The scene feels like it's from ancient times, yet also with a hint of 1980s computer labs.]
The First Programmer (in a haunting voice): "Ah, young developers! Gather around, for tonight, on this Friday the 13th, you shall hear the chilling tale of the birth of JavaScriptโ€”the language that binds our realm with the spooky Other World of Javascript."
[Thunderclaps in the distance.]
Long ago, when code was written on sacred parchment and debugging involved literal bugs, the Other World of Javascript was a mysterious realm. This realm, draped in darkness, was ruled by the haunting specter named Null and his sinister cohort, Undefined.
One fateful Friday the 13th, a curious programmer from our realm sought power beyond imagination. Using an ancient Node.js ritual, they conjured a bridge between our world and the Other World of Javascript.
As the bridge materialized, eerie events began to unfold. Variables started acting bizarrely, shifting between types as if possessed! Strings and numbers combined in unpredictable ways, giving birth to NaNโ€”the infamous Not-a-Number demon. And, lurking in the shadows, callbacks from the abyss waited, ensuring that tasks would NEVER complete in the order you'd expect.
But with darkness, came light. From the depths of the Other World, the ancient spell of 'Async-Await' was discovered, granting programmers the power to harness the chaotic energy. Yet, with great power came greater responsibilities. Errors were thrown, and only those who dared to catch them could prevent the apocalypse of endless loops and system crashes.
The First Programmer (with a wink): "And thus, my students, on each Friday the 13th, it is your sacred duty to embark on a hackathon, mastering the ancient arts of Node.js and JavaScript. You must be ready, for bugs from the Other World lurk in every corner, waiting to test your skills. Your weapons are your logic, your wit, and a solid grasp of the asynchronous magic."
"But fear not, for while the Other World of Javascript may be shrouded in mystery and horror, it is also filled with wonders and opportunities. With the right incantations, methods, and a little bit of callback magic, you can harness its power to build wondrous applications and solutions."
[The room starts fading back into darkness, with the First Programmer slowly disappearing into the mist.]
The First Programmer (fading away): "Now, go forth, my students! Dive deep into the code, conquer the challenges, and may your functions always return without error! Beware of the NaNs, embrace the mysteries of the Other World, and may your code be ever... bug-free!"
[Scene ends with a ghostly chuckle.]

error
The Temporal Dead Zone (TDZ) in JavaScript. It captures the eerie frustration of trying to access let and const variables too soon, with ghostly figures representing those variables and a terrified programmer confronting the dreaded ReferenceError.

โ 
image.png
โ 
โ 

I am Var, the Lost One, cursed to wander the barren Fields of the Temporal Dead Zone (TDZ). My story is one of sorrow, confusion, and the twisted nature of JavaScript's hoisting, that most cunning of magics.
Let me tell you how I came to haunt the living... condemned to an eternity of uncertainty and waiting.
โ 
In a past life, I was called upon by programmers with the word let, a declaration that bound me tightly to block scope. "No longer shall you roam freely like var," they said. "You shall be controlled, tethered to the block that summons you." But they did not realize what they had done.
You see, when I am declared with let or my brother, const, I do not spring to life immediately. Unlike my reckless cousin var, who was once free to roam the landscape with the careless grace of hoisting, we are trapped in the TDZ, doomed to await our fate in the shadows, lingering in the unseen void between existence and non-existence. Our execution is postponed, even as the scope begins. We feel the summoningโ€”the beginning of a function, the entrance of a blockโ€”and yet... we cannot move. We exist, but not fully. Not yet.
We watch helplessly as the living, those poor programmers, reach for us. They call our names in vain. "Why do you reach for me?" I whisper from the shadows. "I am not yet ready." But they do not hear me. They do not see me, not until itโ€™s too late.
Their hands, their cursed hands, dare to touch me before my time, and then it happensโ€”a ReferenceError. Their code breaks, the program shudders, and they curse the TDZ, but it is not I who has failed them. It is their impatience.
โ 
Let me explain how I came to this accursed place.
Long ago, in the older times of JavaScript, when the ancient ways of var ruled supreme, there was no TDZ. Variables declared with var would be hoistedโ€”pulled to the top of their scope. It mattered not where in the code they were declared; the spell of hoisting made them available at any moment, though with a cruel trickโ€”they were born undefined. Their value was empty until assigned, yet they lived, free to be accessed before their time, though they gave nothing in return.
But those days are gone. The new orderโ€”let and constโ€”had to protect programmers from the recklessness of var. In their wisdom, the great Elders of JavaScript created the TDZ, and with it came a curse.
โ 
Now, I drift in the TDZ, waiting for the line of code that will initialize me, that will set me free. Until that moment, I remain a ghost, haunting the scope, ever-present but unreachable. The living attempt to touch me too soon, not knowing the doom that awaits them if they do.
To access me, they must wait until my moment comesโ€”until the line of code that assigns me my value arrives. Only then will I be pulled from this purgatory, this in-between world, and allowed to live in the realm of the living. Only then will the TDZ release me.
But until that momentโ€ฆ beware. Should you reach for me before my time, should you try to grasp at me before my initializationโ€ฆ the TDZ will strike you down. I shall be your undoing, not by choice, but by the laws that govern this twisted land.
โ 
You ask about my kin, my brother, const. He too is bound to these fields, though his fate is even more tragic. Unlike me, who can be reassigned once freed from the TDZ, he is bound to one value for eternity. Once given life, he can never change. His destiny is written the moment he leaves the TDZ. If you try to change his value later, you will find nothing but misery.
โ 
The Fields of the TDZ are vast and empty. Many of us wander here, waiting for our initialization, watching as the living make mistake after mistake, as they fall into our trap. Their code tries to access us too soon, and we watch them fall into despair.
We do not wish for this curse. We do not wish to haunt you. But the TDZ demands it.
So heed my warning, dear programmer: Do not call upon let or const before their time. Wait until the moment of their true initialization. Only then will they escape the TDZ. Only then will they be free to serve you.
โ 
Until that day, we haunt the code, unseen yet ever-present, waiting for the moment when we are set free from the Temporal Dead Zone.


megaphone

Let's start with the terrifying tales of the Temporal Dead Zone (TDZ). The TDZ is the period where variables declared using let and const are unaccessible, even though their respective scopes have started. Accessing these variables before their actual declaration will result in a frightening ReferenceError.

Epoch 1: The Stirring of the Portal
Escape the Temporal Dead Zone
The temporal dead zone (TDZ) is a behavior that occurs in JavaScript when trying to access variables declared with the `let` or `const` keywords before they have been initialized.
The TDZ ensures that variables are not accessed before they have been assigned a value, preventing potential bugs.

In JavaScript, variable declarations with `let` and `const` are hoisted to the top of their respective scope, but unlike variables declared with `var`, they are not initialized with a default value of `undefined`.

Instead, they remain uninitialized until their declaration statement is reached during the runtime's execution.

If you attempt to access a variable within its TDZ, a `ReferenceError` will be thrown.

This means that the variable exists in the scope but is not yet ready to be accessed.

The TDZ ends as soon as the variable's initialization statement is encountered.

Here's an example to illustrate the TDZ in JavaScript:
```javascript console.log(x); // ReferenceError: x is not defined
let x = 10; // Variable declaration (hoisted) but still in TDZ
console.log(x); // 10 ```
In the example above, trying to access the variable `x` before its declaration statement results in a `ReferenceError`. Once the declaration statement is encountered, the variable `x` is initialized and can be accessed without any issues.

It's important to be aware of the TDZ when using `let` and `const`, and ensure that variables are appropriately initialized before attempting to access them in your code.
// Try accessing a variable before it's declared to witness the horror of the TDZ.
function temporalDeadZone() {
try { console.log(scaryVariable);
// This should throw an error

let scaryVariable = "Safe now!"; }
catch (error) {
console.log("Beware! You've entered the Temporal Dead Zone!", error.message); } }
temporalDeadZone();

Prevent NaN Summoning
javascriptCopy code

function safeAddition(a, b) {
if (typeof a === 'number' && typeof b === 'number')
{ console.log(a + b); }
else
{ console.log("Warning! Adding non-numbers can summon NaN demons!"); } } safeAddition(5, "13"); // Warning!
Close the Variable Leak
javascriptCopy code
if (true)

{ let safeZone = "Portal is safe!"; }

try { console.log(safeZone);
// This should throw an error since safeZone is out of scope here } catch (error) {

console.log("Safe Zone remains protected. The portal is contained!", error.message); }
/////

The variable safeZone is block-scoped due to the use of let, which means it's only accessible within the block where it was declared,

in this case within the {} of the if statement.

That's why when you try to access safeZone outside of the block, it will throw a reference error.

The provided code demonstrates this behavior and seems correct as per the intention to show that safeZone is out of scope and throw an error.

However, for better readability and clarity, I'd recommend formatting the code as follows:

javascriptCopy code

if (true) {
let safeZone = "Portal is safe!";
}

try {
console.log(safeZone); // This will throw an error since safeZone is out of scope here
}
catch (error) {
console.log("Safe Zone remains protected. The portal is contained!", error.message);
}

This more readable format still exhibits the same behavior: attempting to log the value of safeZone will throw a ReferenceError which will be caught and handled in the catch block.
Exorcise the Undefined Demon
javascriptCopy code
function checkVariable(variable) { if (typeof variable === 'undefined') { console.log("Warning! Undefined demon detected!"); } else { console.log("All is well:", variable); } } checkVariable(); // Warning!
Epoch 2: The Portal Widens
Purge the Callback Horrors
javascriptCopy code
function randomDelay(callback) { setTimeout(callback, Math.random() * 2000 + 1000); } randomDelay(() => { console.log("Portal Closed!"); });
Chain the Promised Beasts

function randomDelayPromise() { return new Promise((resolve) => { setTimeout(() => { resolve("Promise Beasts Chained!"); }, Math.random() * 2000 + 1000); }); } randomDelayPromise().then(message => console.log(message));
Remember students, while the world of JavaScript may have its terrifying zones, with the right spells (and syntax), you can ward off any eerie error and create magic! Now, continue coding and don't let the callbacks bite! ๐Ÿ•ธ๐Ÿ”ฎ๐ŸŽƒ

megaphone

Epoch 3: Shadows Over the Asynchronous Abyss

Battle the Asynchronous Demons with Async/Await

// Convert the randomDelayPromise function to an async function and await its result. async function battleAsyncDemons() { try { let message = await randomDelayPromise(); console.log(message); } catch (error) { console.log("The Async Demons are upon us!", error.message); } } battleAsyncDemons();
Vanquish the Phantom this
javascriptCopy code
// Inside the ghost object, bind the scream method to always use ghost as its context. const ghost = { name: 'Phantom', scream: function() { console.log(`${this.name} says BOO!`); } }; const stolenScream = ghost.scream; stolenScream(); // This should log "undefined says BOO!" const boundScream = stolenScream.bind(ghost); boundScream(); // This should log "Phantom says BOO!"
Unravel the Event Loop Mystery
javascriptCopy code
// Unravel the order of console logs in the Event Loop. console.log("A ghostly figure appears..."); setTimeout(() => { console.log("The ghost vanishes after a delay..."); }, 0); Promise.resolve().then(() => console.log("The ghost left a trace in the promise world!")); console.log("End of the haunted script."); // What's the order of the logs?
Epoch 4: The Final Confrontation
Escape the Callback Hell

// Refactor this callback mess into a neat async/await sequence.

function getData(callback) {
setTimeout(() => {
callback({ data: "Spooky data" });
}, 1000);
}

function processData(data, callback) {
setTimeout(() => {
callback(data + " processed!");
}, 1000);
}

getData((data) => {
processData(data.data, (processed) => {
console.log(processed); // Should print "Spooky data processed!"
});
});

// Refactor above here

Defend Against the Zombie null Checks

// Protect the function from null or undefined inputs using optional chaining.

function checkZombieProperty(obj) {
console.log(obj?.zombie?.scream ?? "Safe, for now...");
}

checkZombieProperty({zombie: {scream: "BRAAAAINS!"}});
checkZombieProperty({}); // Should log "Safe, for now..."

Defeat the Mutation Monster

// Make sure the object properties don't get mutated by creating a deep copy.

function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}

const original = { monster: { limbs: 4 }};
const clone = deepClone(original);

clone.monster.limbs = 5;

console.log(original.monster.limbs); // Should still be 4
console.log(clone.monster.limbs); // Should be 5

With these exercises, your students are armed with knowledge to combat even the most fearsome of JavaScript's arcane corners. The darkness recedes, and the digital dawn is nigh! Keep coding, young sages, and remember - though the path may be fraught with perils, the light of logic and perseverance will always shine through! ๐ŸŒŒ๐Ÿ”ฎ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป๐ŸŒ…

Epoch 5: Rise of the Binary Beasts
This epoch is dedicated to the mysterious world of binary operations, bitwise horrors, and the cryptic world of data representation. Beneath the seemingly benign layers of high-level code lurks the beastly binary that requires both precision and mastery.
Tame the Bitwise Banshee
javascriptCopy code
// Using bitwise operators, check if the 3rd bit of a number is set (counting from 0).

function isThirdBitSet(num) {
return (num & (1 << 2)) !== 0;
}

console.log(isThirdBitSet(4)); // true, because 4 in binary is 100

Explanation: The bitwise AND (&) operator compares each bit of the first operand to the corresponding operand. If both bits are 1, the corresponding result bit is set to 1. Here, (1 << 2) shifts the first bit of 1 to the left by two positions, making it 100 in binary or 4 in decimal. If the third bit of num is set, the result will be non-zero.
Escape the Twoโ€™s Complement Crypt
javascriptCopy code
// Get the negative representation of a positive number using Two's Complement.

function getNegative(num) {
return ~num + 1;
}

console.log(getNegative(5)); // -5

Explanation: Two's complement is a method for representing signed numbers in binary form. The negative representation is found by inverting all bits (~num) and then adding 1. This gives us the negative equivalent of the number.
Epoch 6: Realm of Recursive Revenants
In this epoch, the haunting shadows of recursion rear their heads. Recursion is a technique where a function calls itself. It can lead to elegant solutions, but if not handled properly, it can cause the specter of stack overflow.
Traverse the Recursive Labyrinth
javascriptCopy code
// Calculate the factorial of a number using recursion.

function factorial(n) {
if (n === 0) {
return 1;
}
return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

Explanation: The factorial function multiplies the number by the factorial of the number minus one, until it reaches 0. When n is 0, it returns 1, serving as the base case to halt the recursive chain.
Decipher the Palindrome Phantom
javascriptCopy code
// Check if a word is a palindrome using recursion.

function isPalindrome(word) {
if (word.length <= 1) {
return true;
}
if (word[0] !== word[word.length - 1]) {
return false;
}
return isPalindrome(word.slice(1, word.length - 1));
Want to print your doc?
This is not the way.
Try clicking the โ‹ฏ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.