Vanilla JavaScript in 30 Days: A Free Study Plan for Middle Developer
23 August 2026
518 views
This study plan is up to date as of August 2026
In This Study Plan
Writing JavaScript that works is one skill. Understanding why it works - what a closure captures, how prototypal inheritance resolves a property lookup, or why an async function behaves the way it does - is another. That deeper understanding is what lets you reason through unfamiliar code instead of copying patterns. This is where many beginner-to-middle transitions slow down: the syntax is familiar, but the mental model underneath it is still incomplete.
Why study JavaScript at this depth instead of moving straight to a framework? Because many framework problems that confuse intermediate developers - a stale closure in a React hook, a this binding issue in a class method, or a race condition in async data fetching - trace back to these fundamentals. This plan builds that underlying model deliberately so framework code becomes easier to debug and explain.
This is a genuine 30 days of JavaScript challenge, not a survey course - every day assumes real prior JS comfort and moves into topics that beginner plans intentionally skip. Four phases carry you from closures and prototypes through modern ES6+ syntax, deep asynchronous patterns, and testing fundamentals, ending with the habits that make code genuinely maintainable rather than merely functional.
Who This Free Study Plan Was Built For?
This plan assumes real, working JavaScript experience already in place - comfortable use of functions, arrays, objects, the DOM, and basic async code, without needing to look up core syntax. It is built for the gap between “JS works for me” and “I can explain why JS behaves this way,” a distinction that shows up quickly in technical interviews and code reviews. The 30-day structure suits developers who already have a job or active job search underway and need this depth built efficiently, not developers starting from zero. This plan is most useful for:
- Beginner-level developers whose JavaScript knowledge has plateaued at "it works, I'm not sure why." If you can build working features but struggle to explain closures or prototypal inheritance in an interview, this plan builds that missing explanatory layer directly.
- Developers about to start learning a framework like React who want the fundamentals solid first. Frameworks assume this depth of JavaScript understanding implicitly; arriving without it means debugging framework-specific symptoms of gaps this plan closes directly.
- Self-taught or bootcamp-trained developers preparing for mid-level technical interviews. Closures,
thisbinding, and the event loop are recurring interview topics precisely because they reveal whether a candidate understands JavaScript or has memorized patterns; this free plan prepares you for exactly that kind of questioning.
JavaScript for 30 Days: Build Deeper Vanilla JS Skills
A structured JavaScript for 30 days plan works best when you already know the basics and use the month to strengthen the parts that are harder to learn from isolated tutorials. This vanilla JavaScript 30 days path focuses on language mechanics first - closures, prototypes, modules, async behavior, and testing - rather than jumping into a framework. The goal is to write, trace, refactor, and test enough code that you can explain why it behaves the way it does, not just make it pass. If you prefer to track topics instead of calendar days, use the Complete JavaScript Roadmap alongside this plan; it gives you a broader topic-by-topic view and makes it easy to mark completed areas and spot gaps. You can follow the roadmap after these free study plan or switch between the two whenever a topic needs more time than one scheduled day allows.
Stage 1: Closures & Prototypes (Days 1-8)
This stage targets the two concepts most responsible for confusing intermediate JS developers: closures and the prototype chain. Both explain behavior beginners usually accept without questioning, and both come up constantly in technical interviews as a way to distinguish real understanding from memorized syntax.
| Day | Topic | Description |
| Day 1-2 | Scope & Closures | Study lexical scope in depth, then closures - a function retaining access to variables from its enclosing scope even after that scope has finished executing. Practice writing closures deliberately, like counter functions and private variable patterns. |
| Day 3 | Common Closure Pitfalls | Learn the classic loop-and-closure bug (a var loop capturing the wrong value) and how block scoping with let resolves it. Understand why this specific bug appears so often in interview questions. |
| Day 4-5 | The Prototype Chain | Study how JS resolves a property lookup through __proto__ and Object.getPrototypeOf, and how prototypal inheritance differs fundamentally from classical inheritance in languages like Java. |
| Day 6 | Constructor Functions & Object.create |
Learn how constructor functions build objects with shared prototype methods, and Object.create as a more direct way to set up prototypal inheritance without a constructor. |
| Day 7-8 | ES6 Classes as Prototype Syntax | Study class syntax as syntactic sugar over the same prototype mechanics from Days 4-6, including extends, super, and how class methods actually live on the prototype, not on each instance. |
Practice Projects for This Stage
- A module counter built with closures. Build a counter with increment, decrement, and reset functions that share private state through a closure, inaccessible from outside. This is the clearest hands-on demonstration of closure-based encapsulation.
- A memoization function. Write a generic memoize function that caches a function's results using a closure over a cache object. This project applies closures to a genuinely useful, interview-relevant pattern.
- A shape hierarchy using prototypes directly. Build a set of shape objects (circle, rectangle) sharing methods through the prototype chain, first with constructor functions, then rewritten with
classsyntax for direct comparison. - A "fix the loop bug" debugging exercise. Write a loop with the classic
var-and-closure timing bug, observe the incorrect output, then fix it two different ways - switching tolet, and using an IIFE - to understand both solutions.
How I Approached Studying This Stage
- I recommend writing at least three closures from scratch before moving on, since the concept only solidifies once you've built something with it, not just read the definition.
- I suggest deliberately triggering the loop-and-closure bug yourself rather than just reading about it, since seeing the wrong output firsthand makes the explanation click in a way a written example doesn't.
- You should trace a property lookup through the prototype chain manually on paper at least once, following each
__proto__link, before relying on your mental model of how it works. - I advise rewriting the same object hierarchy with constructor functions and then with classes, side by side, so the "classes are syntactic sugar" explanation stops being an abstract claim and becomes something you've verified yourself.
Stage 2: Modern ES6+ Syntax & Modules (Days 9-15)
This stage covers the syntax features that define how modern production JS actually looks, along with the module system that lets code be organized across multiple files instead of one massive script.
| Day | Topic | Description |
| Day 9 | Destructuring & Spread/Rest | Study array and object destructuring for extracting values concisely, and the spread and rest operators for combining, copying, and collecting values. These appear constantly in modern codebases and frameworks alike. |
| Day 10 | Template Literals & Tagged Templates | Learn template literal syntax for string interpolation and multi-line strings, then briefly cover tagged template literals as a more advanced pattern used in some libraries. |
| Day 11 | Default Parameters & Optional Chaining | Study default function parameters, optional chaining (?.) for safely accessing potentially undefined nested properties, and nullish coalescing (??) as a more precise fallback operator than || when valid falsy values should be preserved. |
| Day 12-13 | ES Modules | Learn import and export syntax (named and default exports), how ES modules differ from the older CommonJS require system, and how to structure a small project across multiple linked files. |
| Day 14-15 | Iterators, Generators & Symbols |
Study the iterator protocol, generator functions (function* and yield) for producing values lazily, and a practical introduction to Symbol as a mechanism for unique object keys. |
Projects to Reinforce This Stage
- A refactor of an earlier project using destructuring and spread. Take a project from Stage 1 and rewrite its function signatures and object handling using destructuring, spread, and default parameters, comparing readability directly against the original.
- A small multi-file project using ES modules. Split a single-file script into logical modules - utilities, data, main logic - connected through
import/export, practicing real project structure instead of one long file. - A safe data-access utility using optional chaining. Build a function that reads deeply nested, sometimes-missing properties from an API-shaped object, using optional chaining and nullish coalescing to avoid runtime errors.
- A custom iterator or generator. Build a generator function that produces a sequence of values lazily (like a custom range function or a Fibonacci sequence generator), demonstrating the iterator protocol in a genuinely useful context.
Notes From Working Through This Stage
- I recommend refactoring old code with new syntax rather than only practicing new syntax on fresh examples, since seeing the same logic get cleaner is what makes destructuring and spread feel worth adopting.
- I suggest splitting a project into modules earlier than feels necessary, since the organizational benefit of ES modules only becomes obvious once a project has enough files to actually get confusing without them.
- You should use optional chaining deliberately on real, sometimes-incomplete data (like a public API response) rather than a made-up example, since its value is clearest when the missing data is genuinely unpredictable.
- I advise not over-investing time in generators beyond a working conceptual grasp, since they're less common in everyday frontend work than the other topics this stage covers - a solid understanding is enough here.
Stage 3: Promises, Async/Await & the Event Loop (Days 16-23)
This stage goes deeper into asynchronous JS than a beginner plan, covering the event loop mechanics that explain why async code behaves the way it does rather than only how to write it.
| Day | Topic | Description |
| Day 16-17 | The Event Loop & Call Stack | Study the call stack, task queue, and microtask queue, and how JS's single-threaded execution model handles asynchronous operations without blocking. This explains behavior that otherwise seems inconsistent. |
| Day 18 | Promises in Depth | Review Promise states (pending, fulfilled, rejected) in detail, then study .then() chaining, error propagation through .catch(), and why returning a value inside .then() matters for the next link in the chain. |
| Day 19 | Promise.all, allSettled, race, and any |
Learn the four Promise combinator methods, the specific scenario each one solves, and when running async operations concurrently is the right choice over running them sequentially. |
| Day 20-21 | Async/Await Error Handling | Study try/catch with async/await in depth, including handling multiple sequential awaited calls and the common mistake of forgetting to await a function that returns a Promise. |
| Day 22-23 | Building a Real API Integration | Apply everything from this stage to a real integration: fetching from a public API, handling loading and error states, and running multiple requests concurrently using Promise.all. |
Projects for Building Real Async Fluency
- A sequential-versus-concurrent request comparison. Fetch data from the same public API three times, once sequentially with individual
awaitcalls and once concurrently withPromise.all, and measure the timing difference directly. - An API integration with proper error and loading states. Build a small UI that fetches data, showing a loading state while waiting and a clear error message if the request fails, using
try/catcharoundasync/await. - A
Promise.racetimeout wrapper. Build a utility function that races a realfetchrequest against a timeout Promise, rejecting if the request takes too long - a genuinely useful real-world pattern. - An event loop prediction exercise. Write a script mixing synchronous code,
setTimeout, and Promises, predict the console output order before running it, then run it and compare. This directly tests your understanding from Days 16-17.
What Helped Me Understand This Stage
- I recommend doing the event loop prediction exercise multiple times with different code arrangements, since guessing wrong and then understanding why is a far more effective teacher than reading the explanation alone.
- I suggest deliberately forgetting an await at least once during practice to see the resulting bug (a Promise object where you expected a value), since recognizing that specific mistake instantly is a skill worth building now rather than during a real debugging session later.
- You should build the sequential-versus-concurrent comparison project even if the outcome seems obvious in theory, since watching the actual timing difference makes the performance argument for
Promise.allconcrete rather than abstract. - I advise reading a few real Promise-based library source files (a small one, nothing overwhelming) once this stage feels solid, since seeing these patterns used in genuine production code reinforces the concepts differently than self-written examples do.
Stage 4: Testing Fundamentals (Days 24-30)
The final stage introduces automated testing so you can verify behavior instead of relying only on manual checks. This stage stays deliberately foundational, covering the concepts and habits that transfer to any testing framework a future job might use.
| Day | Topic | Description |
| Day 24 | Why Automated Testing Matters | Understand the practical case for tests: catching regressions before they reach production, and enabling confident refactoring of code you didn't write and don't fully remember writing. |
| Day 25-26 | Unit Testing with Jest | Set up Jest, write basic unit tests using test and expect, and cover common matchers (toBe, toEqual, toContain). Practice testing pure functions first, since they're the simplest and clearest case. |
| Day 27 | Testing Asynchronous Code | Learn to test functions returning Promises or using async/await, including testing that a function correctly rejects under expected error conditions. |
| Day 28 | Mocking & Test Doubles | Study mocking functions and modules with Jest's mocking utilities, and why isolating the function under test from its real dependencies (like a live API call) makes tests faster and more reliable. |
| Day 29-30 | Writing a Real Test Suite | Apply everything from this stage to write a genuine test suite for a project built earlier in this plan, covering pure functions, async logic, and at least one mocked dependency. |
Projects to Apply These Testing Skills
- A test suite for the memoization function from Stage 1. Write unit tests confirming the memoize function correctly caches results and doesn't recompute for repeated inputs, practicing pure-function testing in its clearest form.
- Async tests for the API integration project from Stage 3. Write tests for the API integration project, mocking the
fetchcall so tests run reliably and quickly without depending on a real network request. - A test-driven small utility function. Pick a small, well-defined function (like a string formatter or a date calculator) and write its tests before writing the implementation, practicing test-driven development on a genuinely small scale.
- A full coverage report on one completed project. Run Jest's coverage tool against a completed project from earlier in the plan, and add tests for whatever meaningful logic coverage reveals as untested.
Advice for Making Testing Actually Stick
- I recommend starting with pure functions specifically, since they're the easiest to test correctly and build the right mental habits before tackling anything asynchronous or dependent on external state.
- I suggest writing a genuinely failing
teston purpose at least once, just to see Jest's failure output clearly, since recognizing what a real failure looks like matters as much as writing passing tests. - You should mock external dependencies deliberately rather than skipping tests that touch them, since avoiding async or API-dependent code entirely leaves a real, meaningful gap in your testing skills.
- I advise not chasing 100% coverage as a goal in itself during this stage - focus on testing the logic that would actually break something if it silently stopped working, since that judgment matters more than a coverage percentage.
Deepening This 30 Days of JavaScript Free Study Path
The material worth prioritizing at this stage moves past general tutorials into resources built specifically around the concepts this plan covers in depth - closures, the prototype chain, and the event loop rarely get the attention they deserve outside of dedicated deep-dive content. Official documentation remains essential for precise behavior, particularly around Promise combinators and ES module syntax, where small details genuinely change how code behaves. A well-regarded JS book focused on the language itself can also be useful here because closures, prototypes, and the event loop are often rushed in short-form tutorials. Interview-preparation resources are also worth incorporating here, since much of this plan's content overlaps directly with what mid-level technical interviews actually test.
| Resource | Type | Best For (50-80 words) |
| JavaScript30 by Wes Bos | Free project-based video course | Perfect for a 30‑day, mid‑level practice track using only vanilla JS. Each day is a focused mini project (custom video player, canvas, localStorage, event propagation, etc.), which forces you to use language features, DOM APIs, and browser events in real scenarios. Great for sharpening problem‑solving and refactoring habits. |
| TypeScript vs JavaScript in 2026: Should You Make the Switch? | Free article | Useful once your vanilla JS fundamentals feel solid and you are deciding whether TypeScript should be the next step. The article compares the two languages, explains where static typing changes day-to-day development, and helps you judge when moving from JavaScript to TypeScript adds practical value to a frontend project. |
| 30 Days of JavaScript (Asabeneh) | Free GitHub challenge | Best as a structured, concept‑heavy roadmap from fundamentals to closures, promises, and clean code. You can use the repository as your curriculum, doing one “day” per calendar day: reading the explanations, solving the exercises, then applying each concept in a small script or utility function. |
| JavaScript Lab - 30 Day Consistency Journey | Free practice repo | Great for daily algorithmic and DOM‑focused coding drills. The repo includes logic problems, basic DSA, and browser projects. For a mid‑level 30‑day plan, you can alternate between “logic days” (arrays, sorting, searching) and “browser days” (events, DOM, small tools), improving both reasoning and real‑world JS fluency. |
| “Learn JavaScript in 30 Days: Projects-Based Approach” - Leanpub | Paid project-based book/course | Ideal if you want a guided project path that still goes beyond basics. Each day introduces a specific concept and then uses it in a small project, touching ES6+, APIs, tooling, and dev workflow. Works well as the backbone of your 30‑day plan, with room to expand individual days into larger portfolio pieces. |
Confirming You're Ready for the Next Level
Thirty days of stage-based practice at this depth can build the kind of JS understanding that holds up in interviews and real debugging, not just familiarity with advanced syntax. This checklist is meant for honest self-assessment against genuine middle-level expectations, not a formality to skim past. If any item here still feels uncertain rather than solid, that's a precise, useful signal for which stage deserves revisiting before you rely on it in a real interview or on a production codebase. Work through it deliberately, since the gap between recognizing these concepts and actually reasoning through them is exactly what separates a beginner's understanding from a middle developer's.
- Explain closures and the prototype chain clearly enough to answer a technical interview question about either without hesitation.
- Write modern JavaScript fluently using destructuring, spread/rest, ES modules, and the other ES6+ features.
- Reason through asynchronous code confidently, including explaining event loop behavior and choosing correctly between sequential and concurrent async patterns.
- Handle errors properly in both Promise chains and
async/awaitcode, without the common mistakes (like a forgottenawait) that cause silent bugs. - Write a meaningful automated test suite for a real project, covering pure functions, async logic, and at least one mocked dependency.
Middle-level JavaScript fluency handles individual features and functions well, but production codebases at scale introduce a different category of problem entirely - performance under real load, complex state management, build tooling, and the architectural decisions that senior developers are expected to own. That's precisely the territory the next stage of this path covers.
Your next step is The 100 Days of JavaScript Challenge: A Study Plan for Senior Developers