javascript fundamentals

Intermediate

Overview

JavaScript fundamentals form-end and increasingly server-side development. At the intermediate level, interviews focus on deep conceptual understanding rather than just topics are commonly tested at companies like Amazon, Spotify, and mid-sized tech firms where developers need to demonstrate mastery of mechanics to solve complex.


Core Concepts

Definition: A function that remembers values from its lexical scope even after the outer function has completed execution.

Key Points:

  • Created when a nested function references variables from its outer scope
  • Allows for data encapsulation and private variables
  • Critical for understanding asynchronous patterns and callbacks

Example:

function outer() {
  let count = 0;
  return function inner() { // Closure captures 'count'
    count++;
    return count;
  };
}

const increment = outer(); 
console.log(increment()); // 1
console.log(increment());

Event Loop

Definition: : The mechanism that enables JavaScript (single-threaded) to handle asynchronous operations using a call stack and callback queue.

Key Points:

  • Operations: Call Stack, Callback Queue, Micro Task Queue (promises, mutation observers)
  • Order: Execute main script → process micro tasks → handle asynchronous callbacks
  • Determines execution order of async code

Example:

console.log('1');

setTimeout(() => console.log('2'), 0); 

Promise.resolve().then(() => console.log('3'));

console.log('4');
// Output: 1, 4, 3, 2

Prototype Inheritance

Definition: : Mechanism where objects can inherit properties/methods from other objects through a prototype chain.

Key Points:

  • Object.create() for manual inheritance
  • class syntax is syntactic sugar over prototypes
  • Avoids explicit constructor functions for most use cases

Example:

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  console.log(`${this.name} makes a noise.`);
};

function Dog(name) {
  Animal.call(this, name); // Inherit properties
}

Dog.prototype = Object.create(Animal.prototype); 
Dog.prototype.constructor = Dog;

const d = new Dog('Rex');
d.speak(); // Rex makes a noise.

Asynchronous Patterns

Definition: : Different approaches to handling non-blocking operations in JavaScript.

PatternUse CaseKey Advantage
CallbacksSimple async operationsDirect control flow
PromisesChaining async stepsError propagation
async/awaitComplex async flowsSynchronous-looking code

Example:

// async/await example
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

Common Interview Questions

  1. Explain the difference between == and === in JavaScript

    • == performs type coercion before comparison
    • === checks both value and type without coercion
    • Always prefer === to avoid unexpected behavior
    console.log(0 == false); // true (coerced to 0 vs 0)
    console.log(0 === false); // false (different types)
    
  2. Implement a debounce function

    • Debounce limits how often a function can be called
    • Useful for search inputs, window resizing
    function debounce(fn, delay) {
      let timeoutId;
      return function(...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => fn.apply(this, args), delay);
      };
    }
    
  3. What is the purpose of the this keyword?

    • Refers to the object that owns the method being executed
    • Value determined by how the function is called, not where it's defined
    • Common gotchas: losing this in callbacks without binding
  4. Compare and contrast forEach, map, and filter array methods

    • forEach: Executes callback for each element (returns undefined)
    • map: Creates new array by transforming each element
    • filter: Creates new array with elements that pass a test
  5. Explain IIFEs (Immediately Invoked Function Expressions)

    • Function that executes immediately upon definition
    • Creates private scope to avoid global pollution
    (function() {
      const secret = 'top secret';
    })();
    

Detailed Examples

Example 1: Understanding Scope and Hoisting

console.log(a); // undefined (not ReferenceError due to hoisting)
var a = 10;

function checkHoisting() {
  console.log(b); // ReferenceError: b is not defined
  let b = 20;
}

Explanation:

  • var declarations are hoisted to the top of their scope as undefined
  • let declarations are hoisted but not initialized, causing Temporal Dead Zone

Example 2: Promise Chain Error Propagation

fetch('invalid-url')
  .then(res => res.json())
  .then(data => {
    console.log(data);
    return data.user.id;
  })
  .then(userId => fetch(`/users/${userId}`))
  .then(res => res.json())
  .catch(err => console.error('Failed:', err));

Explanation:

  • Errors jump to the nearest .catch()
  • Each .then() receives the value from the previous handler
  • Always include .catch() at the end of chains

Practice Problems

  1. Easy: Convert this callback-based code to use promises

    function readFile(path, cb) {
      fs.readFile(path, 'utf8', cb);
    }
    

    > Use the 'util.promisify' module or wrap in a new Promise

  2. Medium: Implement a throttle function that executes a function at most once every X milliseconds

    > Track last execution time and use setTimeout/clearTimeout

  3. Intermediate: Create a deep clone function that handles nested objects, arrays, and basic types without using JSON methods

    > Use recursion and handle circular references carefully


Key Takeaways

  • Closures are your friend: Use them for encapsulation and avoiding global state
  • Master the event loop: Understanding execution order is crucial for debugging async behavior
  • Prefer === and let/const: Avoid coercion pitfalls and accidental redeclaration
  • Practice async patterns: Convert between callbacks, promises, and async/await fluently
  • Common pitfalls:
    • Losing this context in callbacks
    • Not handling promise rejections
    • Misunderstanding hoisting behavior of var vs let

Study Tips:

  • Build small projects that force you to use closures and async patterns
  • Write unit tests for edge cases in your utility functions
  • Review MDN documentation for edge cases in built-in methods

Ready for a new challenge?

Start a new session to explore different topics or increase the difficulty level.

Start New Session