JavaScript and TypeScript course Β· Module 2: Functions, Arrays and Objects
Arrow Functions
In this lesson7
Welcome back to Jurassic Park! Today we will get to know arrow functions - a modern way of creating functions in JavaScript that was introduced in ES6. Arrow functions are more concise than traditional function expressions and have a few unique properties that make them extremely useful in everyday work.
Basic Syntax of Arrow Functions
Arrow functions have a more concise syntax than traditional functions. Let's compare:
Traditional function:
1// Traditional anonymous function
2const identifyDinosaur = function(species) {
3 return "Species identified: " + species;
4};Arrow function:
1// Arrow function
2const identifyDinosaur = (species) => {
3 return "Species identified: " + species;
4};We can see that the arrow function:
- Does not use the
functionkeyword - Uses the arrow sign
=>between the parameters and the function body
Even More Concise
Arrow functions offer an even shorter syntax in the following cases:
One parameter: you can omit the parentheses
1// With parentheses
2const getSpeciesInfo = (species) => {
3 return `The species ${species} was found in the database.`;
4};
5
6// Without parentheses - the same thing
7const getSpeciesInfoShort = species => {
8 return `The species ${species} was found in the database.`;
9};One-line function: implicit return
If the function body consists of only a single expression that returns a value, we can omit the curly braces and the return keyword:
1// With return
2const getSpeciesAge = (species) => {
3 return species === "Tyrannosaurus" ? "68 million years" : "Age unknown";
4};
5
6// With an implicit return - the same thing
7const getSpeciesAgeShort = (species) => species === "Tyrannosaurus" ? "68 million years" : "Age unknown";Returning an object
When returning an object, we have to put parentheses around it so that it is not interpreted as a block of code:
1// Incorrect - JavaScript will think it is a block of code
2// const getDinoData = (name, species) => { name: name, species: species };
3
4// Correct - the object is wrapped in parentheses
5const getDinoData = (name, species) => ({ name: name, species: species });
6
7const blue = getDinoData("Blue", "Velociraptor");
8console.log(blue); // { name: "Blue", species: "Velociraptor" }Practical Uses of Arrow Functions
1. Processing Data Collections
Arrow functions are especially handy with array methods such as map, filter, reduce:
1// List of dinosaurs
2const dinosaurs = [
3 { name: "Rex", species: "Tyrannosaurus", carnivore: true, weight: 7000 },
4 { name: "Spike", species: "Stegosaurus", carnivore: false, weight: 5000 },
5 { name: "Blue", species: "Velociraptor", carnivore: true, weight: 100 },
6 { name: "Trixie", species: "Triceratops", carnivore: false, weight: 8000 }
7];
8
9// Filtering the carnivores
10const carnivores = dinosaurs.filter(dino => dino.carnivore);
11
12// Mapping to species names
13const speciesList = dinosaurs.map(dino => dino.species);
14
15// Calculating the total weight of all dinosaurs
16const totalWeight = dinosaurs.reduce((sum, dino) => sum + dino.weight, 0);
17
18console.log("Carnivores:", carnivores.length);
19console.log("Species:", speciesList.join(", "));
20console.log("Total weight:", totalWeight, "kg");2. Objects with Methods
Arrow functions come in handy inside object methods, for example as callbacks in setInterval, because they keep the method's this (more on that a little later):
1const dinoMonitor = {
2 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
3 lastUpdated: null,
4
5 init() {
6 this.update();
7
8 // Schedule an update every 60 seconds
9 setInterval(() => {
10 this.update();
11 console.log("Monitor updated");
12 }, 60000);
13 },
14
15 update() {
16 this.lastUpdated = new Date().toISOString();
17 console.log(`Monitored dinosaurs: ${this.dinosaurs.join(", ")}`);
18 }
19};
20
21dinoMonitor.init();The Key Difference: How this Behaves
The most important difference between arrow functions and traditional functions is the way they bind the this context.
Traditional functions: dynamic this binding
In traditional functions, this is determined at the moment the function is called, and it can change:
1function DinoTracker() {
2 this.zone = "Unknown";
3
4 this.updateZone = function(newZone) {
5 this.zone = newZone;
6 console.log(`Zone: ${this.zone}`);
7 };
8
9 // Problem: 'this' in a regular function passed to setInterval does not point to DinoTracker (in the browser it is window, in Node.js a Timeout object)
10 setInterval(function() {
11 console.log(`Current zone: ${this.zone}`); // undefined
12 }, 1000);
13}
14
15const tracker = new DinoTracker();
16tracker.updateZone("Enclosure A");Arrow functions: lexical this binding
Arrow functions do not have their own this. Instead, they inherit this from the scope in which they were defined (so-called lexical binding):
1function DinoTracker() {
2 this.zone = "Unknown";
3
4 this.updateZone = function(newZone) {
5 this.zone = newZone;
6 console.log(`Zone: ${this.zone}`);
7 };
8
9 // Solution: the arrow function keeps 'this' from the surrounding scope
10 setInterval(() => {
11 console.log(`Current zone: ${this.zone}`); // It works!
12 }, 1000);
13}
14
15const tracker = new DinoTracker();
16tracker.updateZone("Enclosure A");When NOT to Use Arrow Functions
Despite their advantages, arrow functions are not suitable for every situation:
1. Object methods (when we need our own this)
1// WRONG - an arrow function has no 'this' of its own
2const dinoBad = {
3 name: "Blue",
4 introduce: () => {
5 console.log(`Hi, I'm ${this.name}`); // this does not point to the dino object, so you will not see the name Blue here
6 }
7};
8
9// CORRECT - a traditional function has its own 'this' context
10const dinoGood = {
11 name: "Blue",
12 introduce: function() {
13 console.log(`Hi, I'm ${this.name}`); // It works!
14 }
15};
16
17// CORRECT - the ES6 shorthand method syntax
18const dinoShort = {
19 name: "Blue",
20 introduce() {
21 console.log(`Hi, I'm ${this.name}`); // It works too!
22 }
23};2. Constructor functions (when we use new)
1// WRONG - arrow functions cannot be constructors
2const DinosaurArrow = (name, species) => {
3 this.name = name;
4 this.species = species;
5};
6
7// This will cause an error:
8// const rex = new DinosaurArrow("Rex", "Tyrannosaurus");
9
10// CORRECT - a traditional function as a constructor
11function Dinosaur(name, species) {
12 this.name = name;
13 this.species = species;
14}
15
16const rex = new Dinosaur("Rex", "Tyrannosaurus");
17console.log(rex); // Dinosaur { name: "Rex", species: "Tyrannosaurus" }3. When we need the arguments object
1// WRONG - arrow functions do not have their own 'arguments' object
2const logArgsArrow = () => {
3 console.log(arguments); // ReferenceError or the arguments of the enclosing function
4};
5
6// CORRECT - a traditional function has its own 'arguments' object
7function logArgsRegular() {
8 console.log(arguments); // an array-like object with the arguments
9}
10
11// Alternatively in ES6 - use the rest parameter
12const logArgsRest = (...args) => {
13 console.log(args); // an array with the arguments
14};Practical Example: A Park Monitoring System
Let's build a complete example of a Jurassic Park monitoring system, making use of arrow functions and their unique properties:
1const parkMonitoringSystem = {
2 // System state
3 status: "Offline",
4 dinosaurs: [],
5 sectors: [],
6 securityAlerts: [],
7
8 // System initialization
9 initialize() {
10 this.status = "Starting";
11
12 // Helper function that preserves the this context
13 const loadConfigs = () => {
14 // Simulating configuration loading
15 console.log("Loading system configuration...");
16
17 // Using this from the outer scope
18 setTimeout(() => {
19 this.status = "Online";
20 this.dinosaurs = [
21 { id: "TRX-01", name: "Rex", species: "Tyrannosaurus", location: "Sector A", status: "OK" },
22 { id: "VLR-01", name: "Blue", species: "Velociraptor", location: "Sector B", status: "OK" },
23 { id: "TRC-01", name: "Trixie", species: "Triceratops", location: "Sector C", status: "OK" }
24 ];
25 this.sectors = ["A", "B", "C", "D"];
26
27 console.log(`System online. Dinosaurs monitored: ${this.dinosaurs.length}`);
28 this.startMonitoring();
29 }, 2000);
30 };
31
32 loadConfigs();
33 },
34
35 // Starting the monitoring
36 startMonitoring() {
37 this.checkSecurity();
38
39 // Checking the status every 5 minutes
40 setInterval(() => this.checkStatus(), 300000);
41
42 // Collecting dinosaur data every hour
43 setInterval(() => this.collectData(), 3600000);
44
45 console.log("Monitoring started");
46 },
47
48 // Checking the status of the dinosaurs
49 checkStatus() {
50 const timestamp = new Date().toISOString();
51
52 // Using map with an arrow function
53 const statusReport = this.dinosaurs.map(dino => ({
54 id: dino.id,
55 name: dino.name,
56 status: dino.status,
57 location: dino.location,
58 timestamp
59 }));
60
61 console.log("Dinosaur status report:", statusReport);
62 return statusReport;
63 },
64
65 // Checking security
66 checkSecurity() {
67 console.log("Checking the park's security status...");
68
69 // Using filter with an arrow function
70 const compromisedSectors = this.sectors.filter(sector =>
71 Math.random() < 0.1 // 10% chance of a problem in each sector (simulation)
72 );
73
74 if (compromisedSectors.length > 0) {
75 // Using forEach with an arrow function
76 compromisedSectors.forEach(sector => {
77 const alert = {
78 id: `ALERT-${Date.now()}`,
79 sector,
80 type: "Security breach",
81 timestamp: new Date().toISOString()
82 };
83
84 this.securityAlerts.push(alert);
85 this.triggerAlert(alert);
86 });
87 } else {
88 console.log("All sectors secure");
89 }
90 },
91
92 // Triggering an alert
93 triggerAlert(alert) {
94 console.log(`ALERT! ${alert.type} in sector ${alert.sector}!`);
95
96 // Calling the appropriate procedures
97 this.notifySecurityTeam(alert);
98 },
99
100 // Notifying the security team
101 notifySecurityTeam(alert) {
102 const securityPersonnel = ["John Hammond", "Robert Muldoon", "Ray Arnold"];
103
104 // Using an arrow function to create the message
105 const createMessage = person => `Notifying ${person} about the alert in sector ${alert.sector}`;
106
107 // Sending the message to each team member
108 securityPersonnel.forEach(person => {
109 console.log(createMessage(person));
110 });
111 },
112
113 // Collecting dinosaur data
114 collectData() {
115 console.log("Collecting dinosaur data...");
116
117 // Using reduce with an arrow function
118 const speciesCount = this.dinosaurs.reduce((counts, dino) => {
119 counts[dino.species] = (counts[dino.species] || 0) + 1;
120 return counts;
121 }, {});
122
123 console.log("Number of dinosaurs by species:", speciesCount);
124
125 return {
126 timestamp: new Date().toISOString(),
127 totalCount: this.dinosaurs.length,
128 speciesDistribution: speciesCount
129 };
130 }
131};
132
133// Starting the system
134parkMonitoringSystem.initialize();Summary
Arrow functions bring several important changes to the JavaScript language:
- More concise syntax - less code to write
- Lexical
thisbinding - solves many problems with context - Implicit return - for one-line functions
This makes them especially useful in many situations, such as:
- Functions passed to array methods (
map,filter,reduce) - Callback functions in timers and event handling
- Short helper functions
At the same time, let's remember their limitations:
- No
thisof their own (which is an advantage or a drawback, depending on the situation) - No
arguments(although you can use the rest parameter) - They cannot be used as constructors
In our Jurassic Park, arrow functions help us write more concise and clearer code, which is extremely important when managing such a complex ecosystem. In the next modules we will use arrow functions in various contexts, which will let us understand their uses even better.
Code for this lesson: index.js
1// Arrow functions - Jurassic Park
2console.log("Arrow Functions Laboratory");
3console.log("Modern approach to functions in JavaScript\n");
4
5// ===========================================
6// 1. BASIC SYNTAX
7// ===========================================
8console.log("=== 1. Arrow function syntax ===");
9
10// Traditional function
11function traditionalRoar(dino) {
12 return `${dino} says: ROAAR!`;
13}
14
15// Arrow function - full syntax
16const arrowRoar = (dino) => {
17 return `${dino} says: ROAAR!`;
18};
19
20// Arrow function - shorthand (implicit return)
21const shortRoar = dino => `${dino} says: ROAAR!`;
22
23console.log(traditionalRoar("T-Rex"));
24console.log(arrowRoar("Velociraptor"));
25console.log(shortRoar("Triceratops"));
26
27// ===========================================
28// 2. DIFFERENT SYNTAX VARIANTS
29// ===========================================
30console.log("\n=== 2. Syntax variants ===");
31
32// No parameters
33const getDinosaurs = () => ["T-Rex", "Velociraptor", "Triceratops"];
34console.log("Dinosaur list:", getDinosaurs());
35
36// One parameter (parentheses optional)
37const double = x => x * 2;
38console.log("Doubling 5:", double(5));
39
40// Multiple parameters (parentheses required)
41const calculateWeight = (length, species) => length * (species === "T-Rex" ? 1000 : 500);
42console.log("T-Rex weight (12m):", calculateWeight(12, "T-Rex"), "kg");
43
44// Returning an object (requires parentheses)
45const createDino = (name, species) => ({
46 name: name,
47 species: species,
48 createdAt: new Date().toISOString()
49});
50
51console.log("\nNew dinosaur:", createDino("Blue", "Velociraptor"));
52
53// ===========================================
54// 3. COMPARISON WITH TRADITIONAL FUNCTIONS
55// ===========================================
56console.log("\n=== 3. Traditional vs Arrow Function ===");
57
58// Traditional
59function feedTraditional(dino) {
60 console.log(`Feeding ${dino} (traditional function)`);
61}
62
63// Arrow
64const feedArrow = dino => {
65 console.log(`Feeding ${dino} (arrow function)`);
66};
67
68feedTraditional("T-Rex");
69feedArrow("Velociraptor");
70
71// ===========================================
72// 4. ARROW FUNCTIONS WITH ARRAY METHODS
73// ===========================================
74console.log("\n=== 4. Arrow functions with array methods ===");
75
76const dinosaurs = [
77 { name: "Rex", species: "T-Rex", weight: 8000, carnivore: true },
78 { name: "Blue", species: "Velociraptor", weight: 80, carnivore: true },
79 { name: "Trike", species: "Triceratops", weight: 9000, carnivore: false },
80 { name: "Brachio", species: "Brachiosaurus", weight: 50000, carnivore: false }
81];
82
83// map
84const names = dinosaurs.map(dino => dino.name);
85console.log("Names:", names);
86
87// filter
88const carnivores = dinosaurs.filter(dino => dino.carnivore);
89console.log("\nCarnivores:", carnivores.map(d => d.name));
90
91// reduce
92const totalWeight = dinosaurs.reduce((sum, dino) => sum + dino.weight, 0);
93console.log("\nTotal weight:", totalWeight, "kg");
94
95// forEach
96console.log("\nList of all dinosaurs:");
97dinosaurs.forEach((dino, index) => {
98 console.log(` ${index + 1}. ${dino.name} - ${dino.species} (${dino.weight}kg)`);
99});
100
101// find
102const heaviest = dinosaurs.find(dino => dino.weight > 10000);
103console.log("\nHeaviest (>10000kg):", heaviest ? heaviest.name : "None");
104
105// some & every
106const hasHerbivores = dinosaurs.some(dino => !dino.carnivore);
107const allCarnivores = dinosaurs.every(dino => dino.carnivore);
108console.log("\nAre there herbivores?", hasHerbivores);
109console.log("Are all carnivores?", allCarnivores);
110
111// ===========================================
112// 5. METHOD CHAINING
113// ===========================================
114console.log("\n=== 5. Method chaining ===");
115
116const dangerousHeavyDinos = dinosaurs
117 .filter(dino => dino.carnivore)
118 .filter(dino => dino.weight > 100)
119 .map(dino => `${dino.name} (${dino.weight}kg)`)
120 .sort();
121
122console.log("Dangerous and heavy dinosaurs:", dangerousHeavyDinos);
123
124// ===========================================
125// 6. CALLBACK FUNCTIONS
126// ===========================================
127console.log("\n=== 6. Arrow functions as callbacks ===");
128
129function processDinosaurs(dinos, processor) {
130 return dinos.map(processor);
131}
132
133const dinoSummaries = processDinosaurs(dinosaurs, dino =>
134 `${dino.name}: ${dino.carnivore ? 'carnivore' : 'herbivore'} ${dino.weight}kg`
135);
136
137console.log("Summaries:");
138dinoSummaries.forEach(summary => console.log(` ${summary}`));
139
140// ===========================================
141// 7. ARROW FUNCTIONS IN setTimeout/setInterval
142// ===========================================
143console.log("\n=== 7. Arrow functions with timing functions ===");
144
145// setTimeout with an arrow function
146setTimeout(() => {
147 console.log("β° Feeding time! (after 100ms)");
148}, 100);
149
150// Countdown simulation
151let countdown = 3;
152const countInterval = setInterval(() => {
153 if (countdown > 0) {
154 console.log(`β³ Countdown: ${countdown}...`);
155 countdown--;
156 } else {
157 console.log("Start!");
158 clearInterval(countInterval);
159 }
160}, 50);
161
162// ===========================================
163// 8. DIFFERENCE IN 'this' BEHAVIOR
164// ===========================================
165console.log("\n=== 8. Difference in 'this' context ===");
166
167const park = {
168 name: "Jurassic Park",
169 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
170
171 // Method with traditional function
172 displayTraditional: function() {
173 console.log(`\n${this.name} - Traditional function:`);
174 // this.dinosaurs.forEach(function(dino) {
175 // // this here does NOT refer to park!
176 // console.log(` - ${dino}`);
177 // });
178
179 // Solution: arrow function in forEach
180 this.dinosaurs.forEach(dino => {
181 console.log(` - ${dino} from ${this.name}`);
182 });
183 },
184
185 // Method with arrow function
186 displayArrow: function() {
187 console.log(`\n${this.name} - Arrow function:`);
188 this.dinosaurs.forEach(dino => {
189 console.log(` - ${dino} from ${this.name}`);
190 });
191 }
192};
193
194park.displayTraditional();
195park.displayArrow();
196
197// ===========================================
198// 9. WHEN NOT TO USE ARROW FUNCTIONS
199// ===========================================
200console.log("\n=== 9. When NOT to use arrow functions ===");
201
202// DO NOT use as object methods (when you need 'this')
203const dinoObject = {
204 name: "Rex",
205
206 // BAD - arrow function as method
207 roarBad: () => {
208 // this does not point to dinoObject!
209 console.log("this.name:", typeof this.name); // undefined
210 },
211
212 // GOOD - traditional function as method
213 roarGood: function() {
214 console.log(`${this.name} says: ROAAR!`);
215 }
216};
217
218dinoObject.roarBad();
219dinoObject.roarGood();
220
221// ===========================================
222// 10. COMPLEX EXAMPLE
223// ===========================================
224console.log("\n=== 10. Park monitoring system - complex example ===");
225
226const parkSystem = {
227 name: "Isla Nublar",
228 dinosaurs: [
229 { id: 1, name: "Rexy", species: "T-Rex", sector: "A", health: 95, fed: true },
230 { id: 2, name: "Blue", species: "Velociraptor", sector: "B", health: 100, fed: false },
231 { id: 3, name: "Charlie", species: "Velociraptor", sector: "B", health: 88, fed: false },
232 { id: 4, name: "Trike", species: "Triceratops", sector: "C", health: 92, fed: true }
233 ],
234
235 getHealthReport: function() {
236 console.log(`\nHEALTH REPORT - ${this.name}`);
237 console.log("=".repeat(50));
238
239 const avgHealth = this.dinosaurs
240 .map(d => d.health)
241 .reduce((sum, h) => sum + h, 0) / this.dinosaurs.length;
242
243 console.log(`Average health: ${avgHealth.toFixed(1)}%`);
244
245 const needsAttention = this.dinosaurs.filter(d => d.health < 90);
246
247 if (needsAttention.length > 0) {
248 console.log(`\nNeed attention (${needsAttention.length}):`);
249 needsAttention.forEach(d => {
250 console.log(` - ${d.name}: ${d.health}% health`);
251 });
252 }
253
254 return { avgHealth, needsAttention: needsAttention.length };
255 },
256
257 getFeedingStatus: function() {
258 console.log(`\nFEEDING STATUS - ${this.name}`);
259 console.log("=".repeat(50));
260
261 const hungryDinos = this.dinosaurs
262 .filter(d => !d.fed)
263 .map(d => ({ name: d.name, sector: d.sector }))
264 .sort((a, b) => a.sector.localeCompare(b.sector));
265
266 if (hungryDinos.length > 0) {
267 console.log(`Hungry dinosaurs (${hungryDinos.length}):`);
268 hungryDinos.forEach(d => {
269 console.log(` Sector ${d.sector}: ${d.name}`);
270 });
271 } else {
272 console.log("All dinosaurs fed!");
273 }
274
275 return hungryDinos;
276 },
277
278 getSectorReport: function() {
279 console.log(`\nSECTOR REPORT - ${this.name}`);
280 console.log("=".repeat(50));
281
282 const sectors = this.dinosaurs.reduce((acc, dino) => {
283 if (!acc[dino.sector]) {
284 acc[dino.sector] = [];
285 }
286 acc[dino.sector].push(dino.name);
287 return acc;
288 }, {});
289
290 Object.entries(sectors)
291 .sort(([a], [b]) => a.localeCompare(b))
292 .forEach(([sector, dinos]) => {
293 console.log(` Sector ${sector}: ${dinos.join(", ")}`);
294 });
295
296 return sectors;
297 }
298};
299
300// Run all reports
301parkSystem.getHealthReport();
302parkSystem.getFeedingStatus();
303parkSystem.getSectorReport();
304
305console.log("\nCongratulations! You've mastered arrow functions!");Spotted a mistake in this lesson?
Hands-on tasks in the game
- Code editor
You have the data: const ageYears = 25, const weightKg = 8000 and const heightM = 5.6. Calculate ageDays, the age in days (a year has 365 days), and bmi, the weight divided by the square of the height. Display ageDays and bmi rounded to two decimal places (toFixed(2)) in the console.
- Horizontal ordering
Arrange the elements of a correct let variable declaration with value assignment:
- Code editor
You have the data: const fenceVoltage = 10000, const requiredVoltage = 8000, const visitorsInPark = 250 and const maxVisitors = 500. Create Boolean variables: isFenceSecure (the voltage is equal to the required one or higher), isParkFull (the number of visitors has reached the limit) and canAcceptVisitors (the fence is secure and at the same time the park is not full). Display all three in the console.