JavaScript / ES6+ Learning – Lesson 6 – this, call, apply, bind, Arrow Functions and Closures

This is one of the most important JavaScript lessons for us as a Ruby/Rails developer.

After this lesson, we should be able to read code like:

const user = {
name: "John",
greet() {
console.log(this.name);
}
};

and understand exactly what this means.

We’ll also understand why:

const greet = user.greet;

can behave differently from:

user.greet();

Then we’ll connect that to:

useEffect(() => {
...
});

and eventually to closures, which are fundamental to React and Node.js.


1. What is this?

The easiest starting point is:

this is a special value available inside a function.

But unlike Ruby’s self, its value is usually determined by how a regular function is called.

Consider:

const user = {
name: "John",
greet() {
console.log(this.name);
}
};
user.greet();

Output:

John

Why?

Because we called:

user.greet();

The object before the . is the receiver.

So:

user.greet()
↓
this = user

Therefore:

this.name

is effectively:

user.name

2. Think about the call site

This is the most useful mental model:

user.greet();

Look at the left side of the dot:

user . greet()
^^^^
receiver

For a normal method call, that receiver becomes this.

Example:

const person = {
name: "Alice",
sayName() {
console.log(this.name);
}
};
person.sayName();

Here:

this → person

Output:

Alice

3. But this is not permanently attached to the function

This is where JavaScript differs from the simple mental model we may have from Ruby.

Take:

const user = {
name: "John",
greet() {
console.log(this.name);
}
};

Now:

user.greet();

gives:

John

But:

const greet = user.greet;
greet();

is a different call.

We did:

const greet = user.greet;

So greet now refers to the function.

But we’re no longer calling it as:

user.greet()

We’re calling:

greet()

There is no receiver.

So the this behavior is different.


4. The three things we should distinguish

When we see a function, distinguish:

user.greet
user.greet()
const fn = user.greet;
fn();

They mean different things.

user.greet

Get the function.

user.greet()

Call the function with user as the method receiver.

fn()

Call the function independently.

This distinction is extremely important in JavaScript.


5. this in a regular function

Consider:

function showThis() {
console.log(this);
}

What this is depends on how we invoke the function.

That’s the core idea:

regular function
↓
how was it called?
↓
determines `this`

There are several invocation patterns, and we’ll learn the important ones.


6. Method call

The easiest:

const user = {
name: "John",
greet() {
console.log(this.name);
}
};
user.greet();

Here:

this = user

7. Explicitly set this

JavaScript gives us:

call()
apply()
bind()

These let us control what this refers to.

This is a very common int. topic.


8. call

Suppose:

function greet() {
console.log(this.name);
}

We have an object:

const user = {
name: "John"
};

We can explicitly say:

greet.call(user);

Now:

this = user

Therefore:

John

So:

greet.call(user);

means approximately:

Call greet now, and make this equal to user.


9. call with arguments

Suppose:

function greet(message) {
console.log(`${message}, ${this.name}`);
}

Then:

const user = {
name: "John"
};

Call:

greet.call(user, "Hello");

Output:

Hello, John

The structure is:

function.call(thisArg, arg1, arg2, ...)

For example:

greet.call(user, "Hello");

means:

this = user
message = "Hello"

10. apply

apply is similar to call.

greet.apply(user, ["Hello"]);

Output:

Hello, John

The difference is mainly how arguments are supplied.

call

greet.call(user, "Hello", "How are you?");

apply

greet.apply(user, ["Hello", "How are you?"]);

So:

call → arguments individually
apply → arguments as an array

Historically apply was particularly useful when we already had an array of arguments. With modern JavaScript, spread syntax has reduced some of those use cases.


11. bind

bind is different.

const boundGreet = greet.bind(user);

This does not immediately call greet.

Instead it creates a new function.

Think:

greet.bind(user)
↓
new function
↓
this permanently set to user

Then:

boundGreet("Hello");

Output:

Hello, John

So:

call → call now
apply → call now
bind → create another function

That’s the key distinction.


12. call vs apply vs bind

Memorize this table:

MethodExecutes immediately?Arguments
callYesIndividual
applyYesArray
bindNoReturns new function

Example:

fn.call(obj, 1, 2);
fn.apply(obj, [1, 2]);
const newFn = fn.bind(obj);
newFn(1, 2);

13. A practical example

Suppose:

const user1 = {
name: "John"
};
const user2 = {
name: "Jane"
};
function greet(message) {
console.log(`${message}, ${this.name}`);
}

