The previous lesson ended on a limitation: priorityWeight and describeTask are declarations with fixed names, always available and always the same. But in Nómada Tasks you are going to need something more flexible: a table that maps each status to the function that formats it, a list of validators you can walk with a loop, or a function chosen at run time depending on what Marta asked for. All of that is possible because in JavaScript a function is a value, exactly like a number or a piece of text: it can be stored in a variable, put into an array, placed inside an object and passed as an argument. In this lesson you will learn the two ways of writing functions as values —the function expression and the arrow function—, when to use each one and what the real differences between them are.
Contents
- Functions are first-class values
- The function expression
- Named function expressions
- The arrow function: the complete syntax
- Returning an object literal from an arrow
- Comparison table: declaration vs expression vs arrow
- Arrows and
this: what you need to know today - A formatters object for Nómada Tasks
- A walkable table of validators
- IIFE: the function that calls itself the moment it is born
- When to choose each form
- Common Mistakes and Tips
- Exercises
- Conclusion
- Functions are first-class values
Saying that functions are first-class citizens means the language treats them like any other value. Check it with the function from the previous lesson:
function priorityWeight(priority) {
if (priority === 'high') return 3;
if (priority === 'medium') return 2;
if (priority === 'low') return 1;
return 0;
}
// 1. It can be assigned to a variable
const calculateWeight = priorityWeight;
console.log(calculateWeight('high')); // 3
// 2. It can be put into an array
const operations = [priorityWeight];
console.log(operations[0]('medium')); // 2
// 3. It can be stored in an object
const utils = { weight: priorityWeight };
console.log(utils.weight('low')); // 1
// 4. It can be inspected
console.log(typeof priorityWeight); // 'function'
console.log(priorityWeight.name); // 'priorityWeight'
console.log(priorityWeight.length); // 1 (number of declared parameters)Look closely at the line const calculateWeight = priorityWeight;: there are no parentheses. The function is not being called, the reference to the function is being copied. Now there are two names pointing at the same piece of code. This connects directly with what you learned in Variables and Data Types: functions are reference values, like objects and arrays.
And that is the mechanism that makes everything that follows possible: if a function is a value, it can be passed to another function (you will see this in Higher-Order Functions) and it can be chosen at run time.
- The function expression
A function expression is a function written in the place where a value would go. The most common form is to assign it to a constant:
const isOverdue = function (dueDate, status, today) {
return dueDate < today && status !== 'done';
};
console.log(isOverdue('2026-09-05', 'pending', '2026-09-20')); // trueThree syntax details worth flagging:
- After
functionthere is no name. It is said to be an anonymous function. - The constant
isOverdueis what gives the function its practical name. - There is a semicolon at the end, because the whole line is an assignment (a statement), not a declaration.
The most important practical difference from a declaration is when it exists:
console.log(declared('high')); // 3 ✓ works
function declared(p) { return p === 'high' ? 3 : 1; }
console.log(expressed('high')); // ✗ ReferenceError: Cannot access 'expressed' before initialization
const expressed = function (p) { return p === 'high' ? 3 : 1; };A function declaration is ready before execution even starts; an expression assigned to const or let does not exist until its line runs. You will study the full mechanism (hoisting and the temporal dead zone) in Hoisting and the Execution Context. Today's practical consequence: with expressions, define before you use.
That restriction, which looks like a drawback, is in fact an advantage: it forces a natural top-to-bottom reading order and rules out the messy style in which functions turn up after the code that uses them.
- Named function expressions
You can give the function a name and also assign it to a variable. That is called a named function expression:
const calculateUrgency = function calculateUrgencyInner(priority, daysLeft) {
if (daysLeft < 0) return 100;
return priorityWeight(priority) * 10 - daysLeft;
};What is that internal name for, if you call it through calculateUrgency?
| Benefit | Explanation |
|---|---|
| Readable error traces | The stack of an Error shows calculateUrgencyInner instead of <anonymous> |
| Self-reference | Inside the body you can call yourself by that name, which is useful in recursion (see Recursion) |
| It does not leak outside | calculateUrgencyInner only exists inside the body; outside it gives a ReferenceError |
console.log(calculateUrgency.name); // 'calculateUrgencyInner'
console.log(typeof calculateUrgencyInner); // ✗ ReferenceErrorIn practice it is rarely used, because modern engines already infer the name from the variable:
const isOverdue = function (d, s, t) { return d < t && s !== 'done'; };
console.log(isOverdue.name); // 'isOverdue' ← inferred from the constant
- The arrow function: the complete syntax
The arrow function, introduced in ES2015, is a shorter way of writing a function expression. Its syntax has several forms depending on what you need, and it is worth knowing all of them because you will see them mixed together in any real codebase.
4.1 Block body
It is the literal translation of a function expression: you drop function and put => between the parentheses and the braces.
With braces, return is mandatory if you want to give something back.
4.2 Implicit return
If the body is a single expression, you can drop the braces and the return. The value of that expression is returned automatically:
This is the form you will see most often in professional code for small functions.
4.3 A single parameter
With exactly one parameter, the parentheses are optional:
Many teams configure their linter to always require the parentheses ((text) => ...), because that way adding a second parameter does not force a rewrite of the line. In Code Quality: ESLint and Prettier you will see how that decision gets automated.
4.4 No parameters
The empty parentheses are mandatory:
4.5 Summary of forms
| Form | Syntax | When to use it |
|---|---|---|
| Block | (a, b) => { ...; return x; } |
Several statements, guards, internal variables |
| Implicit return | (a, b) => a + b |
A single expression |
| One parameter | a => a * 2 |
Simple transformations |
| No parameters | () => 'value' |
Computed constants, factories |
| Object literal | (a) => ({ key: a }) |
See the next section |
- Returning an object literal from an arrow
Here there is a syntax trap that catches everyone out the first time. This does not work the way you expect:
const summarize = (title, hours) => { title: title, hours: hours };
console.log(summarize('Update the bookings website', 14)); // undefinedThe engine reads those braces as a code block, not as an object literal. Inside the block, title: looks like a label (like the break label from break, continue and Nested Loops), there is no return, and the function returns undefined.
The fix is to wrap the object in parentheses, so the engine knows it is an expression:
const summarize = (title, hours) => ({ title: title, hours: hours });
console.log(summarize('Update the bookings website', 14));
// { title: 'Update the bookings website', hours: 14 }A rule to remember it by: braces = block; parentheses + braces = object.
flowchart TD
A["(x) => { ... }"] --> B["The braces are a BLOCK<br/>You need an explicit return"]
C["(x) => ({ ... })"] --> D["The parentheses force an EXPRESSION<br/>It returns the object"]
- Comparison table: declaration vs expression vs arrow
This is the table worth keeping to hand until the differences stick:
| Aspect | Declarationfunction f() {} |
Expressionconst f = function () {} |
Arrowconst f = () => {} |
|---|---|---|---|
| Can be called before it is defined | Yes (full hoisting) | No (TDZ of const/let) |
No (TDZ of const/let) |
| Has a name of its own | Yes, always | Optional (anonymous or named) | No; inferred from the variable |
Own this |
Yes | Yes | No: it inherits the surrounding one |
arguments object |
Yes | Yes | No |
Can be used with new |
Yes | Yes | No (TypeError) |
| Implicit return | No | No | Yes, if the body is an expression |
| Shortest syntax | No | No | Yes |
| Typical use | A module's main functions | Functions assigned conditionally | Callbacks and one-line functions |
Three of those rows need an immediate clarification:
argumentsis a legacy object that holds every argument received even when they are not declared as parameters. You will see it in Parameters and Return Values, along with its modern replacement, rest parameters. Arrows do not have it.newis used to create objects with constructor functions; that is the subject of Prototypes and Inheritance. With an arrow it throws an error, and that is deliberate.thisdeserves its own section.
- Arrows and
this: what you need to know today
this: what you need to know todaythis is a keyword whose value depends on how the function is called. It is a topic with enough nuance to fill a whole lesson, and it has one: Object Methods and this. Today you only need to commit one sentence to memory:
An arrow function has no
thisof its own: it uses the one from the scope where it was written.
From that follow the two practical consequences you will run into:
- Do not use an arrow as an object method when the method needs to reach that object's data. The arrow will not "see" the object.
- Do use an arrow for functions passed to another function (callbacks, the event handlers of Module 6), because it keeps the surrounding
thisand that is usually exactly what you want.
A minimal example, just so you recognize the symptom when it shows up:
const board = {
title: 'Taller Nómada backlog',
withFunction: function () {
return this.title; // 'Taller Nómada backlog'
},
withArrow: () => {
return this.title; // undefined ← the arrow does not see the object
}
};
console.log(board.withFunction()); // Taller Nómada backlog
console.log(board.withArrow()); // undefinedDo not try to understand why right now: just remember the pattern. In 04-02 you will take it apart piece by piece. Nothing you write in this module depends on this.
- A formatters object for Nómada Tasks
This is where functions as values start to pay off. In Module 2 you formatted each field with a switch or with chains of if scattered around the file. Now you can group every formatter into one single object, with the advantage that each one can be replaced, reused or tested separately.
'use strict';
const TODAY = '2026-09-20';
const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const formatters = {
// '2026-09-30' → '30 Sep 2026'
date: (iso) => {
const [year, month, day] = [iso.slice(0, 4), iso.slice(5, 7), iso.slice(8, 10)];
return `${Number(day)} ${MONTHS[Number(month) - 1]} ${year}`;
},
// 'in-progress' → 'Under way'
status: (status) => {
if (status === 'pending') return 'Not started';
if (status === 'in-progress') return 'Under way';
if (status === 'done') return 'Completed';
return 'Unknown';
},
// 'high' → '🔴 High'
priority: (priority) => {
if (priority === 'high') return '🔴 High';
if (priority === 'medium') return '🟠 Medium';
if (priority === 'low') return '🟢 Low';
return '⚪ No priority';
},
// 7.5 → '7.5 h'
hours: (hours) => `${hours} h`,
// null → '—'
assignee: (name) => name ?? '—'
};
console.log(formatters.date('2026-09-30')); // 30 Sep 2026
console.log(formatters.status('in-progress')); // Under way
console.log(formatters.priority('high')); // 🔴 High
console.log(formatters.hours(7.5)); // 7.5 h
console.log(formatters.assignee(null)); // —An explanation of what this structure buys you:
- A single point of change for the whole presentation. If Marta asks for dates to be shown as
30/09/2026, you touch one line. - The formatter is chosen with a variable.
formatters[field](value)lets you format a field whose name you do not know until the program runs. That could not be done with aswitch. - Each formatter is independent. In Module 8 you will be able to test
formatters.datewithout starting up the rest of the application.
And this is how dynamic selection is used, making the most of the ?? from Basic Operators:
function formatField(field, value) {
const formatter = formatters[field];
if (!formatter) return String(value); // a field with no formatter: raw value
return formatter(value);
}
const fields = ['status', 'priority', 'date', 'hours'];
const values = ['pending', 'high', '2026-09-05', 5];
for (let i = 0; i < fields.length; i++) {
console.log(`${fields[i]}: ${formatField(fields[i], values[i])}`);
}
// status: Not started
// priority: 🔴 High
// date: 5 Sep 2026
// hours: 5 hThe
const [year, month, day] = [...]in the date formatter is array destructuring; it is covered in Array Destructuring. Here it just means "assign the first element toyear, the second tomonthand the third today".
- A walkable table of validators
The second pattern that functions-as-values unlock is a list of rules. In Module 2, validating a task was a forty-line block of chained ifs. Now each rule is an entry in an array, with its code and its message:
const validators = [
{
rule: 'R2',
message: 'The title is required.',
check: (title, hours, priority, dueDate) => title.trim().length > 0
},
{
rule: 'R3',
message: 'The hours must be between 0 and 40.',
check: (title, hours, priority, dueDate) => hours > 0 && hours <= 40
},
{
rule: 'R6',
message: 'The priority must be high, medium or low.',
check: (title, hours, priority, dueDate) =>
priority === 'high' || priority === 'medium' || priority === 'low'
},
{
rule: 'R4',
message: 'The due date cannot be earlier than today.',
check: (title, hours, priority, dueDate) => dueDate >= TODAY
}
];
function validateTask(title, hours, priority, dueDate) {
const errors = [];
for (let i = 0; i < validators.length; i++) {
const v = validators[i];
if (!v.check(title, hours, priority, dueDate)) {
errors.push(`${v.rule}: ${v.message}`);
}
}
return errors;
}
console.log(validateTask('Carpentry workshop quote', 5, 'high', '2026-09-05'));
// [ 'R4: The due date cannot be earlier than today.' ]
console.log(validateTask(' ', 60, 'urgent', '2026-10-01'));
// [ 'R2: The title is required.',
// 'R3: The hours must be between 0 and 40.',
// 'R6: The priority must be high, medium or low.' ]Read carefully what has happened here: adding a new rule no longer means touching validateTask, it means adding an object to the array. The function that validates does not know how many rules there are or what they check; it only knows how to walk them and call them. That separation between the engine and the rules is one of the most profitable ideas in programming, and in Higher-Order Functions you will take it a great deal further.
- IIFE: the function that calls itself the moment it is born
An IIFE (Immediately Invoked Function Expression) is a function that is defined and run on the spot:
(function () {
const key = 'private value';
console.log('Initializing Nómada Tasks…');
})();
// Arrow version
(() => {
console.log('The same, but shorter.');
})();The outer parentheses turn the declaration into an expression; the trailing () calls it. They were used everywhere before 2015 for one very specific reason: creating a private scope. Before let and const, any var in a file became global and could clash with another script on the page. Wrapping everything in an IIFE isolated the variables.
// The classic pattern from the jQuery years
var NomadaTasks = (function () {
var counter = 0; // invisible from outside
return {
nextId: function () { return ++counter; }
};
})();
console.log(NomadaTasks.nextId()); // 1
console.log(NomadaTasks.nextId()); // 2
console.log(typeof counter); // 'undefined' → it is protectedIs it still needed? Almost never. Today you have two better tools:
| Need | Old solution | Modern solution |
|---|---|---|
| Isolating a file's variables | IIFE | ES modules (05-04) |
| Isolating a block's variables | IIFE | A { } block with let/const |
| Private state inside a function | IIFE | Closures (03-04) |
It is worth recognizing the pattern because it shows up in an awful lot of legacy code, but in new code the norm is not to write it.
- When to choose each form
In professional practice, the choice comes down to these guidelines:
| Situation | Recommended form | Reason |
|---|---|---|
A file's main function (validateTask, createTask) |
Declaration or const + expression |
A clear name in the traces; either works, pick a style and be consistent |
| A short one-line callback | Arrow with implicit return | Maximum brevity and readability |
| A function stored in a configuration object | Arrow | It is a value among other values |
| An object method that uses the object's data | function (or method syntax) |
It needs its own this (see 04-02) |
| A recursive function | Declaration | The name is guaranteed inside the body |
| A DOM event handler that uses the element | It depends | In Module 6 you will see both cases |
And a style recommendation that most teams follow: use const + arrow by default, and a declaration only when you want to make a file's main function stand out. What matters is not which one you pick, but that the whole file is consistent.
Common Mistakes and Tips
1. Calling an expression before defining it.
With expressions and arrows: define at the top, use below. Always.
2. Forgetting the return when you use braces in an arrow.
const double = (n) => { n * 2; }; // ✗ returns undefined
const double = (n) => { return n * 2; }; // ✓
const double = (n) => n * 2; // ✓ better still3. Returning an object without parentheses. You have already seen this in section 5: (x) => ({ ... }).
4. Forgetting the semicolon after a function expression. It usually breaks nothing thanks to ASI, but it can produce very strange errors if the next line starts with ( or [.
5. Using an arrow as a method that needs this. The symptom is an inexplicable undefined when reading a property of the object. If it happens to you in Module 4, go back to section 7.
6. Confusing myFunction with myFunction() when storing it.
const formatters = { status: statusLabel() }; // ✗ stores the RESULT
const formatters = { status: statusLabel }; // ✓ stores the FUNCTIONThis mistake is especially hard to spot because it is not a syntax error: you simply store undefined or a piece of text where you expected a function, and the failure turns up lines later with a TypeError: ... is not a function.
7. Tip: if the arrow takes more than two lines, give it braces. An implicit return with enormously long expressions and chained ternaries is hard to read and worse to debug (you cannot drop a console.log in the middle without rewriting it).
8. Tip: name your functions even when they are anonymous. Assigning them to a const with a descriptive name is not just style: it is what makes the error messages of Module 8 readable.
Exercises
Exercise 1 — From declarations to arrows
Rewrite these three functions as arrows, using the shortest possible form in each case, and check that they give the same result.
function isDone(status) {
return status === 'done';
}
function effort(priority, hours) {
return priorityWeight(priority) * hours;
}
function shortSummary(title, assignee, hours) {
const name = assignee === null ? 'unassigned' : assignee;
return `${title} (${name}, ${hours} h)`;
}Exercise 2 — A table of comparators
Create a comparators object whose keys are 'byHours', 'byDueDate' and 'byPriority', and whose values are functions that receive the data of two tasks (by index) and return -1, 0 or 1 depending on which should come first. Then write mostUrgent(criterion, ...), which walks the backlog with a loop and returns the index of the task that comes first according to the chosen criterion. Do not use sort (it arrives in 04-05).
Exercise 3 — Spot the fault
Each of these fragments has a bug related to what this lesson covered. Identify it and fix it.
// a)
const label = (status) => { status.toUpperCase() };
console.log(label('done'));
// b)
const newTask = (title, hours) => { title: title, hours: hours };
console.log(newTask('Signage', 6));
// c)
console.log(weightOf('high'));
const weightOf = (p) => (p === 'high' ? 3 : 1);
// d)
const actions = { print: console.log('Hello') };
actions.print('Goodbye');Solutions
Exercise 1
'use strict';
const priorityWeight = (priority) => {
if (priority === 'high') return 3;
if (priority === 'medium') return 2;
if (priority === 'low') return 1;
return 0;
};
// One parameter, one expression → minimal form
const isDone = (status) => status === 'done';
// Two parameters, one expression → implicit return
const effort = (priority, hours) => priorityWeight(priority) * hours;
// It needs an intermediate variable → block body
const shortSummary = (title, assignee, hours) => {
const name = assignee ?? 'unassigned';
return `${title} (${name}, ${hours} h)`;
};
console.log(isDone('done')); // true
console.log(effort('high', 12)); // 36
console.log(shortSummary('Update the bookings website', null, 14));
// Update the bookings website (unassigned, 14 h)Comment: the third one should not be written with an implicit return. It would fit on one line using ??, but the block makes it clear that there are two steps and lets you add a debugging console.log without rewriting anything. Notice too that assignee ?? 'unassigned' replaces the ternary: the ?? from Basic Operators covers null and undefined, which is exactly the case R8 describes.
Exercise 2
const ids = [1, 2, 3, 4, 5, 6];
const priorities = ['high', 'medium', 'high', 'low', 'medium', 'high'];
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'];
const comparators = {
// Most hours first
byHours: (a, b) => {
if (hours[a] > hours[b]) return -1;
if (hours[a] < hours[b]) return 1;
return 0;
},
// Nearest date first (ISO strings compare directly)
byDueDate: (a, b) => {
if (dueDates[a] < dueDates[b]) return -1;
if (dueDates[a] > dueDates[b]) return 1;
return 0;
},
// Highest priority weight first
byPriority: (a, b) => {
const wa = priorityWeight(priorities[a]);
const wb = priorityWeight(priorities[b]);
if (wa > wb) return -1;
if (wa < wb) return 1;
return 0;
}
};
function mostUrgent(criterion) {
const compare = comparators[criterion];
if (!compare) throw new Error(`Unknown criterion: ${criterion}`);
let best = 0;
for (let i = 1; i < ids.length; i++) {
if (compare(i, best) < 0) best = i;
}
return best;
}
console.log(ids[mostUrgent('byHours')]); // 3 (14 h)
console.log(ids[mostUrgent('byDueDate')]); // 6 (2026-09-05)
console.log(ids[mostUrgent('byPriority')]); // 1 (the first 'high' found)Comment: the "maximum with a flag" pattern is the same one from Loops, but now the comparison criterion comes in as data. Changing the order of the backlog no longer requires rewriting the loop. The throw for the unknown criterion applies the fail-fast principle from Error Handling.
Exercise 3
| Fragment | Bug | Fix |
|---|---|---|
| a) | Braces with no return → it returns undefined |
const label = (status) => status.toUpperCase(); |
| b) | Object literal without parentheses → the braces are a block | const newTask = (title, hours) => ({ title, hours }); |
| c) | An expression used before it is defined → ReferenceError |
Move the definition above the call |
| d) | It stores the result of console.log('Hello') (undefined), not the function |
const actions = { print: console.log }; |
In b) the shorthand { title, hours } appears, equivalent to { title: title, hours: hours }; it is covered in Introduction to Objects. In d), the real symptom is a TypeError: actions.print is not a function on the next line, far from the true bug: a reminder that the point where things blow up and the point where the fault lives rarely coincide.
Conclusion
You have learned that in JavaScript a function is a first-class value: you can assign it to a variable, store it in an array or in an object, compare it by reference and pass it as an argument. That property, which looks like a technical detail, is what makes possible the two patterns you have already put to work in Nómada Tasks: the formatters object, which centralizes all field presentation and lets you pick the formatter with a variable, and the validators array, which turns each business rule into a piece of data and leaves validateTask entirely unconcerned with how many rules exist.
You know the three ways of writing a function and their real differences: the declaration, available before its own line; the function expression, anonymous or with an internal name, which only exists from its assignment onward; and the arrow function, with its block-body, implicit-return, single-parameter and no-parameter variants, plus the parentheses trick for returning an object literal. You know that an arrow has no this of its own, no arguments and cannot be used with new, and that you will close the first of those points in Object Methods and this. And you recognize the IIFE, the pattern that isolated variables before let, const and modules existed.
Something has been getting uncomfortable along the way. The validators in section 9 take four parameters even though each one uses only a single one; describeTask from the previous lesson took eight; and you still do not know what happens if someone calls a function with fewer arguments than were declared, nor how to give reviewer a default value when it is not supplied. All of that —how data comes in and how results go out— is the subject of Parameters and Return Values, where you will build createTask() with default values and summarizeWorkload() returning several totals at once.
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
