JavaScript Crash Course#

Table of Contents#

  1. 1. Modern JavaScript & Environments
  2. 2. Variables, Let, Const & Data Types
  3. 3. Operators, Equality & Type Coercion
  4. 4. Objects, Arrays & Destructuring
  5. 5. Functions, Arrow Functions & this
  6. 6. Scope, Hoisting & Closures
  7. 7. Async JS: Promises & Async/Await
  8. 8. Array Higher-Order Methods
  9. 9. Modern ES6+ Features (Nullish, Optional Chaining)
  10. 10. Modules: ESM vs CommonJS
  11. 11. Error Handling & Custom Errors
  12. 12. JavaScript Quick Reference

1. Modern JavaScript & Environments#

JavaScript (ECMAScript) is a dynamic, multi-paradigm, prototype-based scripting language. Originally built for web browsers, it now runs everywhere — from backend servers (Node.js, Deno, Bun) to edge workers (Cloudflare Workers, Vercel Edge).

JavaScript Runtime Components

  • Engine (e.g. V8 in Chrome/Node): Compiles JavaScript into machine code using a JIT (Just-In-Time) compiler. Contains the Call Stack and Memory Heap.
  • Web APIs / C++ Bindings: Features provided by the environment (DOM, fetch(), setTimeout, file system in Node).
  • Event Loop: Coordinates asynchronous execution by moving callbacks from task queues into the call stack when the stack is empty.

2. Variables, Let, Const & Data Types#

Always declare variables with const by default, and let only when the variable will be reassigned. Avoid legacy var due to its lack of block scoping.

// 1. Declaration
const PI = 3.14159;    // Cannot be reassigned
let score = 0;         // Block-scoped, mutable
score += 10;

// 2. JavaScript Primitives (Immutable, passed by value)
const str = "TechToday";         // String
const num = 42;                  // Number (64-bit float IEEE 754)
const big = 9007199254740991n;   // BigInt
const bool = true;               // Boolean
const undef = undefined;         // Unassigned variable
const empty = null;              // Intentional absence of value
const sym = Symbol("id");        // Unique primitive identifier

// 3. Complex Objects (Passed by reference)
const user = { name: "Alice", role: "Engineer" };
const tags = ["frontend", "v8", "async"];

3. Operators, Equality & Type Coercion#

Always use strict equality (===) rather than loose equality (==) to prevent silent bugs caused by automatic type coercion:

// Loose Equality (==) -> Performs implicit coercion (AVOID)
"5" == 5;       // true (string coerced to number)
0 == false;     // true
null == undefined; // true

// Strict Equality (===) -> Checks both value and type (ALWAYS PREFER)
"5" === 5;      // false
0 === false;    // false
null === undefined; // false

// Falsy values in JavaScript:
// false, 0, -0, 0n, "", null, undefined, NaN
// Everything else is truthy (including empty arrays [] and objects {})!

4. Objects, Arrays & Destructuring#

Modern JavaScript provides clean object and array literals, destructuring, and spread/rest operators:

// Object & Array Creation
const user = {
  id: 101,
  username: "pankaj",
  profile: { city: "San Francisco", verified: true },
  skills: ["Python", "JavaScript", "Docker"]
};

// Destructuring with default values and renaming
const { username: handle, profile: { city }, age = 28 } = user;
console.log(handle, city, age); // "pankaj", "San Francisco", 28

// Array Destructuring
const [primarySkill, secondarySkill, ...restSkills] = user.skills;
console.log(primarySkill); // "Python"
console.log(restSkills);   // ["Docker"]

// Object Cloning and Merging (Shallow Copy)
const updatedUser = { ...user, active: true, username: "pankaj_updated" };

5. Functions, Arrow Functions & this#

JavaScript functions are first-class citizens: they can be passed as arguments, returned from other functions, and assigned to variables.

// 1. Function Declaration (Hoisted)
function calculateTotal(price, taxRate = 0.08) {
  return price + (price * taxRate);
}

// 2. Arrow Function (Lexical `this`, concise)
const double = (x) => x * 2;

// 3. Arrow function lexical `this` difference:
const timer = {
  seconds: 0,
  start() {
    // Arrow function captures `this` from the outer `start()` scope
    setInterval(() => {
      this.seconds++;
    }, 1000);
  }
};

6. Scope, Hoisting & Closures#

A closure is the combination of a function bundled together with references to its surrounding state (the lexical environment). Closures give functions access to an outer function's scope even after the outer function has returned.