Now:

greet.call(user1, "Hello");

Output:

Hello, John

And:

greet.call(user2, "Hello");

Output:

Hello, Jane

Same function.

Different this.

This demonstrates why this in regular functions is associated with the invocation.


14. Arrow functions change the rules

Now comes a critical concept.

Arrow functions do not get their own this.

They use this from the surrounding lexical scope.

Example:

const user = {
name: "John",
greet: () => {
console.log(this.name);
}
};

We might think:

user.greet()
↓
this = user

But that’s not how an arrow function works.

An arrow function does not create its own this.

It captures this from the surrounding scope.


15. Why this matters

Compare:

Regular function

const user = {
name: "John",
greet() {
console.log(this.name);
}
};
user.greet();

Here:

this = user

Arrow function

const user = {
name: "John",
greet: () => {
console.log(this.name);
}
};

Here this does not become user.

This is why object methods generally should not be written as arrow functions when they need dynamic this.


16. The classic callback problem

Now we get to a very common JavaScript issue.

Suppose:

const user = {
name: "John",
greet() {
setTimeout(function() {
console.log(this.name);
}, 1000);
}
};
user.greet();

We might expect:

John

But the inner regular function has its own this behavior.

The setTimeout callback isn’t being called as:

user.someMethod()

So it doesn’t automatically inherit the outer method’s this.


17. Arrow functions solve this nicely

Now:

const user = {
name: "John",
greet() {
setTimeout(() => {
console.log(this.name);
}, 1000);
}
};
user.greet();

Now the arrow function:

() => {
console.log(this.name);
}

doesn’t create a new this.

It captures the this from greet().

So:

user.greet()
↓
this = user
↓
arrow callback
↓
uses surrounding this
↓
this = user

Output:

John

This is one of the biggest reasons arrow functions are so useful.


18. Arrow functions and React

We’ll often see:

<button onClick={() => handleClick()}>
Click
</button>

Here the arrow function is primarily being used as a callback.

React will call the arrow function later when the event occurs.

Similarly:

useEffect(() => {
loadUsers();
}, []);

The callback passed to useEffect is an arrow function.

Again, remember:

() => {
...
}

is just a function.

React receives that function and decides when to invoke it.


19. Now: closures

This is perhaps even more important than this.

A closure happens when a function remembers variables from its surrounding lexical scope.

Consider:

function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}

Call:

const counter = createCounter();

Now:

counter(); // 1
counter(); // 2
counter(); // 3

The amazing part is:

createCounter() has already finished executing.

Yet the returned function can still access:

count

Why?

Because the function closed over the variable.


20. Visualize the closure

When we execute:

const counter = createCounter();

think:

createCounter()
|
| creates
v
count = 0
|
| returns function
v
counter ────────→ function
|
└── remembers count

Then:

counter();

the function can still access the remembered count.

After:

counter();

the value becomes:

count = 1

Next:

counter();

the function still has access to the same variable:

count = 2

21. Closure does NOT mean copying the value

This is an important detail.

Some beginners imagine:

count = 0

gets copied into the function.

That’s not the best mental model.

Instead, the function retains access to the lexical environment containing count.

Therefore:

counter();
counter();
counter();

all operate on the same captured variable.


22. Why closures are useful

Closures allow us to keep private state.

Example:

function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
},
getBalance() {
return balance;
}
};
}

Now:

const account = createBankAccount(1000);
account.deposit(500);
console.log(account.getBalance());

Result:

1500

But:

account.balance

doesn’t exist as a public property.

The state is captured by the closure.

This is conceptually similar to encapsulation.


23. Closures and callbacks

Callbacks frequently create closures.

Example:

Output:

Hello John

The returned callback remembers:

name = "John"

even after greetUser() finished.

So:

outer function
↓
creates variable
↓
creates callback
↓
callback remembers variable

That’s a closure.


24. Closures in loops

Here’s a classic int. example:

for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}

Many people expect:

0
1
2

but with var, all callbacks refer to the same function-scoped i, which has reached 3 when the callbacks run.

So we get:

3
3
3

This is a classic closure + asynchronous execution int. question.

Now change var to let:

for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}

Now the callbacks see the per-iteration i values:

0
1
2

This connects directly to our Lesson 2 discussion of let and block/iteration scoping.


25. Closure + React

Closures are everywhere in React.

For example:

function User({ user }) {
const handleClick = () => {
console.log(user.name);
};
return (
<button onClick={handleClick}>
Click
</button>
);
}

