javascript-interview-prep/lessons/00/knowledge.md

11 KiB
Raw Blame History

Lesson 00: Getting Started & Assessment Review

Duration: 45 minutes Objective: Understand JavaScript fundamentals and identify key areas for focused study


Introduction

Welcome to your JavaScript interview preparation curriculum! This lesson reviews the diagnostic assessment and establishes foundational knowledge that will be built upon in subsequent lessons.

Based on common evaluation results, most developers need focused review in:

  • Type coercion and equality
  • Closures and scope
  • The this keyword and context binding
  • Prototypes and inheritance
  • Modern ES6+ syntax
  • Asynchronous execution patterns

Part 1: JavaScript Type System Fundamentals

1.1 Primitive Types

JavaScript has 7 primitive types:

typeof undefined;    // "undefined"
typeof true;         // "boolean"
typeof 42;           // "number"
typeof 9007199254740991n; // "bigint"
typeof "hello";      // "string"
typeof Symbol();     // "symbol"
typeof null;         // "object" ⚠️ This is a bug in JavaScript!

The null Bug: typeof null returning "object" is a historical bug that can't be fixed without breaking existing code. null represents the intentional absence of any object value.

// Checking for null correctly
const value = null;
console.log(value === null);           // true
console.log(typeof value === "object" && value === null); // true

1.2 Reference Types

Objects, Arrays, and Functions are reference types:

typeof {};           // "object"
typeof [];           // "object" (arrays are objects)
typeof function(){}; // "function"

Key Difference:

  • Primitives are compared by value
  • References are compared by reference (memory location)
// Primitives - value comparison
const a = 42;
const b = 42;
console.log(a === b); // true

// References - memory location comparison
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false (different objects in memory)
console.log(arr1 === arr1); // true (same reference)

const arr3 = arr1;
console.log(arr3 === arr1); // true (same reference)

Part 2: Floating-Point Precision

2.1 The 0.1 + 0.2 Problem

JavaScript uses IEEE 754 double-precision floating-point format. Some decimal numbers cannot be represented exactly in binary.

console.log(0.1 + 0.2);           // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);   // false ⚠️

Why?

  • 0.1 in binary: 0.0001100110011... (repeating)
  • Computer must round, introducing tiny errors
  • These errors accumulate during operations

Solutions:

// 1. Use epsilon comparison
function areClose(a, b, epsilon = 0.0001) {
  return Math.abs(a - b) < epsilon;
}
console.log(areClose(0.1 + 0.2, 0.3)); // true

// 2. Use Number.EPSILON
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON); // true

// 3. Work with integers (convert to cents, etc.)
const price1 = 10; // 10 cents
const price2 = 20; // 20 cents
const total = price1 + price2; // 30 cents (exact)

// 4. Round to fixed decimals
const result = Math.round((0.1 + 0.2) * 100) / 100; // 0.3

Interview Tip: Always mention this caveat when working with money or precise calculations. Suggest using integers or decimal libraries.


Part 3: Var, Let, and Const

3.1 The Problem with var

// Function scope (not block scope)
function example() {
  if (true) {
    var x = 5;
  }
  console.log(x); // 5 (var leaks out of the block!)
}

// Hoisting
console.log(y); // undefined (not an error!)
var y = 10;

// What actually happens:
var y;
console.log(y); // undefined
y = 10;

The Classic Loop Problem:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Logs: 3, 3, 3
// Why? All callbacks share the same 'i' variable
// By the time they execute, the loop finished and i = 3

3.2 Let and Const (ES6+)

// Block scope
if (true) {
  let x = 5;
  const y = 10;
}
console.log(x); // ReferenceError
console.log(y); // ReferenceError

// No hoisting issues
console.log(z); // ReferenceError (temporal dead zone)
let z = 15;

// Loop with let creates a new binding per iteration
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Logs: 0, 1, 2 ✅

Const Rules:

const x = 10;
x = 20; // TypeError: Assignment to constant variable

// But objects are mutable!
const obj = { name: "Alice" };
obj.name = "Bob"; // ✅ Allowed (modifying property)
obj = {}; // ❌ TypeError (reassigning variable)

// Use Object.freeze() for immutability
const frozen = Object.freeze({ name: "Alice" });
frozen.name = "Bob"; // Silently fails (throws in strict mode)
console.log(frozen.name); // "Alice"

Best Practice: Use const by default, let when you need reassignment, avoid var.


Part 4: Array Behavior and Holes

4.1 Sparse Arrays

const arr = [1, 2, 3];
arr[10] = 11;

console.log(arr.length);    // 11 (not 4!)
console.log(arr);           // [1, 2, 3, empty × 7, 11]
console.log(arr[5]);        // undefined (hole in array)

Array Length:

  • Length is always the highest index + 1
  • Creating holes doesn't initialize elements
const sparse = new Array(5);
console.log(sparse.length); // 5
console.log(sparse[0]);     // undefined
console.log(0 in sparse);   // false (hole, not undefined value)

const filled = [undefined, undefined];
console.log(0 in filled);   // true (actual undefined value)

Array Methods and Holes:

const arr = [1, , 3]; // Array with hole

arr.forEach(x => console.log(x)); // 1, 3 (skips holes)
arr.map(x => x * 2);              // [2, empty, 6] (preserves holes)
Array.from(arr);                   // [1, undefined, 3] (converts holes)

