JavaScript Fundamentals¶
The bar: you can write, read, and debug plain JavaScript by hand — variables, control flow, functions, arrays, objects, the DOM, errors, and async — without a single AI autocomplete carrying you.
This is the module where you stop copying code and start writing it. No Copilot, no ChatGPT, no autocomplete finishing your thoughts. You type every line and you understand what each one does. That discipline is the whole point: every framework, every library, every clever tool you touch later is JavaScript underneath. If the foundation is borrowed, everything you build on it is borrowed too.
Slow is smooth here. You are trading the short-term speed of a generated snippet for the long-term ability to read a stack trace at 2 a.m. and know exactly where it broke. That is what shipping real software to veterans depends on.
4.1 Variables and Data Types¶
Objective: understand how JavaScript stores and handles the data your program moves around.
A variable is a labeled box. let gives you a box you can reassign, const gives you one you cannot, and var is the old function-scoped box you should avoid in new code because it leaks past the block you expect it to live in. Reach for const by default and drop to let only when you truly reassign.
The primitive types — string, number, boolean, null, undefined — are the atoms. The gotcha that bites every self-taught dev is coercion: JavaScript will silently turn a number into a string when you least want it, which is why typeof is your flashlight for checking what you actually hold. Watch the edge cases too: NaN is a number that means "not a number" and is never equal to itself, and Infinity shows up when math runs off the rails.
Template literals with backticks let you build strings cleanly with ${} interpolation instead of clumsy + concatenation, and the string methods do the everyday cleanup work.
Drill:
- Declare and reassign with
let, lock values withconst, and provevarscope leaks - Inspect every primitive with
typeof, includingtypeof null(the famous"object"bug) - Trigger and detect
NaNandInfinity; testNaN === NaN - Build strings with template literals; use
.trim(),.toUpperCase(),.slice(),.includes()
4.2 Operators and Expressions¶
Objective: use operators to transform data and drive decisions.
Arithmetic (+, -, *, /, %, **) is the easy part; the modulo % and exponent ** are the two people forget they have. The decisions come from comparison and logical operators, and this is where the single most important rule in the module lives: always use === and !==, never == and !=. The double-equals versions coerce types before comparing, so 0 == "" is true and that will cost you an afternoon.
Logical operators &&, ||, and ! short-circuit — they stop evaluating the moment the answer is known. That behavior is a tool, not just trivia: user && user.name safely skips the second half when user is missing. The ternary condition ? a : b gives you a one-line conditional, and understanding operator precedence keeps you from wrapping everything in defensive parentheses out of superstition.
Drill:
- Run every arithmetic operator, especially
%and** - Prove why
===beats==with a coercion example like0 == "" - Use
&&and||for short-circuit guards and defaults - Rewrite an
if/elseas a ternary; test precedence with mixed expressions
4.3 Control Flow¶
Objective: control the order your program runs with conditionals and loops.
if / else if / else is the branch you will use most; switch is cleaner when you are checking one value against many fixed cases. Loops are how you repeat work. Learn the standard for loop, then the two that trip people up: for...of walks the values of an array, for...in walks the keys of an object — mix them up and you get surprising results.
while and do...while run until a condition flips, with do...while guaranteeing at least one pass. break bails out of a loop early and continue skips to the next iteration. Nested loops are sometimes necessary, but every layer multiplies your work, so when you find yourself three loops deep, stop and ask whether an array method would say it more clearly.
Drill:
- Branch with
if/else if/elseand aswitch - Loop with standard
for,for...ofover an array,for...inover an object - Use
whileanddo...while; control flow withbreakandcontinue - Write a nested loop, then refactor it flatter
4.4 Functions¶
Objective: package logic into reusable, testable functions.
A function is a named, reusable block. Declarations (function add() {}) are hoisted so you can call them before they appear in the file; expressions (const add = function() {}) and arrow functions (const add = () => {}) are not. Parameters are the names in the definition, arguments are the real values you pass, and default values (function greet(name = "vet")) save you from undefined surprises. A function without a return hands back undefined — a classic silent bug.
Scope is where names live: global, function, and block. Getting scope right is what keeps your variables from stepping on each other. Then there are closures — a function remembering the variables from where it was created, even after that outer function has finished. Closures sound academic until you realize they power almost every React hook and event handler you will write, so do not skip past them.
Drill:
- Write a declaration, a function expression, and an arrow function that do the same job
- Use default parameters and prove a missing
returnyieldsundefined - Demonstrate global vs function vs block scope
- Build a counter with a closure that keeps private state
4.5 Arrays¶
Objective: store and reshape ordered collections of data.
Arrays are ordered lists. Create them, index into them, and learn the mutating crew — push and pop at the end, shift and unshift at the front. Know which methods change the original array and which return a new one: splice mutates, slice and concat copy. That distinction is the source of countless "why did my data change?" bugs.
The search methods indexOf, includes, find, and findIndex answer "is it here and where?" The three you will lean on hardest are map (transform every item), filter (keep the ones that pass), and reduce (fold the whole thing into one value). Prefer these over forEach and raw for loops when you are producing a new result — they read like a sentence. Round it out with sorting, reversing, and multi-dimensional arrays for grids and tables.
Drill:
- Add and remove with
push,pop,shift,unshift - Contrast mutating
splicewith copyingsliceandconcat - Search with
indexOf,includes,find,findIndex - Chain
map,filter,reduce; compare againstforEach;sort,reverse, and nest arrays
4.6 Objects¶
Objective: model real things with key-value data structures.
Objects hold named properties instead of ordered slots. Build them with a literal {} or a constructor, read them with dot notation (user.name) or bracket notation (user["name"]) when the key is dynamic, and add, change, or delete properties as you go. Methods are just functions stored on a property.
The this keyword points at whatever object called the method, and it confuses everyone at first — its value depends on how the function is called, not where it is defined. Lean on Object.keys(), Object.values(), and Object.entries() to loop over objects. Destructuring (const { name } = user) and the spread operator ({ ...user, active: true }) are the modern moves you will see in every React codebase, so get fluent now.
Drill:
- Create objects as literals and with a constructor
- Access with dot and bracket notation; add, modify, and
deleteproperties - Write an object method and observe
this - Iterate with
Object.keys/values/entries; destructure and spread objects
4.7 DOM Manipulation¶
Objective: read and change a live web page from JavaScript.
The DOM is the browser's live model of the page, and JavaScript is how you touch it. Select elements with getElementById or the more flexible querySelector, then change what they show with textContent (safe, text only) or innerHTML (powerful but a security risk if you inject untrusted input). Change appearance through style and, better, by toggling classList.
You can create elements from scratch and remove them, which is how dynamic interfaces get built before any framework. Wire behavior with event listeners for click, submit, and keydown; the event object carries the details and event.preventDefault() stops the browser's default action — essential for forms that would otherwise reload the page. Event delegation, attaching one listener to a parent instead of many to children, is the trick that keeps dynamic lists fast.
Drill:
- Select with
getElementByIdandquerySelector - Change content with
textContentandinnerHTML; toggleclassListandstyle - Create and remove elements dynamically
- Handle
click,submit,keydown; callevent.preventDefault(); wire one delegated listener
4.8 Error Handling¶
Objective: write code that fails on purpose, in a controlled way, instead of crashing.
Real software hits bad input and missing data. try / catch / finally lets you attempt risky work, catch the failure, and run cleanup either way. You can throw your own errors — including custom error types — to signal that something is wrong on your terms rather than letting a vague failure ripple downstream.
Know the built-in error types: TypeError when you call something that is not a function or read a property of undefined, ReferenceError for a name that does not exist, and SyntaxError for code the engine cannot even parse. When things break, the console is your field radio: console.log, console.error, console.table, and friends. Most important, learn to actually read a stack trace top to bottom — it names the file, line, and call path. That single skill separates people who fix bugs from people who guess.
Drill:
- Wrap risky code in
try / catch / finally throwa built-in error and a custom error- Reproduce a
TypeError,ReferenceError, andSyntaxErroron purpose - Debug with
console.log,console.error,console.table; read a real stack trace to its source line
4.9 Asynchronous JavaScript Basics¶
Objective: understand how JavaScript handles work that takes time.
JavaScript runs one thing at a time, so anything slow — a network call, a timer — has to happen out of line. Start with setTimeout and setInterval to schedule work. The old way to handle "do this when it's done" was callbacks, and nesting them deep enough produces the infamous callback hell: a pyramid of code you cannot read or maintain.
Promises fixed that. A Promise is a placeholder for a value that is not ready yet, and .then() chaining lets you sequence steps flatly instead of nesting them. Then async/await made Promises read like ordinary top-to-bottom code — await pauses inside an async function until the Promise settles. You will use all of this the moment you call fetch to pull data from an API, which is the front door to every real application.
Drill:
- Schedule work with
setTimeoutandsetInterval - Write a nested callback, feel the pain, then flatten it
- Create a Promise and chain it with
.then() - Rewrite the chain with
async/await; pull data withfetch
4.10 Hands-On Lab: JavaScript Projects¶
Objective: build four working projects from scratch, every line typed by hand, no AI assistance.
What you ship
Four small applications, each exercising a different slice of this module, all authored by you without AI tools:
- Calculator — buttons wired with event listeners, results driven through DOM manipulation.
- Todo list — add, complete, and delete tasks, with state persisted in
localStorageso it survives a refresh. - Quiz application — questions, answer selection, and live scoring built on arrays, objects, and control flow.
- Form validation — check inputs on
submit, block bad data withevent.preventDefault(), and show clear error messages.
Passing bar: you can open any file you wrote, explain each line out loud, and fix a bug in it without help.
On the VWC codebase
Everything here is the bedrock under the real Vets Who Code app, which runs Next.js 15 on the Pages Router with React 18 and TypeScript. The array methods, destructuring, async/await, and event handling you drill in plain JS are the exact same operations you will use in React components and in src/pages/api/ handlers — TypeScript just adds type checking on top. The fetch and Promise work maps onto how the app loads data server-side through getServerSideProps and getStaticProps. Learn it clean here in vanilla JavaScript and the framework layer stops being magic.