The function:

handleClick

uses:

user

from its surrounding scope.

So it forms a closure over user.

This is normal JavaScript behavior, not React-specific magic.


26. Closure + useEffect

Consider:

function UserComponent({ userId }) {
useEffect(() => {
console.log(userId);
}, [userId]);
return null;
}

The callback passed to useEffect accesses:

userId

from the surrounding function scope.

That’s a closure.

This is why closures and React hooks are closely related.

Later, when we study the dependency array deeply, this will become very important.


27. Closure + Node.js

Closures are equally useful in Node.

For example:

function createLogger(prefix) {
return message => {
console.log(`[${prefix}] ${message}`);
};
}
const errorLogger = createLogger("ERROR");
const infoLogger = createLogger("INFO");
errorLogger("Database failed");
infoLogger("Server started");

Output:

[ERROR] Database failed
[INFO] Server started

Each returned function remembers its own prefix.

So closures are not merely “React stuff.”

They’re a fundamental JavaScript feature.


28. this vs closure

These are different concepts.

this

Answers:

What object/context does this regular function call use as this?

Closure

Answers:

What variables from surrounding lexical scopes can this function still access?

For example:

const user = {
name: "John",
greet() {
const message = "Hello";
setTimeout(() => {
console.log(this.name);
console.log(message);
}, 1000);
}
};

The arrow callback has access to:

this
message

but for different reasons:

this → inherited from surrounding context
message → closure over surrounding lexical variable

This distinction is worth remembering.


29. Ruby comparison

Ruby has:

self

JavaScript has:

this

But don’t assume:

Ruby self == JavaScript this

They have important differences.

Ruby self is much more naturally tied to the current execution context/object.

JavaScript this for a regular function is heavily dependent on how the function is called.

And JavaScript arrow functions don’t create their own this.

That distinction is especially important when moving from Rails backend code to React/Node.


30. A very useful mental model

For regular functions, ask:

How was this function called?

For arrow functions, ask:

What is the surrounding `this`?

For closures, ask:

Which variables from outer scopes does this function use?

These three questions will solve a huge number of JavaScript puzzles.


🎯 Interview questions

Try these without running them.

Q1

What is this here?

const user = {
name: "John",
greet() {
console.log(this.name);
}
};
user.greet();

Q2

What’s the problem here?

const greet = user.greet;
greet();

Q3

What’s the difference between:

greet.call(user);

and:

const fn = greet.bind(user);

Q4

What is the difference between call and apply?

Q5

What is a closure?

Q6

Why does this work?

function createCounter() {
let count = 0;
return () => ++count;
}
const counter = createCounter();
counter(); // ?
counter(); // ?

Q7

Why does var produce the classic loop problem?

for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}

🧪 Live coding practice

Problem 1 – this

Predict the output:

const user = {
name: "John",
greet() {
return `Hello ${this.name}`;
}
};
console.log(user.greet());

Problem 2 – call

Write:

function introduce(role) {
console.log(`${this.name} is a ${role}`);
}

Then use call so that:

const user = {
name: "Abhilash"
};

produces:

Abhilash is a Senior Software Engineer

Problem 3 – bind

Given:

const user = {
name: "John"
};
function greet(message) {
console.log(`${message}, ${this.name}`);
}

Create:

const boundGreet = ...

so that:

boundGreet("Hello");

produces:

Hello, John

Problem 4 – Closure

Implement:

const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3

The count variable should not be directly accessible from outside.


Problem 5 – Closure with argument

Create:

const double = multiplier(2);
double(5); // 10
double(10); // 20

The function multiplier(2) should return another function.

This is an excellent exercise for understanding closures.


Problem 6 – Callback + closure

Implement:

function createGreeter(name) {
return function() {
console.log(`Hello ${name}`);
};
}

Then:

const greetJohn = createGreeter("John");
const greetJane = createGreeter("Jane");
greetJohn();
greetJane();

Expected:

Hello John
Hello Jane

Be able to explain why each returned function remembers a different name.


Final mental model

We now have four layers:

FUNCTION
↓
Function can be passed around as a value
CALLBACK
↓
A function passed to another function
THIS
↓
For regular functions, determined by how the function is called
CLOSURE
↓
A function retains access to variables from its lexical scope

And the most important distinctions:

user.greet
↓
function reference
user.greet()
↓
function call
↓
this = user
greet.call(user)
↓
call now with explicit this
greet.apply(user, args)
↓
call now with explicit this + array arguments
greet.bind(user)
↓
return a new function with bound this