Part 5: Type Coercion Essentials

5.1 Truthy and Falsy Values

Falsy values (8 total):

if (false) {}       // false
if (0) {}           // 0
if (-0) {}          // -0
if (0n) {}          // 0n (BigInt zero)
if ("") {}          // empty string
if (null) {}        // null
if (undefined) {}   // undefined
if (NaN) {}         // NaN

Everything else is truthy, including:

if ("0") {}         // truthy (non-empty string)
if ("false") {}     // truthy
if ([]) {}          // truthy (empty array)
if ({}) {}          // truthy (empty object)
if (function(){}) {} // truthy

5.2 Unary Plus Operator

console.log(+"42");      // 42
console.log(+"3.14");    // 3.14
console.log(+"hello");   // NaN
console.log(+true);      // 1
console.log(+false);     // 0
console.log(+null);      // 0
console.log(+undefined); // NaN

// Quick number conversion
const input = "123";
const num = +input;      // Same as Number(input)

5.3 Addition Coercion

// String concatenation wins
console.log("" + 1 + 2);     // "12" (1 converts to string first)
console.log(1 + 2 + "");     // "3" (addition happens first)
console.log("" + []);        // "" (array becomes empty string)
console.log("" + {});        // "[object Object]"

// Arrays
console.log([] + []);        // "" (both arrays convert to empty strings)
console.log([1] + [2]);      // "12"
console.log([1, 2] + [3]);   // "1,23"

Part 6: Essential Function Concepts

6.1 Function Declarations vs Expressions

// Declaration - hoisted
foo(); // ✅ Works
function foo() {
  console.log("I'm hoisted!");
}

// Expression - not hoisted
bar(); // ❌ ReferenceError
const bar = function() {
  console.log("I'm not hoisted!");
};

6.2 Arrow Functions Preview

Arrow functions have important differences (covered in Lesson 02):

// No 'this' binding
const obj = {
  value: 42,
  regularFunc: function() {
    console.log(this.value); // 42
  },
  arrowFunc: () => {
    console.log(this.value); // undefined (or global value)
  }
};

obj.regularFunc(); // 42
obj.arrowFunc();   // undefined

Key Differences:

  • Arrow functions inherit this from outer scope
  • Cannot be used as constructors
  • No arguments object
  • Cannot use yield (not generators)

Part 7: Common Interview Gotchas

7.1 Object Property Duplicates

const obj = { a: 1, b: 2, a: 3 };
console.log(obj.a); // 3 (last value wins)

In modern JavaScript, duplicate keys are allowed. The last value overwrites previous ones.

7.2 Automatic Semicolon Insertion (ASI)

// This looks fine...
function getData() {
  return
  {
    name: "Alice"
  }
}
console.log(getData()); // undefined ⚠️

// JavaScript interprets it as:
function getData() {
  return; // semicolon inserted!
  {
    name: "Alice"
  }
}

// Fix: Keep opening brace on same line
function getData() {
  return {
    name: "Alice"
  };
}

7.3 Implicit Global Variables

function oops() {
  x = 10; // ⚠️ Creates global variable (no var/let/const)
}
oops();
console.log(x); // 10 (global!)

// Even worse:
let x = y = 10;
// This is really: let x = (y = 10)
// x is local, y is global!

function example() {
  let x = y = 20;
}
example();
console.log(y); // 20 (global!)

Fix: Always use "use strict"; and declare all variables.

"use strict";
function safe() {
  x = 10; // ReferenceError: x is not defined
}

Part 8: Preparing for Deep Dive Topics

Topics Covered in Upcoming Lessons

Lesson 01 - Type System & Coercion:

  • Double equals vs triple equals
  • Type conversion rules
  • Advanced coercion scenarios

Lesson 02 - This Binding:

  • How this is determined
  • Call, apply, and bind
  • Arrow functions and this

Lesson 03 - Closures:

  • Lexical scope
  • Practical closure patterns
  • Memory implications

Lesson 04 - Event Loop:

  • Call stack, task queue, microtask queue
  • Understanding async execution order

Lesson 05 - Promises:

  • Promise states and chaining
  • Error handling
  • Async/await patterns

Lesson 06 - Prototypes:

  • Prototype chain
  • Constructor functions vs classes
  • Inheritance patterns

Lesson 07 - ES6+ Features:

  • Destructuring
  • Spread/rest operators
  • Template literals, default parameters

Lesson 08 - Array Methods:

  • Map, filter, reduce
  • ForEach vs for loops
  • Functional programming concepts

Key Takeaways

Remember:

  1. typeof null returns "object" (historical bug)
  2. Never use === with floating-point decimals
  3. var is function-scoped, let/const are block-scoped
  4. Arrays are compared by reference, not value
  5. Use const by default, let when needed, avoid var
  6. Arrow functions don't have their own this binding
  7. Array length = highest index + 1
  8. Always declare variables to avoid implicit globals

Practice Tips

  1. Run code examples - Don't just read them
  2. Predict output before running code
  3. Understand "why" - Memorization isn't enough
  4. Practice explaining concepts out loud
  5. Focus on weak areas identified in your evaluation

Next Steps

Complete the medium-difficulty quiz to test your understanding of these fundamentals. Then proceed to Lesson 01 to dive deeper into the type system.

Time to complete quiz: 10-15 minutes