Loading...
Interview QuestionsJavaScript

JavaScript – Quick Recap

Summary Quick Revision

use strict

In JavaScript, "use strict" is a directive introduced in ECMAScript 5 (ES5) that enables Strict Mode, a restricted variant of JavaScript.

Closures
A closure is a function that retains access to variables from its outer scope after that outer function has returned, enabling private state and encapsulation.

IIFE
An Immediately Invoked Function Expression runs as soon as it is defined and creates a private scope to avoid polluting the global namespace.

call, apply and bind
call invokes a function with a specified this and individual arguments; apply invokes with a specified this and an array of arguments; bind returns a new function with this (and optionally arguments) permanently bound.

this
The value of this depends on how a function is called: object method, standalone function, arrow function (lexical this), constructor, or event handler.

Hoisting
Function declarations and var declarations are hoisted to the top of their scope; let and const are not accessible before their declaration (temporal dead zone).

Temporal Dead Zone
The period between entering scope and the let/const declaration where accessing the variable throws a ReferenceError.

Event Propagation
Events travel through three phases: capturing, target, and bubbling. Handlers can run during capture or bubble and can stop propagation.

  • Capturing Phase (Trickling): The event starts from the window and travels downward through ancestors until it reaches the target element.
  • Target Phase: The event reaches the actual element that was interacted with (e.g., a button).
  • Bubbling Phase: The event “bubbles” back up from the target element through its ancestors to the window

Function Types – Ways function can be defined
Function declarations, function expressions, arrow functions, generator functions, and async functions each have different hoisting, scoping, and this behavior.

Array Methods
Mutating methods: push, pop, splice; non‑mutating: map, filter, reduce, slice; modern helpers: flat, at, with.

DOM Methods
Selection, creation, attribute manipulation, event handling, and utilities such as getBoundingClientRect and scrollIntoView.

Promises vs Observables and RxJS
Promises produce a single future value and execute immediately; Observables produce streams, execute on subscription, and can be cancelled. RxJS operators like map, filter, switchMap, and mergeMap transform and control streams.

Tricky Coding Questions

console.log(a); var a = 10; # undefined
console.log(b); let b = 10; # ReferenceError
(function() {
var x = y = 5;
})();
console.log(typeof x); # undefine
console.log(typeof y); # number
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
}
# Output: 333
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
# Execution Order: Promise, setTimeout
console.log(0 == '0'); # true
console.log(0 === '0'); # false
console.log(10 + 2 + "9") # 129
console.log(NAN === NAN) # false
# How to access variable x (which is outside function) in function test?
x = 10;
function test(){
  let x = 20;
  console.log(this.x); # 10, accessed global variable x using this.
  console.log(x); # 20, local variable x
}
let x=1;
y = x++; # At this point: x=1, y=1
console.log(x,y); # 2, 1 
# Note: ++ operator first assigns existing value, then increases by one, so assign 1 to y and becomes 2 in next console.log statement)

Hoisting and Scoping (The “Undefined” Trap):