For arrow functions:

arrow function
↓
doesn't create its own `this`
↓
captures surrounding `this`

For closures:

outer scope
↓
function created inside it
↓
inner function remembers outer variables

The connection to React

We can now look at:

function User({ user }) {
const handleClick = () => {
console.log(user.name);
};
return (
<button onClick={handleClick}>
Click
</button>
);
}

and identify:

{ user } → object destructuring
handleClick → function
() => → arrow function
onClick={handleClick}→ function reference
user.name → closure over user

That’s the exact JavaScript foundation we want before going further into React.

Next lesson: JavaScript asynchronous programming – synchronous execution, call stack, event loop, Web/Node APIs, callback queue, microtask queue, Promises, and why async/await works. This will be especially important for us because it connects JavaScript directly to Node.js.

Lesson 7: https://railsdrop.com/2024/12/10/javascript-es6-learning-lesson-7-asynchronous-javascript-event-loop-promises-and-async-await/

JavaScript / ES6+ Learning Lesson 3 – JavaScript Functions, Arrow Functions and Callbacks

This lesson is probably the most important JavaScript lesson before React.

You already know the basic idea of functions from Ruby. The big shift is:

In JavaScript, a function is also a value.

Once that clicks, callbacks, map, filter, React event handlers, useEffect, promises, and Node.js APIs become much easier to understand.

1. Normal function

The traditional syntax is:

function add(a, b) {
return a + b;
}

Call it:

const result = add(10, 20);
console.log(result); // 30

Execution:

add(10, 20)
↓
a = 10
b = 20
↓
a + b
↓
30

Very similar to Ruby:

def add(a, b)
a + b
end

2. A function is a value

This is the important part.

function add(a, b) {
return a + b;
}

You can do:

const operation = add;

Now:

operation(10, 20); // 30

Notice that we wrote:

const operation = add;

not:

const operation = add();

These are completely different.

add

Means:

Give me the function itself.

add()

Means:

Execute the function now and give me its return value.

Example:

function add() {
return 30;
}
const a = add;
const b = add();
console.log(a); // function
console.log(b); // 30

This distinction is extremely important for React.


3. Function expression

A function can also be assigned to a variable:

const add = function(a, b) {
return a + b;
};

Now:

add(10, 20);

This is called a function expression.

Compare:

function add(a, b) {
return a + b;
}

with:

const add = function(a, b) {
return a + b;
};

Both create functions, but they are written differently.


4. Arrow functions

Modern JavaScript heavily uses arrow functions.

const add = (a, b) => {
return a + b;
};

Shorter:

const add = (a, b) => a + b;

These are roughly equivalent for simple use cases:

function add(a, b) {
return a + b;
}
const add = (a, b) => a + b;

But later we’ll learn an important difference involving this.


5. Arrow function syntax

Let’s break down:

const add = (a, b) => a + b;
const add
   ↓
variable

(a, b)
   ↓
parameters

=>
   ↓
arrow function

a + b
   ↓
implicit return

Because there are no { }, JavaScript automatically returns the expression.

So:

const add = (a, b) => a + b;

means:

const add = (a, b) => {
return a + b;
};

6. Parentheses rules

With multiple parameters:

const add = (a, b) => a + b;

With one parameter, parentheses can be omitted:

const square = n => n * n;

Equivalent:

const square = (n) => n * n;

With no parameters:

const greet = () => "Hello";

So you’ll commonly see:

() => ...
x => ...
(x, y) => ...

7. Explicit vs implicit return

This is important.

Explicit return

const add = (a, b) => {
return a + b;
};

Implicit return

const add = (a, b) => a + b;

But this:

const add = (a, b) => {
a + b;
};

returns:

undefined

Why?

Because once you use { }, you have a function body. JavaScript requires an explicit:

return

if you want to return a value.


8. Returning an object from an arrow function

This is a common syntax trap.

You might write:

const createUser = () => {
name: "John"
};

That does not return the object you expect.

Use parentheses:

const createUser = () => ({
name: "John"
});

Now:

createUser();
// { name: "John" }

Explanation:

This is one of the most confusing parts of JavaScript arrow functions. The key is that {} has two different meanings depending on context.

1. Your first example

const createUser = () => {
name: "John"
};

You might read this as:

“Return an object containing name: "John".”

But JavaScript does not interpret it that way.

Because the arrow function has:

() => {
...
}

the {} is interpreted as a function block, not an object literal.

