You closed Module 2 with a legitimate complaint: you had written the chain that turns priority into a weight four times, the transition validation appeared twice, and the forty-line block that validates a new task was useless for validating an edited task unless you copied the whole thing. This lesson solves exactly that. You are going to learn to wrap a piece of logic under a name, store it and run it as many times as you like with different data each time. It is the most important concept in the course so far: from here on, Nómada Tasks stops being a linear script and becomes a set of named pieces you can combine, read and —in Module 8— test separately.
Contents
- The real problem: code that repeats
- Your first function: the declaration
- What happens exactly on each call
- The precise vocabulary, in one table
- Parameters: the same logic with different data
return: leaving with a value- Functions with no parameters and functions with no return value
- How to name a function well
- The single responsibility rule
- The call stack, intuitively
- Case study: refactoring Module 2
- Common Mistakes and Tips
- Exercises
- Conclusion
- The real problem: code that repeats
Take a fragment from Module 2. This block turns a priority into a numeric weight so the weighted effort of the backlog can be calculated:
let weight;
if (priority === 'high') {
weight = 3;
} else if (priority === 'medium') {
weight = 2;
} else if (priority === 'low') {
weight = 1;
} else {
weight = 0;
}Nine lines. The problem is not the length: it is that that exact block appeared in four different places in the previous module's code. And that has three very concrete consequences:
- The change multiplies. If tomorrow Marta decides that high priority is worth
5instead of3, four places have to be touched. You will touch three and the fourth will be left wrong. - The mistake multiplies. If in one of the copies you wrote
'meduim'with the letters swapped, you have a fault that only shows up in one part of the program. - The intent is lost. When you read the file you see nine lines of
if, you do not see "this is where the weight of a priority is calculated". The what is buried under the how.
A function solves all three at once:
function priorityWeight(priority) {
if (priority === 'high') return 3;
if (priority === 'medium') return 2;
if (priority === 'low') return 1;
return 0;
}From now on, in those four places you write priorityWeight(priority). The logic lives in one single place, it changes in one single place, and the name says what it does without anyone having to read the inside.
A function is to logic what a variable is to a value: giving something a name so you can refer to it without repeating it.
- Your first function: the declaration
The most basic way to create a function is called a function declaration:
Piece by piece:
| Part | In the example | What it is |
|---|---|---|
| Keyword | function |
Tells the engine: "what follows is a function" |
| Name | greetTheTeam |
The identifier you will call it by |
| Parentheses | () |
This is where the parameters go (here, none) |
| Braces | { ... } |
They delimit the body: the code that will run |
Notice an important detail: writing that declaration prints nothing. Defining a function is like writing a recipe down on paper; nothing gets cooked until someone follows it. To run it you have to call it, and calling it means writing its name followed by parentheses:
function greetTheTeam() {
console.log('Welcome to the Nómada Tasks board.');
}
greetTheTeam(); // Welcome to the Nómada Tasks board.
greetTheTeam(); // Welcome to the Nómada Tasks board.The parentheses are mandatory and they are what tells using the function apart from naming it:
greetTheTeam; // Does nothing: it is just a reference to the function
greetTheTeam(); // This actually runs itThis is one of the most frequent slips when you start out. If you call a function and "nothing happens", the first thing to look at is the parentheses.
A note on order: function declarations can be called before the line where they are written. The engine prepares them before running the file. This is called hoisting and you will study it in detail in Hoisting and the Execution Context; for now, write the functions at the top and the calls at the bottom, which is the most readable arrangement.
- What happens exactly on each call
When you write priorityWeight('high'), the engine goes through this sequence:
sequenceDiagram
participant P as Main code
participant F as priorityWeight
P->>P: Evaluates the argument 'high'
P->>F: Jumps into the body with priority = 'high'
Note over F: Runs the body<br/>line by line
F-->>P: return 3
Note over P: The call is replaced<br/>by the value 3
P->>P: Carries on with the next line
Four ideas worth being very clear about from the start:
- Arguments are evaluated before entering. In
priorityWeight(priorities[i]),priorities[i]is resolved first (to'high', say) and it is that already-resolved value that goes in. - Execution of the calling code stops while the function works, and resumes exactly where it was when the function finishes.
- Every call is independent. Variables declared inside the body are born on entry and disappear on exit. Two calls share nothing. You will formalize this in Scope and Closures.
- A call is an expression: it can be used anywhere a value fits.
const weight = priorityWeight('medium'); // in an assignment
console.log(`Weight: ${priorityWeight('high')}`); // inside a template literal
if (priorityWeight(priorities[0]) >= 3) { /* ... */ } // inside a condition
const effort = priorityWeight('high') * 12; // inside an operationThat last line is worth reading slowly: the call returns 3, the expression becomes 3 * 12 and effort ends up being 36. The function is replaced by what it returns.
- The precise vocabulary, in one table
When talking about functions, six terms come up that are worth not confusing, because error messages and documentation use them precisely:
| Term | What it is | In the example |
|---|---|---|
| Declaration | The code that creates the function | function priorityWeight(priority) { ... } |
| Name | The function's identifier | priorityWeight |
| Parameter | The variable declared inside the parentheses in the definition | priority |
| Argument | The real value you pass when calling | 'high' in priorityWeight('high') |
| Body | The block between braces that runs | The four if/return lines |
| Call / invocation | The act of running it | priorityWeight('high') |
| Return value | What the call produces | 3 |
The parameter / argument distinction is the one that gets confused most and the most useful one. A mnemonic: the parameter lives in the pattern (the definition); the argument is the actual value of one specific call.
// ↓ parameter (it exists once, in the definition)
function isTaskOverdue(dueDate, status, today) {
return dueDate < today && status !== 'done';
}
// ↓ arguments (different on every call)
isTaskOverdue('2026-09-05', 'pending', '2026-09-20'); // true
isTaskOverdue('2026-11-05', 'in-progress', '2026-09-20'); // false
- Parameters: the same logic with different data
Parameters are what turn a function into a reusable tool rather than a simple shortcut. They are declared separated by commas and behave like local variables that already come with a value:
// Returns the title trimmed to a maximum number of characters.
function shortenTitle(title, maximum) {
if (title.length <= maximum) {
return title;
}
return title.slice(0, maximum - 1) + '…';
}
console.log(shortenTitle('Redesign the multipurpose room', 20));
// Redesign the multip…
console.log(shortenTitle('Carpentry workshop quote', 40));
// Carpentry workshop quoteTwo key details in that example:
- Order matters.
shortenTitle(20, 'Redesign…')does not fail with a clear message: it tries to read.lengthof a number, getsundefined, comparesundefined <= 'Redesign…'and returns something absurd. Arguments are matched by position, not by name. titleandmaximumonly exist inside the body. Outside the function they are not declared; if you try to use them, you will get aReferenceError.
return: leaving with a value
return: leaving with a valuereturn does two things at once, and both of them matter:
- It ends the function immediately. No later line of the body runs.
- It hands a value back to the point the call was made from.
function classifyWorkload(hours) {
if (hours > 40) return 'overloaded';
if (hours > 30) return 'high';
if (hours > 15) return 'normal';
return 'room to spare';
console.log('This is NEVER printed'); // dead code
}
console.log(classifyWorkload(25)); // normal
console.log(classifyWorkload(48)); // overloadedThis style —a chain of returns instead of an if/else if/else with a temporary variable— is the function version of the guard clauses you learned in Conditional Statements. Each line reads as an independent rule, with no nesting.
In Parameters and Return Values you will go deeper into what to return, how to return several pieces of data at once and why it is a good idea to always return the same type. For now the mechanics are enough.
- Functions with no parameters and functions with no return value
Not every function takes data in and not every function gives something back. All four combinations are legitimate and are used for different things:
| Takes input | Returns | What it is for | Example |
|---|---|---|---|
| Yes | Yes | Calculating or transforming | priorityWeight('high') |
| Yes | No | Causing an effect | printTask(task) |
| No | Yes | Producing something new each time | todaysDate() |
| No | No | Running a fixed routine | showHeader() |
// No parameters, with a return value: it always produces the same format
function todaysDate() {
return '2026-09-20'; // in the project we use a fixed date so we can reason about it
}
// With parameters, no return value: its worth lies in the effect it produces
function printSeparator(title) {
console.log('');
console.log(`── ${title} ${'─'.repeat(40 - title.length)}`);
}
printSeparator('Taller Nómada backlog');A function with no return does not return "nothing": it returns undefined. This is a source of confusion, so check it for yourself:
const result = printSeparator('Test');
console.log(result); // undefined
console.log(typeof result); // undefinedAnd watch out for the classic trap: a function that does console.log does not return what it prints.
function badCalculation(hours) {
console.log(hours * 2); // it prints, but it does not return
}
const double = badCalculation(12); // prints 24
console.log(double + 1); // NaN ← undefined + 1A practical rule: console.log is for looking; return is for using. If another part of the program is going to consume the value, it has to come out through return.
- How to name a function well
A function's name is free documentation. The universal convention in JavaScript is verb + noun, in camelCase:
| Kind of function | Naming pattern | Examples from the project |
|---|---|---|
| Calculates and returns a value | calculate…, get…, count… |
calculateUrgency, getWorkloadFor, countPending |
| Returns a boolean | is…, has…, can… |
isTaskOverdue, isComplete, hasTag, canChangeStatus |
| Turns data into another format | format…, describe…, to… |
formatDate, describeTask, toIsoDate |
| Causes an effect | print…, save…, log… |
printBacklog, saveTask, logChange |
| Checks and throws if it fails | validate…, assert… |
validateTask, assertValidHours |
And the names to avoid, with the reason why:
| Bad name | Problem |
|---|---|
doThing(), process() |
It does not say what it does; any code at all fits in there |
data(), task() |
A bare noun looks like a variable, not an action |
check() |
Ambiguous: does it return a boolean or does it throw an error? |
calculateUrgencyAndSave() |
The "and" warns you that it does two things (see section 9) |
fn2(), aux() |
Meaningless names; in three weeks nobody will know what they are |
One nuance that saves a lot of mistakes: functions that return a boolean are named as an affirmative question, so that using them in an if reads like a sentence.
if (isTaskOverdue('2026-09-05', 'pending', TODAY)) {
console.warn('R10: overdue task.');
}
// it reads: "if the task is overdue, warn"
- The single responsibility rule
A function should do one single thing and should be explainable in one sentence without the word "and". Here is a real example of what happens when the rule is ignored:
// ✗ It does four things: calculate, format, print and warn
function processTask(title, priority, dueDate, status, hours, today) {
let weight;
if (priority === 'high') weight = 3;
else if (priority === 'medium') weight = 2;
else if (priority === 'low') weight = 1;
else weight = 0;
const effort = weight * hours;
const overdue = dueDate < today && status !== 'done';
const badge = status === 'done' ? '✓' : '○';
console.log(`${badge} ${title} · effort ${effort}`);
if (overdue) console.warn(`⚠ ${title} is overdue`);
}That function cannot be reused for anything else: if you only want the effort, without printing, you cannot have it. If you want to know whether it is overdue so you can paint it red in Module 6, you cannot either. It is all welded together.
Now the same logic split into pieces:
flowchart TD
A["processTask (all in one)"] --> B["priorityWeight<br/>priority → number"]
A --> C["isOverdue<br/>date + status → boolean"]
A --> D["describeTask<br/>data → text"]
A --> E["printTask<br/>text → console"]
B --> F["Reusable in the<br/>effort calculation"]
C --> G["Reusable in the<br/>warnings and in the filter"]
D --> H["Reusable in the<br/>console and in the DOM"]
Three questions to work out whether a function has too many responsibilities:
- Does its name need an "and"? → split it.
- Is its body longer than 20-25 lines? → there are probably two ideas inside.
- Does it calculate something and also print it or save it? → separate the calculation from the effect.
- The call stack, intuitively
When one function calls another, the engine has to remember where to go back to. It does so with a structure called the call stack: a stack of plates where each call adds a plate on top and each return removes the top one.
function priorityWeight(priority) {
if (priority === 'high') return 3;
if (priority === 'medium') return 2;
if (priority === 'low') return 1;
return 0;
}
function taskEffort(priority, hours) {
return priorityWeight(priority) * hours;
}
function taskReport(title, priority, hours) {
const effort = taskEffort(priority, hours);
return `${title}: ${effort} points`;
}
console.log(taskReport('Redesign the multipurpose room', 'high', 12));
// Redesign the multipurpose room: 36 pointsThis is how the stack grows and empties during that single line:
flowchart LR
subgraph P1["1 · Initial call"]
A1["taskReport"]
A0["(global)"]
end
subgraph P2["2 · taskEffort is entered"]
B2["taskEffort"]
B1["taskReport"]
B0["(global)"]
end
subgraph P3["3 · priorityWeight is entered"]
C3["priorityWeight"]
C2["taskEffort"]
C1["taskReport"]
C0["(global)"]
end
subgraph P4["4 · Everything has returned"]
D0["(global)"]
end
P1 --> P2 --> P3 --> P4
What matters today is the intuition: the last one in is the first one out, and the program always returns exactly to the point where it left off. That stack is also the one that shows up in the error messages from Module 2 (at priorityWeight, at taskEffort, …): the stack of an Error is literally a snapshot of this stack. In Hoisting and the Execution Context you will see the mechanism from the inside and what happens when the stack fills up.
- Case study: refactoring Module 2
Let us bring back the canonical Taller Nómada backlog and rewrite with functions the logic that was duplicated in Module 2. You are still working with parallel arrays —real objects and arrays arrive in Module 4—, but notice how much the final loop clears up.
'use strict';
const TODAY = '2026-09-20';
const ids = [1, 2, 3, 4, 5, 6];
const titles = [
'Redesign the multipurpose room',
'Signage for the screen-printing workshop',
'Update the bookings website',
'Screen-printing ink inventory',
'Bookbinding guide for residents',
'Carpentry workshop quote'
];
const assignees = ['Iván', 'Marta', 'Lucía', 'Marta', 'Iván', 'Iván'];
const priorities = ['high', 'medium', 'high', 'low', 'medium', 'high'];
const statuses = ['in-progress', 'pending', 'pending', 'done', 'in-progress', 'pending'];
const hours = [12, 6, 14, 3, 8, 5];
const dueDates = [
'2026-09-30', '2026-10-15', '2026-10-02',
'2026-09-12', '2026-11-05', '2026-09-05'
];
// ─── The pieces with names of their own ────────────────────────────
// Turns a priority into its numeric weight. A business rule.
function priorityWeight(priority) {
if (priority === 'high') return 3;
if (priority === 'medium') return 2;
if (priority === 'low') return 1;
return 0; // unknown priority: it adds nothing
}
// R10: overdue = due date in the past and the task is not finished.
function isOverdue(dueDate, status, today) {
return dueDate < today && status !== 'done';
}
// Visual symbol for the status.
function statusBadge(status) {
if (status === 'done') return '✓';
if (status === 'in-progress') return '▸';
return '○';
}
// Builds the text line for a task. It does NOT print: it returns.
function describeTask(id, title, assignee, priority, status, estimatedHours, dueDate, today) {
const badge = statusBadge(status);
const warning = isOverdue(dueDate, status, today) ? ' ⚠ OVERDUE' : '';
return `${badge} [${id}] ${title} · ${assignee} · ${priority} · ${estimatedHours} h${warning}`;
}
// ─── The program, now readable ─────────────────────────────────────
let totalEffort = 0;
for (let i = 0; i < ids.length; i++) {
console.log(
describeTask(
ids[i], titles[i], assignees[i], priorities[i],
statuses[i], hours[i], dueDates[i], TODAY
)
);
totalEffort += priorityWeight(priorities[i]) * hours[i];
}
console.log(`Total weighted effort: ${totalEffort}`); // 124Output:
▸ [1] Redesign the multipurpose room · Iván · high · 12 h ○ [2] Signage for the screen-printing workshop · Marta · medium · 6 h ○ [3] Update the bookings website · Lucía · high · 14 h ✓ [4] Screen-printing ink inventory · Marta · low · 3 h ▸ [5] Bookbinding guide for residents · Iván · medium · 8 h ○ [6] Carpentry workshop quote · Iván · high · 5 h ⚠ OVERDUE Total weighted effort: 124
Compare that loop body with the Module 2 one. Before, there was a twenty-line switch, a ternary for the badge and another one for the warning, all mixed together. Now the loop has two statements and both read like plain English sentences: "describe task i" and "accumulate the weight times the hours".
And notice the most valuable detail: describeTask returns the text instead of printing it. That is what will let you, in Module 6, use exactly the same function to paint the task on the web page without changing a single line of its body.
About those eight parameters: yes, there are too many. It is the unavoidable symptom of working with parallel arrays. In Parameters and Return Values you will see how to group related data into a single argument, and in Module 4 the task will become an object and the signature will simply be
describeTask(task, today).
Common Mistakes and Tips
1. Calling without parentheses.
console.log(priorityWeight); // ƒ priorityWeight(priority) { ... }
console.log(priorityWeight('high')); // 3If you see something starting with ƒ or [Function: ...] in the console, you left out the parentheses.
2. Forgetting the return and expecting a value. A function with no return returns undefined. If your later calculations come out as NaN, suspect this before anything else.
3. Confusing printing with returning. console.log inside the function is there for debugging, not for communicating the result to the rest of the program.
4. Putting code after the return. It never runs. Modern editors gray it out (unreachable code).
5. A line break after return. This one is especially treacherous because of the automatic semicolon insertion (ASI) you studied in JavaScript Syntax and Basic Concepts:
function badReturn(hours) {
return // ← ASI inserts a ';' here
hours * 2; // ← dead code
}
console.log(badReturn(12)); // undefinedThe value you return has to start on the same line as return.
6. Depending on global variables instead of parameters.
// ✗ It depends on TODAY, a variable from outside: it cannot be tested with another date
function isOverdueBad(dueDate, status) {
return dueDate < TODAY && status !== 'done';
}
// ✓ Everything it needs comes in through parameters
function isOverdue(dueDate, status, today) {
return dueDate < today && status !== 'done';
}The second version can be tested with any date; the first one only works if TODAY exists and always gives the same result. This idea is formalized in 03-03 with pure functions.
7. Tip: write the call first. Before programming the body, write how you would like to use the function (describeTask(task, today)) and what it should return. Designing from the point of use produces far more comfortable signatures.
8. Tip: one function, one level of detail. If inside a function you mix business concepts (priority === 'high') with formatting details ('─'.repeat(40)), that is a sign that there are two functions in there.
Exercises
Exercise 1 — Three basic project functions
Write three functions and try them out on the backlog:
remainingHours(estimatedHours, status): returns0if the status is'done'andestimatedHoursin any other case.statusLabel(status): returns'Not started','Under way'or'Completed'; for any other value,'Unknown'.isImminent(dueDate, status, today, graceDays): returnstrueif the task is not done and its due date is on or aftertodaybut falls within the nextgraceDaysdays. Hint: you can compare ISO strings if you build the upper bound by adding days withDate.
Exercise 2 — Splitting a function that does too much
This function breaks single responsibility. Split it into at least three functions with correct names and rewrite the loop that used it.
function assigneeReport(assignee, assignees, hours, statuses) {
let total = 0;
let open = 0;
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] === assignee) {
total += hours[i];
if (statuses[i] !== 'done') open += hours[i];
}
}
const workloadStatus = open > 40 ? 'OVERLOADED' : 'OK';
console.log(`${assignee}: ${open} h open of ${total} h — ${workloadStatus}`);
}Exercise 3 — Tracing the stack
Without running it, write the exact order in which the lines are printed and draw the call stack at the moment 'C enters' is printed.
function c() { console.log('C enters'); console.log('C leaves'); }
function b() { console.log('B enters'); c(); console.log('B leaves'); }
function a() { console.log('A enters'); b(); console.log('A leaves'); }
a();
console.log('End');Solutions
Exercise 1
'use strict';
const TODAY = '2026-09-20';
function remainingHours(estimatedHours, status) {
if (status === 'done') return 0;
return estimatedHours;
}
function statusLabel(status) {
if (status === 'pending') return 'Not started';
if (status === 'in-progress') return 'Under way';
if (status === 'done') return 'Completed';
return 'Unknown';
}
function isImminent(dueDate, status, today, graceDays) {
if (status === 'done') return false;
if (dueDate < today) return false; // already overdue, not imminent
const cutoff = new Date(today);
cutoff.setDate(cutoff.getDate() + graceDays);
const cutoffIso = cutoff.toISOString().slice(0, 10);
return dueDate <= cutoffIso;
}
console.log(remainingHours(3, 'done')); // 0
console.log(remainingHours(12, 'in-progress')); // 12
console.log(statusLabel('in-progress')); // Under way
console.log(statusLabel('archived')); // Unknown
console.log(isImminent('2026-09-30', 'in-progress', TODAY, 15)); // true
console.log(isImminent('2026-11-05', 'in-progress', TODAY, 15)); // false
console.log(isImminent('2026-09-05', 'pending', TODAY, 15)); // false (overdue)Comment: each of the three has a single responsibility, all of them receive everything they need through parameters (including today) and none of them prints anything. Notice the first guard in isImminent: telling "overdue" apart from "imminent" stops R10 and the urgency warning from stepping on each other.
Exercise 2
// 1. Adds up one assignee's hours, with the option of counting only the open ones.
function sumHours(assignee, openOnly, assignees, hours, statuses) {
let total = 0;
for (let i = 0; i < assignees.length; i++) {
if (assignees[i] !== assignee) continue;
if (openOnly && statuses[i] === 'done') continue;
total += hours[i];
}
return total;
}
// 2. Applies R7: nobody goes over 40 open hours per week.
function isOverloaded(openHours) {
return openHours > 40;
}
// 3. Builds the report text (it does not print).
function describeWorkload(assignee, openHours, totalHours) {
const badge = isOverloaded(openHours) ? 'OVERLOADED' : 'OK';
return `${assignee}: ${openHours} h open of ${totalHours} h — ${badge}`;
}
const team = ['Iván', 'Marta', 'Lucía'];
for (let i = 0; i < team.length; i++) {
const open = sumHours(team[i], true, assignees, hours, statuses);
const total = sumHours(team[i], false, assignees, hours, statuses);
console.log(describeWorkload(team[i], open, total));
}
// Iván: 25 h open of 25 h — OK
// Marta: 6 h open of 9 h — OK
// Lucía: 14 h open of 14 h — OKComment: now isOverloaded can be reused in the assignment form, and describeWorkload will serve as it stands for painting a row in Module 6. The continue is the same one you learned in break, continue and Nested Loops, but now it lives inside a reusable piece.
Exercise 3
Output:
The stack at the instant of 'C enters' (top to bottom): c → b → a → global context. All three functions are "open" at the same time; none of them has finished. The lines B leaves and A leaves show that the program returns exactly to the point where it left off.
Conclusion
You have made the most important conceptual leap of the course so far. A function is a named piece of logic: it is declared once with function name(parameters) { body } and called as many times as necessary with name(arguments). On each call, the arguments go in by position, the body runs top to bottom and return ends the function handing back a value; with no return, the value handed back is undefined.
You now handle the vocabulary precisely —declaration, call, parameter, argument, body, return— and you know that the parameter lives in the pattern while the argument changes on every call. You know how to name functions as verb + noun, to reserve the is…/has…/can… prefixes for the ones that return booleans, and to spot from the name when a function is doing too much. And you have a solid intuition for the call stack: every call pushes a context and every return pops it, always in reverse order.
Above all, you have seen the practical effect: the duplicated logic from Module 2 has turned into priorityWeight(), isOverdue(), statusBadge() and describeTask(), and the loop that walks the Taller Nómada backlog has gone from thirty tangled lines to two readable statements. The weighted effort still comes out as 124, but now the rule that produces it lives in one single place.
One obvious limitation remains. Every function in this lesson is a declaration with a fixed name: they exist from the moment the file loads and they are always called the same thing. But very often you will need something more flexible: storing a function inside a variable, putting several of them into a table so you can pick one according to the priority, or passing a function as an argument to another one. For that you have to understand that in JavaScript a function is just another value, like a number or a piece of text. That is what you will see in Function Expressions and Arrow Functions, where priorityWeight will stop being a lone declaration and become an entry in a table of formatters and validators for Nómada Tasks.
JavaScript Course: From Beginner to Advanced
Module 1: Introduction to JavaScript
- What Is JavaScript?
- Setting Up Your Development Environment
- Your First JavaScript Program
- JavaScript Syntax and Basic Concepts
- Variables and Data Types
- Basic Operators
- Type Conversion and Comparisons
- The Course Project: Nómada Tasks
Module 2: Control Structures
- Conditional Statements
- Loops: for, while, do-while
- Switch Statements
- Flow Control: break, continue and Nested Loops
- Error Handling with try-catch
Module 3: Functions
- Defining and Calling Functions
- Function Expressions and Arrow Functions
- Parameters and Return Values
- Scope and Closures
- Hoisting and the Execution Context
- Higher-Order Functions
- Recursion
Module 4: Objects and Arrays
- Introduction to Objects
- Object Methods and the
thisKeyword - Arrays: Basics and Methods
- Iterating over Arrays
- Searching, Sorting and Aggregating Data: find, sort and reduce
- Array Destructuring
- Object Destructuring, Spread and Rest
- JSON and Copying Objects
Module 5: Advanced Objects and Functions
- Prototypes and Inheritance
- Classes and Object-Oriented Programming
- Encapsulation: Getters, Setters and Private Fields
- Modules: Import and Export
- Asynchronous JavaScript: Callbacks
- Promises and Async/Await
- The Event Loop and the Microtask Queue
- Iterators and Generators
Module 6: The Document Object Model (DOM)
- Introduction to the DOM
- Selecting and Manipulating DOM Elements
- Handling Events
- Propagation, Delegation and Custom Events
- Creating and Removing DOM Elements
- Rendering Lists and HTML Templates
- Handling and Validating Forms
Module 7: Browser APIs and Advanced Topics
- Local and Session Storage
- The Fetch API and AJAX
- Robust Requests: Errors, Timeouts and AbortController
- WebSockets
- Service Workers and Progressive Web Apps (PWAs)
- Essential Browser APIs
- Introduction to WebAssembly
Module 8: Testing and Debugging
- Debugging JavaScript
- Code Quality: ESLint, Prettier and Conventions
- Unit Testing with Jest
- Test Doubles: Mocks, Stubs and Spies
- Integration Testing
- End-to-End Testing with Cypress
Module 9: Performance and Optimization
- Measure Before You Optimize: DevTools and Web Vitals
- Optimizing JavaScript Performance
- Memory Management
- Efficient DOM Manipulation
- Lazy Loading and Code Splitting
Module 10: JavaScript Frameworks and Libraries
- Why Frameworks Exist
- Introduction to React
- State Management with Redux
- Vue.js Basics
- Angular Basics
- Choosing the Right Framework