What is the output of console.log(a); var a = 10;?

    Answer: undefined. The declaration var a is hoisted to the top and initialized as undefined, but the assignment 10 stays in place.

    Temporal Dead Zone:

    What happens if you run console.log(b); let b = 10;?

     Answer: ReferenceError. Unlike varlet and const variables are hoisted but not initialized, placing them in a “temporal dead zone” until the declaration is reached.

    Shadowing in Functions:

    What is the output of this IIFE?

    (function() {
    var x = y = 5;
    })();
    console.log(typeof x);
    console.log(typeof y);

    Answer: undefined and numbery becomes a global variable because it is not declared with var, while x is local to the function scope.

    The Loop Closure Quirk:

    What does this code print?

    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i), 1000);
    }
    

    Answer: 3 3 3. Because var is function-scoped, all callbacks share the same i, which is 3 by the time they run. Using let fixes this by creating a new binding for each iteration.

    Microtasks vs. Macrotasks – Asynchronous Execution:

    In what order will “timeout” and “promise” print?

    setTimeout(() => console.log('timeout'), 0);
    Promise.resolve().then(() => console.log('promise'));
    

    Answer: “promise” then “timeout”. The Event Loop prioritizes the microtask queue (Promises) over the macrotask queue (setTimeout). 

    Type Coercion and Comparison

    1. Why does 0 == '0' return true, but 0 === '0' return false?

    Answer: == performs implicit type coercion to make types match before comparing, while === checks both value and type.

    2. What is the result of 10 + 2 + "9"?

    Answer: "129". JavaScript adds the first two numbers (12) and then concatenates the result with the string "9".

    3. What is the result of console.log(NaN === NaN)?

    Answer: falseNaN (Not-a-Number) is the only value in JavaScript that is not equal to itself.


    Detailed Notes and Examples

    use strict

    In JavaScript, "use strict" is a directive introduced in ECMAScript 5 (ES5) that enables Strict Mode, a restricted variant of JavaScript. It is designed to catch common coding mistakes, improve security, and allow JavaScript engines to perform better optimizations by eliminating some of the language’s “sloppy” behaviors. 

    How to Use It

    • Global Scope: Place "use strict"; at the very top of a script file to apply it to all code in that file.
    • Function Scope: Place it inside the body of a specific function to apply strict mode only within that function’s scope.
    • Automatic Activation: Strict mode is automatically enabled in JavaScript Modules (ES6 modules) and within JavaScript Classes

    Key Changes and Rules

    Strict mode changes previously accepted “bad syntax” into real errors: 

    • No Undeclared Variables: You cannot use a variable without declaring it (e.g., x = 10 will throw a ReferenceError instead of creating a global variable).
    • Unique Parameter Names: Functions cannot have duplicate parameter names (e.g., function sum(a, a, b) is not allowed).
    • Secure this: In regular functions, if this is not set by the call, it defaults to undefined rather than the global object (like window).
    • Restricted eval: Variables declared inside an eval() call no longer “leak” into the surrounding scope.
    • Read-Only Protections: Writing to a read-only property or deleting an undeletable property (like Object.prototype) throws an error instead of failing silently.
    • Forbidden Keywords: Certain words reserved for future versions (e.g., implementsinterfacepublicprivate) cannot be used as variable names. 

    Why Use It?

    1. Bug Prevention: It catches typos (like mistyped variable names) and structural mistakes early by throwing immediate errors.
    2. Performance: Engines can optimize strict mode code more effectively because it follows predictable rules.
    3. Security: It prevents access to potentially dangerous features and helps avoid accidental global variable pollution.
    4. Modern Standards: Using it ensures your code is compatible with modern JavaScript features like modules and classes, which rely on strict mode behavior by default. 

    Closures

    Explanation
    A closure is formed when an inner function retains access to variables from its outer function even after the outer function has finished executing. This enables private variables and persistent state.

    Example

    function counter() {
      let count = 0; // private
      return function () {
        count++;
        return count;
      };
    }
    
    const increment = counter();
    console.log(increment()); // 1
    console.log(increment()); // 2
    console.log(increment()); // 3
    

    Pattern

    function createUser(name) {
      let score = 0;
      return {
        getName() { return name; },
        getScore() { return score; },
        increment() { score++; return score; }
      };
    }
    

    IIFE

    Explanation
    An IIFE executes immediately and creates a private scope, preventing variables from leaking into the global scope.

    Example

    (function () {
      const secret = 'hidden';
      console.log('IIFE executed');
    })();
    

    Use cases
    Initialization code, module-like encapsulation in older environments, avoiding global collisions.


    call, apply and bind

    call — invoke immediately with individual arguments.
    apply — invoke immediately with arguments provided as an array.
    bind — return a new function with this and optional arguments bound; does not invoke.

    Examples

    function greet(greeting, punctuation) {
      console.log(`${greeting}, ${this.name}${punctuation}`);
    }
    const user = { name: "Lok" };
    greet.call(user, "Hello", "!"); // Hello, Lok!
    
    function sum(a, b, c) { return a + b + c; }
    const numbers = [1, 2, 3];
    console.log(sum.apply(null, numbers)); // 6
    
    const module = { x: 42, getX() { return this.x; } };
    const unboundGetX = module.getX;
    const boundGetX = unboundGetX.bind(module);
    console.log(boundGetX()); // 42
    
    function multiply(a, b) { return a * b; }
    const double = multiply.bind(null, 2);
    console.log(double(5)); // 10
    

    When to use

    • call for immediate invocation with known args.
    • apply when args are in an array or array-like.
    • bind for callbacks or partial application where this must be preserved.

    this Keyword

    Contexts

    • Object method: this is the object.
    • Standalone function: this is undefined in strict mode, global in non-strict.
    • Arrow function: this is inherited from the lexical scope.
    • Constructor: this is the new instance.
    • Event handler: this is the element that received the event (unless arrow function used).

    Examples

    const obj = { name: 'Lok', sayName() { console.log(this.name); } };
    obj.sayName(); // Lok
    
    function showThis() {
      'use strict';
      console.log(this); // undefined
    }
    showThis();
    
    const person = {
      name: 'Lok',
      regular: function() { console.log(this.name); }, // Lok
      arrow: () => { console.log(this); } // lexical this
    };
    person.regular();
    person.arrow();
    
    function Person(name) { this.name = name; }
    const p = new Person('Lok');
    console.log(p.name); // Lok
    

    Hoisting

    Explanation
    Hoisting moves declarations to the top of their scope at runtime. Function declarations and var declarations are hoisted; let and const are not accessible before their declaration.

    Examples

    // Function declaration hoisted
    sayHi(); // works
    function sayHi() { console.log('Hi'); }
    
    // var hoisted but undefined until assignment
    console.log(x); // undefined
    var x = 5;
    
    // let/const are not accessible before declaration
    console.log(y); // ReferenceError if uncommented
    let y = 10;
    

    Key points

    • Function declarations can be called before they appear in code.
    • var variables exist before their assignment but have value undefined.
    • let and const are in the temporal dead zone until declared.

    Temporal Dead Zone

    Explanation
    The temporal dead zone (TDZ) is the time between entering a scope and the let/const declaration. Accessing the variable in the TDZ throws a ReferenceError.

    Example

    {
      // console.log(a); // ReferenceError: Cannot access 'a' before initialization
      let a = 2;
      console.log(a); // 2
    }
    

    Practical note
    Use let and const to avoid accidental use of uninitialized variables; be aware that they are not hoisted in the same usable way as var.


    Function Types

    Function Declaration

    function sum(a, b) { return a + b; } // hoisted
    

    Function Expression

    const sum = function(a, b) { return a + b; }; // not hoisted
    const factorial = function fac(n) { return n <= 1 ? 1 : n * fac(n - 1); };
    

    Arrow Function

    const multiply = (a, b) => a * b; // lexical this, no arguments object
    

    Generator Function

    function* numberGen() {
      yield 1;
      yield 2;
    }
    const gen = numberGen();
    console.log(gen.next().value); // 1
    

    Async Function

    async function fetchData(url) {
      const res = await fetch(url);
      return res.json();
    }
    

    Array Methods

    Mutating

    const arr = [1, 2, 3];
    arr.push(4);
    arr.pop();
    arr.splice(1, 1, 99);
    

    Non‑mutating

    const nums = [1, 2, 3, 4];
    const doubled = nums.map(x => x * 2);
    const evens = nums.filter(x => x % 2 === 0);
    const sum = nums.reduce((acc, cur) => acc + cur, 0);
    

    Searching and reordering

    nums.includes(2);
    nums.find(x => x > 2);
    nums.sort();
    nums.reverse();
    nums.join('-');
    

    Modern helpers

    const nested = [1, [2, 3], [4, [5]]];
    nested.flat(2); // [1,2,3,4,5]
    
    const arr2 = [1, 2, 3];
    const updated = arr2.with(1, 99); // [1,99,3] non-mutating
    console.log(arr2.at(-1)); // 3
    

    DOM Methods

    Selection

    document.getElementById('myId');
    document.querySelector('.myClass');
    document.querySelectorAll('p');
    

    Creation and manipulation

    const div = document.createElement('div');
    div.textContent = 'Hello';
    document.body.appendChild(div);
    div.remove();
    

    Attributes and classes

    div.setAttribute('id', 'newDiv');
    div.classList.add('highlight');
    div.classList.toggle('active');
    div.getAttribute('id');
    div.removeAttribute('id');
    

    Events

    function onClick(e) { console.log('clicked', e.target); }
    div.addEventListener('click', onClick);
    div.removeEventListener('click', onClick);
    

    Utilities

    const rect = div.getBoundingClientRect();
    div.scrollIntoView({ behavior: 'smooth' });
    div.closest('section');
    div.matches('.highlight');
    

    Event Propagation

    Phases

    • Capturing phase: event travels from the window down to the target.
    • Target phase: event reaches the target element.
    • Bubbling phase: event bubbles up from the target to the window.

    Example

    <div id="outer">
      <button id="btn">Click</button>
    </div>
    <script>
      const outer = document.getElementById('outer');
      const btn = document.getElementById('btn');
    
      // Capture listener
      outer.addEventListener('click', () => console.log('outer capture'), true);
    
      // Bubble listener
      outer.addEventListener('click', () => console.log('outer bubble'), false);
    
      btn.addEventListener('click', (e) => {
        console.log('button clicked');
        // e.stopPropagation(); // stops further propagation
      });
    </script>
    

    Stopping propagation

    • e.stopPropagation() prevents the event from reaching other listeners in later phases.
    • e.stopImmediatePropagation() prevents other listeners on the same element from running.

    Use cases

    • Prevent duplicate handling when parent and child both listen for the same event.
    • Implement delegated event handling by listening on a parent and using event.target.

    Promises vs Observables and RxJS

    Promise example

    const myPromise = new Promise((resolve, reject) => {
      setTimeout(() => resolve('done'), 1000);
    });
    myPromise.then(result => console.log(result)).catch(err => console.error(err));
    

    Observable example

    import { Observable } from 'rxjs';
    
    const myObservable = new Observable(observer => {
      observer.next('first');
      setTimeout(() => {
        observer.next('second');
        observer.complete();
      }, 500);
    });
    
    const subscription = myObservable.subscribe({
      next: value => console.log('value:', value),
      error: err => console.error(err),
      complete: () => console.log('completed')
    });
    
    // subscription.unsubscribe(); // cancel if needed
    

    BehaviorSubject example

    import { BehaviorSubject } from 'rxjs';
    const loggedIn$ = new BehaviorSubject(false);
    loggedIn$.next(true);
    loggedIn$.subscribe(value => console.log('loggedIn:', value));
    

    RxJS Operators Examples

    map

    import { of } from 'rxjs';
    import { map } from 'rxjs/operators';
    of(1,2,3).pipe(map(x => x * 2)).subscribe(console.log); // 2,4,6
    

    filter

    import { filter } from 'rxjs/operators';
    of(1,2,3,4).pipe(filter(x => x % 2 === 0)).subscribe(console.log); // 2,4
    

    switchMap

    import { fromEvent, of } from 'rxjs';
    import { debounceTime, switchMap } from 'rxjs/operators';
    const input = document.querySelector('input');
    fromEvent(input, 'input').pipe(
      debounceTime(300),
      switchMap(e => of(`results for ${e.target.value}`))
    ).subscribe(console.log);
    

    mergeMap

    import { mergeMap } from 'rxjs/operators';
    of(1,2,3).pipe(mergeMap(x => of(x * 10))).subscribe(console.log); // 10,20,30
    

    take and takeUntil

    import { interval, Subject } from 'rxjs';
    import { takeUntil } from 'rxjs/operators';
    const stop$ = new Subject();
    interval(1000).pipe(takeUntil(stop$)).subscribe(console.log);
    setTimeout(() => stop$.next(), 5000); // stop after 5s
    

    Conclusion

    This revision adds hoisting, temporal dead zone, and event propagation to the earlier coverage of closures, IIFEs, function invocation methods, this, function types, array methods, DOM APIs, Promises, Observables, and RxJS operators. Use the summary for quick revision and the detailed sections for examples and deeper understanding.

    If you want, I can convert this into a printable one‑page cheat sheet, flashcards for study, or a small demo page with runnable examples.

    Share this article with your friends