So JavaScript sees:

const createUser = () => {
name: "John";
};

as roughly:

const createUser = () => {
// function body
name: "John"; // labeled statement
// no return
};

Therefore:

console.log(createUser());

prints:

undefined

2. What is name: "John" then?

Inside a normal JavaScript block, this:

name: "John";

is interpreted as a label.

For example, JavaScript supports:

start:
for (let i = 0; i < 5; i++) {
console.log(i);
}

So in your code:

name: "John";

name is treated as a label, not an object property.

3. So why do parentheses fix it?

Now:

const createUser = () => ({
name: "John"
});

The important part is:

({
name: "John"
})

The parentheses tell JavaScript:

“Treat what’s inside these parentheses as an expression.”

Inside an expression, { name: "John" } is understood as an object literal.

So the arrow function becomes:

const createUser = () => ({
name: "John"
});

This is an implicit return.

It is basically equivalent to:

const createUser = () => {
return {
name: "John"
};
};

So:

console.log(createUser());

gives:

{ name: "John" }

4. Why can’t JavaScript just figure it out?

Therefore, JavaScript gives you two distinct forms.

Block body
const createUser = () => {
return {
name: "John"
};
};
Expression body
const createUser = () => ({
name: "John"
});

5. A useful way to remember it

Compare these:

() => {
name: "John";
}

versus:

() => ({
name: "John"
})

First:

=> { ... }
↑
function block

Second:

=> ( ... )
↑
expression
↓
object literal

One very common React example

This is why you often see:

const UserCard = () => (
<div>
<h1>John</h1>
</div>
);

The parentheses aren’t strictly required there, but they make it clear that JSX is the expression being implicitly returned.

And for objects:

const getUser = () => ({
name: "John",
age: 30
});

This is a very common JavaScript int. question because it tests whether you understand arrow functions, implicit returns, blocks and object literals.

You’ll see this frequently in React code.


9. Functions can receive functions

This is where callbacks begin.

Consider:

function execute(callback) {
callback();
}

We can call it:

execute(() => {
console.log("Hello");
});

Let’s slow this down.

The function:

() => {
console.log("Hello");
}

is passed as an argument to:

execute(...)

Inside execute:

function execute(callback) {
callback();
}

So:

execute(...)
|
| receives function
↓
callback
|
| callback()
↓
console.log("Hello")

That passed function is called a callback.


10. Why is it called a callback?

Because you’re essentially saying:

“Here is a function. Call it when you need to.”

Example:

function processUser(user, callback) {
console.log("Processing user:", user.name);
callback(user);
}

Usage:

processUser(
{ name: "John" },
(user) => {
console.log("Finished:", user.name);
}
);

The second argument is the callback.


11. Callbacks are NOT automatically asynchronous

This is very important.

Look at:

function execute(callback) {
console.log("A");
callback();
console.log("B");
}
execute(() => {
console.log("C");
});

Output:

A
C
B

Everything is synchronous.

A callback simply means:

a function passed to another function.

It does not mean asynchronous.


12. Asynchronous callback

Now:

setTimeout(() => {
  console.log("C");
}, 1000);

console.log("A");

Output:

A
C

after approximately one second.

Here the callback is asynchronous because setTimeout schedules it to run later.

This distinction is critical:

callback
≠
async

A callback may be:

synchronous
or
asynchronous

13. Higher-order functions

A function that:

  • accepts a function as an argument, or
  • returns a function

is commonly called a higher-order function.

Example:

function execute(callback) {
callback();
}

execute is a higher-order function because it accepts a function.

Another example:

function createGreeter() {
return function() {
console.log("Hello");
};
}

createGreeter is also a higher-order function because it returns a function.


14. This explains map

Now something you’ll see constantly in React:

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

What is actually happening?

map receives a function:

n => n * 2

Conceptually:

numbers.map(callback)
↓
function(n) {
return n * 2;
}

For every element, map calls your callback.

Conceptually:

1 → callback(1) → 2
2 → callback(2) → 4
3 → callback(3) → 6

Result:

[2, 4, 6]

15. map with objects

This is extremely important for React.

const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" }
];
const names = users.map(user => user.name);

The callback:

user => user.name

runs for every user.

Result:

["John", "Jane"]

You can think:

users
↓
map(callback)
↓
John → "John"
Jane → "Jane"
↓
["John", "Jane"]

16. filter

filter also accepts a callback.

const numbers = [1, 2, 3, 4, 5];
const result = numbers.filter(n => n > 2);

