JavaScript · Syntax · Intermediate
Arrow Functions & this
Master arrow function syntax and understand how `this` behaves differently in arrow functions vs regular functions — a critical distinction for classes, callbacks, and event handlers.
Quick topic start and explanations before exercises (exercises below):
Arrow functions in array methods, classes, and promises
#Arrow functions vs regular functions — reference
#Exercises:
Rewrite as an arrow function
#Rewrite the function `greet` as an arrow function assigned to a `const`. The behavior should be identical.
function greet(name) {
return "Hello, " + name + "!";
}
console.log(greet("Alice"));
Solution
const greet = name => "Hello, " + name + "!";
console.log(greet("Alice"));
Implicit return — shorten to one line
#Shorten `multiply` to a single-line arrow function using implicit return.
const multiply = (a, b) => {
return a * b;
};
console.log(multiply(3, 4));
Solution
const multiply = (a, b) => a * b;
console.log(multiply(3, 4));
this in an object method — why arrow breaks it
#The method `describe` uses an arrow function and returns the wrong result. Fix it so `this` refers to the object correctly.
const user = {
name: "Alice",
age: 30,
describe: () => {
return `${this.name} is ${this.age} years old`;
}
};
console.log(user.describe());
Solution
const user = {
name: "Alice",
age: 30,
describe() {
return `${this.name} is ${this.age} years old`;
}
};
console.log(user.describe());
Fix this in a setTimeout callback
#The `DelayedGreeter` class should print a greeting after 0ms, but `this.name` is undefined. Fix it using an arrow function.
class DelayedGreeter {
constructor(name) {
this.name = name;
}
greet() {
setTimeout(function() {
console.log("Hello, " + this.name + "!");
}, 0);
}
}
new DelayedGreeter("Alice").greet();
Solution
class DelayedGreeter {
constructor(name) {
this.name = name;
}
greet() {
setTimeout(() => {
console.log("Hello, " + this.name + "!");
}, 0);
}
}
new DelayedGreeter("Alice").greet();
Arrow functions in map and filter
#From the `products` array, filter only items with `price > 100` and then map each to just its `name`. Use arrow functions for both. Print the result.
const products = [
{ name: "Laptop", price: 999 },
{ name: "Mouse", price: 25 },
{ name: "Monitor", price: 399 },
{ name: "Keyboard", price: 75 },
];
// your code here
Solution
const products = [
{ name: "Laptop", price: 999 },
{ name: "Mouse", price: 25 },
{ name: "Monitor", price: 399 },
{ name: "Keyboard", price: 75 },
];
const result = products
.filter(p => p.price > 100)
.map(p => p.name);
console.log(result);
Return an object from an arrow function
#Write an arrow function `makeProduct` that takes `name` and `price` and returns an object `{ name, price }` using implicit return. Test it with `"Laptop"` and `999`.
// your code here
console.log(makeProduct("Laptop", 999));
Solution
const makeProduct = (name, price) => ({ name, price });
console.log(makeProduct("Laptop", 999));
Class method vs arrow property
#The class `Counter` has `increment` as a regular method and `decrement` as an arrow function property. Call both on an instance and print `count` after each. Observe which approach works correctly when destructured from the object.
class Counter {
count = 0;
increment() {
this.count++;
}
decrement = () => {
this.count--;
};
}
const c = new Counter();
c.increment();
console.log(c.count);
c.decrement();
console.log(c.count);
// Destructure and call — which one breaks?
const { increment, decrement } = c;
increment();
console.log(c.count);
decrement();
console.log(c.count);
Solution
class Counter {
count = 0;
increment() {
this.count++;
}
decrement = () => {
this.count--;
};
}
const c = new Counter();
c.increment();
console.log(c.count);
c.decrement();
console.log(c.count);
const { increment, decrement } = c;
try { increment(); } catch(e) { console.log("increment failed:", e.message); }
console.log(c.count);
decrement();
console.log(c.count);
Currying with nested arrow functions
#Write a curried `add` function using arrow functions: `add(a)(b)` should return `a + b`. Test with `add(3)(4)` and `add(10)(20)`.
// your code here console.log(add(3)(4)); console.log(add(10)(20));
Solution
const add = a => b => a + b;
console.log(add(3)(4));
console.log(add(10)(20));
Arrow functions in a promise chain
#Complete the promise chain: resolve it with the value `42`, then double it in `.then`, then print the result in the next `.then`. Use arrow functions for both `.then` callbacks.
const p = new Promise(resolve => {
resolve(42);
});
p
.then(/* double the value */)
.then(/* print the result */);
Solution
const p = new Promise(resolve => {
resolve(42);
});
p
.then(val => val * 2)
.then(val => console.log(val));
Choose the right function type
#The code below has two bugs: one method incorrectly uses an arrow function, and one callback incorrectly uses a regular function. Fix both.
class Playlist {
constructor(name) {
this.name = name;
this.songs = [];
}
// Should print "Playlist: Rock"
describe: () => {
console.log("Playlist: " + this.name);
}
// Should print each song with the playlist name
printSongs() {
this.songs.forEach(function(song) {
console.log(this.name + ": " + song);
});
}
}
const pl = new Playlist("Rock");
pl.songs = ["AC/DC", "Led Zeppelin"];
pl.describe();
pl.printSongs();
Solution
class Playlist {
constructor(name) {
this.name = name;
this.songs = [];
}
describe() {
console.log("Playlist: " + this.name);
}
printSongs() {
this.songs.forEach(song => {
console.log(this.name + ": " + song);
});
}
}
const pl = new Playlist("Rock");
pl.songs = ["AC/DC", "Led Zeppelin"];
pl.describe();
pl.printSongs();