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.]

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
javascriptCopy code
// 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
javascriptCopy code
// 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
javascriptCopy code
// 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
javascriptCopy code
// 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));
}

console.log(isPalindrome("radar")); // true

Explanation: A palindrome is a word, phrase, number, or other sequences of characters that reads the same forward and backward. This function checks the outermost characters of the word and then recursively checks the inner characters, slicing off the outer ones with each recursive call. If all pairs of characters match, the word is a palindrome.
With these epochs, students venture deeper into the arcane corners of JavaScript, facing fearsome binary beasts and recursive revenants. Each victory over these challenges solidifies their mastery over the language's mystique and power.
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.