The callback returns a boolean:

1 → false
2 → false
3 → true
4 → true
5 → true

Result:

[3, 4, 5]

The pattern is:

array.filter(callback)

17. find

const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" }
];
const user = users.find(user => user.id === 2);

The callback is:

user => user.id === 2

Result:

{ id: 2, name: "Jane" }

18. Why React uses callbacks everywhere

Consider:

<button onClick={() => console.log("Clicked")}>
Click me
</button>

You’re giving React a function:

() => console.log("Clicked")

You’re essentially saying:

“When the click happens, call this function.”

That is a callback.

Another example:

users.map(user => (
<div key={user.id}>
{user.name}
</div>
))

Again:

user => (...)

is a callback.

So when you see React code filled with arrow functions, don’t think:

“React magic.”

Think:

“JavaScript is passing functions around.”

That mental model is much more useful.


19. A very important React mistake

Compare:

<button onClick={handleClick}>

and:

<button onClick={handleClick()}>

These are not the same.

First

onClick={handleClick}

means:

Give React the function. React will call it when the event occurs.

Second

onClick={handleClick()}

means:

Call the function right now and give the result to React.

This is one of the most common beginner mistakes.

Again:

function reference
handleClick
↓
"Here is the function"
function call
handleClick()
↓
"Execute it now"

20. Function parameters are just variables

Look at:

function greet(name) {
console.log(name);
}

When you call:

greet("John");

JavaScript effectively does:

name = "John"

Similarly:

function execute(callback) {
callback();
}

When:

execute(myFunction);

conceptually:

callback = myFunction

Then:

callback();

calls it.

This is the key to understanding callbacks.


21. Callback with data

Callbacks can receive values too.

function getUser(callback) {
const user = {
id: 1,
name: "John"
};
callback(user);
}

Usage:

getUser(user => {
console.log(user.name);
});

Execution:

getUser()
↓
create user
↓
callback(user)
↓
user => console.log(user.name)

Output:

John

This pattern is foundational to asynchronous JavaScript.


💎 Ruby developer comparison

You already know something conceptually similar in Ruby:

[1, 2, 3].map { |n| n * 2 }

JavaScript:

[1, 2, 3].map(n => n * 2);

Ruby:

users.map { |user| user[:name] }

JavaScript:

users.map(user => user.name);

The syntax is different, but the underlying idea is similar:

collection
↓
iterate
↓
execute supplied function for each item
↓
produce result

But don’t equate Ruby blocks and JavaScript callbacks completely. JavaScript functions are first-class values in a particularly explicit way, and the language’s treatment of closures, this, arguments and async execution differs.


Int. questions

Try these before checking the answers.

Q1

What’s the difference?

handleClick

vs

handleClick()

Q2

What does this return?

const add = (a, b) => a + b;

Q3

Why does this return undefined?

const add = (a, b) => {
a + b;
};

Q4

What is a callback?

Q5

Are callbacks always asynchronous?

Q6

Why is map considered a higher-order-function use case?

Q7

What does this produce?

const numbers = [1, 2, 3];
const result = numbers.map(n => n * 10);

Mini exercise

Predict the output:

function calculate(a, b, callback) {
const result = a + b;
callback(result);
}
calculate(10, 20, result => {
console.log(result);
});

Then trace this one:

function execute(callback) {
console.log("A");
callback();
console.log("B");
}
execute(() => {
console.log("C");
});

Expected order:

?
?
?

And finally:

const users = [
{ id: 1, name: "John", active: true },
{ id: 2, name: "Jane", active: false },
{ id: 3, name: "Mike", active: true }
];
const activeUsers = users.filter(user => user.active);
const names = activeUsers.map(user => user.name);
console.log(names);

Try to mentally execute it as:

users
  ↓
filter(callback)
  ↓
active users
  ↓
map(callback)
  ↓
names

What you should take away

At this point, you should have these mental models:

function          → a value
function()        → call the function
callback           → function passed to another function
higher-order function  → accepts/returns functions
arrow function     → concise function syntax
map/filter/find    → receive callbacks

The next lesson should build directly on this:

Lesson 4 – Arrays, map, filter, find, reduce, spread and rest, with lots of React-style examples. That lesson will make expressions like users.map(...) feel natural rather than mysterious.

Lesson 4: https://railsdrop.com/2024/12/06/javascript-es6-bootcamp-lesson-4-arrays-map-filter-find-reduce-spread-and-rest/