Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
JavaScript Crash Course
JavaScript is the most ubiquitous programming language in the world. It runs in every browser, powers servers with Node.js, builds mobile apps, and even trains machine learning models. If you learn one language, make it JavaScript.This crash course is designed to take you from “Hello World” to understanding closures, prototypes, async/await, and modern ES6+ features.
Why JavaScript?
JavaScript has evolved from a simple scripting language to a versatile powerhouse.Runs Everywhere
From browsers to servers (Node.js) to mobile (React Native) to desktop (Electron) — JavaScript is truly universal.
Massive Ecosystem
npm is the world’s largest package registry with over 2 million packages. There’s a library for everything.
Async by Nature
Built-in event loop and async primitives make handling I/O, network calls, and user interactions seamless.
Rapid Evolution
With yearly ECMAScript updates, JavaScript keeps getting better: async/await, modules, optional chaining, and more.
Course Roadmap
We will peel back the layers of abstraction to understand how JavaScript really works.Fundamentals
Understand variables, types, operators, and control flow.
Start Learning
Functions & Scope
First-class functions, closures, and the execution context.
Explore Functions
Objects & Prototypes
Objects, prototypal inheritance, and the
this keyword.
Master ObjectsAsync JavaScript
Callbacks, Promises, async/await, and the Event Loop.
Go Async
Modern JavaScript (ES6+)
Destructuring, modules, classes, and the latest features.
Go Modern
DOM & Browser APIs
Manipulating the DOM, handling events, and browser APIs.
Build for the Web
Quick Reference — JavaScript’s Most Common Gotchas
If you read nothing else before starting, know these. Each is covered in depth in the relevant chapter.| Gotcha | What happens | Fix |
|---|---|---|
typeof null | Returns 'object' (25-year-old bug) | Use value === null to check for null |
0.1 + 0.2 | 0.30000000000000004 (IEEE 754 floats) | Use integer math for money (cents) |
'5' + 3 | '53' (string concatenation, not addition) | Use Number('5') + 3 or +'5' + 3 |
[] == false | true (coercion through "" to 0) | Always use === (strict equality) |
var in loops | Shared across iterations (closure bug) | Use let for block-scoped loop variable |
this in callbacks | Loses object context | Use arrow functions or .bind() |
.sort() no comparator | Sorts as strings: [10, 9, 80] becomes [10, 80, 9] | Always pass (a, b) => a - b for numbers |
fetch on 404/500 | Does not throw (only network errors throw) | Check response.ok before reading body |
{ ...obj } with nesting | Shallow copy (nested objects shared) | Use structuredClone() for deep copy |
async function return | Always returns a Promise, never a raw value | Use await to unwrap |
Prerequisites
- Basic programming knowledge (any language).
- A modern browser (Chrome, Firefox, Edge).
- Node.js installed for running JS outside the browser (
node -v). - A code editor (VS Code is highly recommended).
The JavaScript Philosophy
“Any application that can be written in JavaScript, will eventually be written in JavaScript.” — Atwood’s LawJavaScript is a multi-paradigm language. You can write object-oriented, functional, or procedural code. It’s dynamically typed (flexibility at the cost of runtime errors), and single-threaded with an event-driven architecture. Think of JavaScript like a Swiss Army knife: it is not the best dedicated tool for any single job, but it is the only tool that works in every environment you will encounter on the web. A backend engineer might reach for Go or Rust for raw performance. A data scientist might prefer Python. But JavaScript is the one language that runs natively in every browser on the planet, on every server via Node.js, and increasingly on edge runtimes and IoT devices. That universality is its superpower — and the reason it is worth learning deeply, quirks and all.
Interview Deep-Dive
JavaScript is single-threaded. How does it handle thousands of concurrent connections in Node.js without blocking?
JavaScript is single-threaded. How does it handle thousands of concurrent connections in Node.js without blocking?
Strong Answer:
- The key insight is that single-threaded does not mean single-tasked. JavaScript uses a single call stack for executing code, but it offloads I/O operations (file reads, network requests, database queries) to the operating system’s kernel or a thread pool managed by libuv (in Node.js) or the browser’s Web APIs.
- When an async operation like
fs.readFileis called, Node hands it off to the OS or a worker thread. The main thread is immediately free to process the next event. When the I/O completes, a callback is placed on the event loop’s task queue. The event loop picks it up only when the call stack is empty. - This is why Node.js excels at I/O-bound workloads (API servers, real-time apps) but struggles with CPU-bound tasks (image processing, heavy computation). A CPU-bound task blocks the single thread and starves every other request. The mitigation is
worker_threadsin Node.js or Web Workers in the browser, which spin up actual OS threads for computation. - In production, a Node.js server handling 10,000 concurrent WebSocket connections is not running 10,000 threads. It is running one thread that rapidly cycles through event callbacks. The bottleneck is never the concurrency model — it is what you do on the main thread between events.
- Use
worker_threadsto offload the resize to a separate thread. The main thread posts the image buffer to the worker and gets a message back when it is done. This keeps the event loop free. - Use a separate microservice or job queue (like Bull/BullMQ backed by Redis) where the API server enqueues a resize job and a dedicated worker process picks it up. This is the production-standard pattern at scale because it lets you scale API servers and image workers independently.
- Use a native addon like
sharp(which calls into libvips via C++ bindings and releases the GIL equivalent automatically), so the heavy lifting happens off the main thread even without explicit worker usage. - The anti-pattern is calling a synchronous image processing library directly in an Express route handler. I have seen this take down a production API serving 500 requests per second because a single 5MB image resize blocked the event loop for 800ms, causing every other request to queue up behind it.
Explain type coercion in JavaScript. Why does [] == ![] evaluate to true?
Explain type coercion in JavaScript. Why does [] == ![] evaluate to true?
Strong Answer:
- JavaScript’s
==operator triggers the Abstract Equality Comparison algorithm (defined in the ECMAScript spec, section 7.2.14), which performs type coercion before comparison. The===operator skips coercion entirely and compares type + value. - For
[] == ![]: first,![]is evaluated. Since[]is truthy (all objects are truthy),![]becomesfalse. Now we have[] == false. The spec says: if one side is boolean, convert it to a number.falsebecomes0. Now we have[] == 0. The spec says: if one side is an object and the other is a number, callToPrimitiveon the object.[].valueOf()returns the array itself (not a primitive), so it falls through to[].toString(), which returns"". Now we have"" == 0. The spec says: if one side is a string and the other is a number, convert the string to a number.Number("")is0. Now we have0 == 0, which istrue. - The practical lesson is not to memorize every coercion path, but to internalize one rule: always use
===unless you have a specific, documented reason to use==. The one defensible exception isvalue == null, which catches bothnullandundefinedin a single comparison. - In production, linting rules like ESLint’s
eqeqeqenforce this automatically. At a previous team I worked on, we had a bug where a form field value of"0"was being treated as falsy in anif (value)check, causing a valid zero input to be silently discarded. The fix was switching toif (value !== null && value !== undefined)— or using the nullish coalescing operator.
Object.is() and ===, and when does it matter?Object.is() differs from === in exactly two cases: Object.is(NaN, NaN) returns true (whereas NaN === NaN is false), and Object.is(-0, 0) returns false (whereas -0 === 0 is true). This matters in specific scenarios: if you are implementing a memoization cache and a function is called with NaN as an argument, === would fail to detect a cache hit. React uses Object.is internally for its dependency comparison in useEffect and useMemo hooks — this is why passing NaN as a dependency does not cause infinite re-renders.JavaScript is called multi-paradigm. What does that mean concretely, and how does it affect architectural decisions on a real project?
JavaScript is called multi-paradigm. What does that mean concretely, and how does it affect architectural decisions on a real project?
Strong Answer:
- Multi-paradigm means JavaScript supports object-oriented programming (prototypal inheritance, classes), functional programming (first-class functions, closures, immutability patterns), and procedural/imperative programming (sequential statements, loops). You are not locked into one style the way you are with, say, Haskell (functional) or Java pre-8 (OOP).
- Concretely, this affects architecture because different parts of a codebase may benefit from different paradigms. Data transformation pipelines (parsing API responses, transforming state) are often cleanest as functional chains:
data.filter(...).map(...).reduce(...). State management and encapsulation (a database connection pool, a cache with TTL) often benefit from OOP with classes. Glue code and scripts are often simplest as imperative procedures. - The risk is inconsistency. On a team of 8 engineers, if half write functional-style code and half write class-based OOP, the codebase becomes incoherent. The practical solution is to establish conventions: “We use functional style for data transforms, classes for services, and we never mix paradigms within a single module.” Linting rules and code review enforce this.
- A real-world example: in a React + Node.js codebase, the frontend is typically functional (function components, hooks, pure render functions) while the backend services might be class-based (NestJS controllers, injectable services). Trying to force one paradigm everywhere leads to awkward code — a React component written as a class in 2026 feels archaic, and a complex stateful service written as nested closures feels unreadable.