JavaScript and TypeScript course Β· Module 2: Functions, Arrays and Objects

Arrow Functions

9 min read
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:

  1. Does not use the function keyword
  2. 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:

  1. More concise syntax - less code to write
  2. Lexical this binding - solves many problems with context
  3. 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 this of 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.

Useful articles