function createCounter(initialValue = 0) {
  let count = initialValue; // Private variable enclosed in closure

  return {
    increment() { count++; return count; },
    decrement() { count--; return count; },
    getCount() { return count; }
  };
}

const counter = createCounter(10);
console.log(counter.increment()); // 11
console.log(counter.increment()); // 12
console.log(counter.getCount());  // 12
// count variable is fully private and inaccessible from the outside!

7. Async JS: Promises & Async/Await#

JavaScript is single-threaded. Non-blocking asynchronous I/O is managed using Promises and async/await syntax:

// 1. A Promise represents a value that will resolve or reject in the future
function fetchUserData(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) resolve({ id: userId, name: "Alice" });
      else reject(new Error("Invalid User ID"));
    }, 500);
  });
}

// 2. Modern Async/Await with error handling
async function loadDashboard() {
  try {
    const user = await fetchUserData(42);
    console.log("Loaded user:", user.name);
    return user;
  } catch (error) {
    console.error("Failed to load user:", error.message);
  } finally {
    console.log("Fetch attempt completed");
  }
}

// 3. Parallel Async Operations with Promise.all
const [orders, products] = await Promise.all([
  fetch("/api/orders").then(r => r.json()),
  fetch("/api/products").then(r => r.json())
]);

8. Array Higher-Order Methods#

Functional array operations produce clean, immutable code transformations without manual for loops:

const items = [
  { id: 1, name: "Keyboard", price: 80, inStock: true },
  { id: 2, name: "Mouse", price: 40, inStock: false },
  { id: 3, name: "Monitor", price: 300, inStock: true }
];

// map: Transform every element
const names = items.map(item => item.name); // ["Keyboard", "Mouse", "Monitor"]

// filter: Keep elements that match predicate
const available = items.filter(item => item.inStock); // Keyboard, Monitor

// reduce: Accumulate into a single value
const totalInventoryValue = items
  .filter(item => item.inStock)
  .reduce((sum, item) => sum + item.price, 0); // 380

// find & some & every
const monitor = items.find(item => item.id === 3);
const hasExpensive = items.some(item => item.price > 200); // true
const allInStock = items.every(item => item.inStock);      // false

9. Modern ES6+ Features (Nullish, Optional Chaining)#

Modern ECMAScript provides concise operators for safely handling deep objects and default values:

// 1. Optional Chaining (?.)
const user = { profile: null };
// Safely returns undefined instead of throwing TypeError: Cannot read property of null
const avatar = user?.profile?.avatarUrl;

// 2. Nullish Coalescing (??)
// Returns right side ONLY if left is null or undefined (preserves 0 and false!)
const port = process.env.PORT ?? 3000;
const count = 0 ?? 10; // 0 (with || it would evaluate to 10!)

// 3. Logical Assignment Operators
let a = null;
a ??= "default"; // Assigns only if a is null or undefined
let b = 1;
b &&= 2;         // Assigns if truthy

10. Modules: ESM vs CommonJS#

JavaScript supports two module standards: standard ECMAScript Modules (ESM) and legacy CommonJS (CJS):

// --- ECMAScript Modules (ESM - Standard) ---
// math.js
export const add = (a, b) => a + b;
export default function multiply(a, b) { return a * b; }

// app.js
import multiply, { add } from './math.js';

// --- CommonJS (CJS - Node.js legacy) ---
// math.cjs
module.exports = { add: (a, b) => a + b };

// app.cjs
const { add } = require('./math.cjs');

11. Error Handling & Custom Errors#

Robust applications use structured error handling and sub-classed error types:

class APIError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = "APIError";
    this.statusCode = statusCode;
  }
}

function processPayment(amount) {
  if (amount <= 0) {
    throw new APIError("Payment amount must be greater than zero", 400);
  }
}

try {
  processPayment(-10);
} catch (err) {
  if (err instanceof APIError) {
    console.error(`API Error [${err.statusCode}]: ${err.message}`);
  } else {
    console.error("Unknown internal error:", err);
  }
}

12. JavaScript Quick Reference#

Core Rules of Thumb

  • Use const by default; use let only when reassigning; never use var.
  • Always use === and !== to avoid implicit type conversions.
  • Use ?? instead of || for default values when 0 or false are valid inputs.
  • Handle asynchronous errors using try...catch around await expressions.
  • Prefer pure array methods (map, filter, reduce) over stateful loops.

Next Steps: Dive into the V8 engine, the event loop, microtask queues, and prototypal inheritance in the JavaScript Detailed Course. Return to the TechToday Homepage.