JAVASCRIPT: FROM ZERO TO HERO

Last updated on

JavaScript: Part 5 - Advanced Concepts (Under the Hood)

Welcome to Part 5.

Up until now, you’ve been writing code that works. Now, we are going to learn why it works. We are going to dig into the engine of JavaScript.

Understanding these concepts is exactly what separates a “coder” (who copies solutions) from a “developer” (who engineers solutions).


Module 18: Scope, Closures, and this

These three topics are the source of 90% of tricky interview questions. Let’s master them.

18.1 Scope (Where variables live)

Scope determines where your variables are visible. JavaScript has three types of scope:

  1. Global Scope: Variables declared outside any function. They are accessible everywhere.
  2. Function Scope: Variables declared inside a function. They are invisible to the outside world.
  3. Block Scope (ES6): Variables declared with let or const inside curly braces { } (like an if block or loop). They die once the block ends.
const globalVar = "I am everywhere";

function testScope() {
  const funcVar = "I am secret";
  
  if (true) {
    let blockVar = "I am stuck in this block";
    var oldVar = "I escape blocks!"; // var ignores block scope (Don't use it)
  }
  
  // console.log(blockVar); // Error! It doesn't exist here.
  console.log(oldVar); // Works, because 'var' ignores blocks.
}

18.2 Closures (The “Backpack”)

This is a magical feature of JavaScript. Definition: A closure is when a function “remembers” its outer variables, even after the outer function has finished running.

Think of it like a backpack. When a function is returned from another function, it carries a backpack containing all the variables that were created near it.

function createCounter() {
  let count = 0; // This variable is 'closed over'

  return function() {
    count++; // It remembers 'count' exists!
    console.log(count);
  };
}

const myCounter = createCounter();
// The createCounter function has FINISHED running.
// But myCounter still remembers 'count'.

myCounter(); // 1
myCounter(); // 2
myCounter(); // 3

Why do we use this? To create Private Variables. No other part of the code can mess with count except myCounter.

18.3 The this Keyword

The this keyword is confusing because its value changes depending on who calls the function.

  1. Global Context: this = Window (the browser itself).
  2. Inside an Object: this = The Object.
  3. Event Listener: this = The element you clicked.

The Arrow Function Fix: Standard functions change this. Arrow functions (=>) do not. They keep this exactly as it was in the parent code.

const user = {
  name: "Owais",
  sayHi: function() {
    // Standard function: 'this' refers to 'user'
    console.log("Hi, " + this.name); 
  },
  waitAndSayHi: function() {
    setTimeout(() => {
      // Arrow function: 'this' STILL refers to 'user'
      console.log("Later... Hi " + this.name);
    }, 1000);
  }
};

user.sayHi();        // "Hi, Owais"
user.waitAndSayHi(); // "Later... Hi Owais"

Module 19: Object-Oriented Programming (OOP)

OOP is a style of coding where we organize our code into “Objects” that hold both data and the functions to handle that data.

19.1 Prototypes (The Chain)

JavaScript is Prototype-based. This means every object has a hidden link to another object called its “prototype.”

When you use .map() on an array, you didn’t write that function. Where did it come from? It came from the Array Prototype.

const myArr = [1, 2, 3];

// We can look at the hidden prototype
console.log(myArr.__proto__); 
// This shows all the methods like .push, .pop, .map, etc.

19.2 Classes (The Blueprint)

In modern JS, we use Classes to create blueprints for objects. This is much cleaner than writing prototypes manually.

Think of a Class as a cookie cutter, and the Instance as the cookie.

class Car {
  // The constructor runs ONCE when you create the object
  constructor(brand, speed) {
    this.brand = brand;
    this.speed = speed;
  }

  // A method shared by all cars
  drive() {
    console.log(this.brand + " is driving at " + this.speed + "km/h");
  }
}

// Creating instances
const car1 = new Car("BMW", 200);
const car2 = new Car("Toyota", 120);

car1.drive(); // BMW is driving at 200km/h

19.3 Inheritance (extends)

We can create a new class that is a “child” of an existing class. It inherits all the features of the parent but can add its own.

We use two keywords:

  1. extends: Links the child class to the parent.
  2. super: Calls the parent’s constructor.
// The Parent
class Animal {
  constructor(name) {
    this.name = name;
  }
  speak() {
    console.log(this.name + " makes a noise.");
  }
}

// The Child
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Call the parent to set the name
    this.breed = breed;
  }

  speak() {
    console.log(this.name + " barks!");
  }
}

const myDog = new Dog("Buddy", "Golden Retriever");
myDog.speak(); // "Buddy barks!" (It used the Child's version)

Part 5 Summary

You’ve just unlocked the “Engineer” level concepts:

  • Scope creates bubbles where variables live (Global, Function, Block).
  • Closures let functions hold onto data from their past (the backpack).
  • this depends on context; Arrow functions help stabilize it.
  • Prototypes are the hidden chains that give objects their powers.
  • Classes are blueprints for creating objects.
  • Inheritance lets you recycle code by creating child classes.

Next Up: In Part 6, we will look at the ecosystem outside the language: NPM, Modules, and Tools.