DEV Community

Mitu M
Mitu M

Posted on

🔑 Core Concepts You Can’t Skip in JavaScript...(part-1)

1. Scopes & Closures

Scopes
Scope defines where variables are accessible.

Global scope → accessible everywhere

Function scope → accessible only inside the function

Block scope (let, const) → accessible only inside {}

Example of Scopes:

let globalVar = "I am Global"; // Global Scope

function scopeExample() {
  let functionVar = "I am Function Scoped"; // Function Scope

  if (true) {
    let blockVar = "I am Block Scoped"; // Block Scope
    console.log(globalVar);   // âś… Accessible
    console.log(functionVar); // âś… Accessible
    console.log(blockVar);    // âś… Accessible
  }

  // console.log(blockVar); // ❌ Error: blockVar not defined here
}

scopeExample();
Enter fullscreen mode Exit fullscreen mode

Closures

A closure happens when a function “remembers” variables from its outer scope, even after that scope has finished executing.

Example of Closure:

function outerFunction() {
  let count = 0; // Variable inside outer scope

  function innerFunction() {
    count++; // inner function still has access to 'count'
    console.log("Count:", count);
  }

  return innerFunction; // return inner function (closure)
}

const counter = outerFunction(); // outerFunction executes once

counter(); // Count: 1
counter(); // Count: 2
counter(); // Count: 3

Enter fullscreen mode Exit fullscreen mode

Here’s what’s happening:

  • outerFunction finishes execution. Normally, count would disappear.
  • But because innerFunction closes over count, it stays alive in memory.
  • Each time counter() is called, it updates the same count.

⚡ Use case of Closures:

  • Data privacy (like private variables)
  • Function factories
  • Maintaining state without global variables

let’s go practical. Closures are super handy when you want to simulate private variables in JavaScript (since JS doesn’t have private in the classic sense).

Here’s a real-world closure example of making a counter with private state:

function createCounter() {
  let count = 0; // private variable (not directly accessible outside)

  return {
    increment: function () {
      count++;
      console.log("Count:", count);
    },
    decrement: function () {
      count--;
      console.log("Count:", count);
    },
    getValue: function () {
      return count; // expose safely
    }
  };
}

// Usage
const counter = createCounter();

counter.increment(); // Count: 1
counter.increment(); // Count: 2
counter.decrement(); // Count: 1
console.log("Final Value:", counter.getValue()); // Final Value: 1

// Direct access is impossible:
console.log(counter.count); // ❌ undefined (private!)

Enter fullscreen mode Exit fullscreen mode

Why this works:

count is inside the createCounter scope.

The returned object’s functions close over count, keeping it alive.

Outside code can’t touch count directly — only through the methods we expose.

👉 This pattern is widely used in module design, authentication tokens, shopping carts, etc. to keep sensitive data safe.

2. this Keyword & Context

Objectives

  • Difference between: Global this, Object method this, Arrow function this (lexical binding).
  • Why? Angular uses class components heavily; misunderstanding this leads to broken method bindings.

1. Global this

In strict mode (default in ES6+), this in the global scope is undefined.
In non-strict mode, this is the global object (window in browsers, global in Node).

console.log(this); 
// In browser: window (non-strict mode)
// In strict mode: undefined

Enter fullscreen mode Exit fullscreen mode

2. Object Method this

When a method is called on an object, this refers to the object itself.

const user = {
  name: "Mitu",
  greet: function () {
    console.log("Hello, " + this.name);
  }
};

user.greet(); // âś… "Hello, Mitu"

Enter fullscreen mode Exit fullscreen mode

But if you detach the method, thisbreaks:

const detached = user.greet;
detached(); 
// ❌ In strict mode: undefined
// ❌ In browser non-strict: "Hello, undefined" (because `this` = window)

Enter fullscreen mode Exit fullscreen mode

3. Arrow Function this (Lexical Binding)

Arrow functions don’t have their own this. Instead, they lexically bind this from their surrounding scope.

const user = {
  name: "Mitu",
  greet: () => {
    console.log("Hello, " + this.name);
  }
};

user.greet(); // ❌ "Hello, undefined"

Enter fullscreen mode Exit fullscreen mode

Why?
Because this in an arrow function doesn’t point to the object — it points to whatever this was in the scope where the function was created (in this case, global this).

âś… Why Angular Uses Arrow Functions in Callbacks

In Angular, class components heavily rely on this to access properties/methods.

Problem: Losing this binding

@Component({...})
export class AppComponent {
  name = "Mitu";

  ngOnInit() {
    setTimeout(function () {
      console.log(this.name); 
      // ❌ ERROR: Cannot read property 'name' of undefined
    }, 1000);
  }
}

Enter fullscreen mode Exit fullscreen mode

Here, this inside setTimeout is not the class, but the global context.

Solution: Use Arrow Functions (lexical this)

@Component({...})
export class AppComponent {
  name = "Mitu";

  ngOnInit() {
    setTimeout(() => {
      console.log(this.name); 
      // âś… "Mitu"
    }, 1000);
  }
}

Enter fullscreen mode Exit fullscreen mode

🔑 Quick Summary

  • Global this → window (non-strict) or undefined (strict).
  • Object Method this → depends on the caller (can break if detached).
  • Arrow Function this → lexically bound; inherits this from surrounding scope.

👉 That’s why Angular encourages arrow functions in callbacks — to preserve this and avoid method-binding issues.

Top comments (0)