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 = 10b = 20 ↓a + b ↓30
Very similar to Ruby:
def add(a, b) a + bend
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); // functionconsole.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 functiona + 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" }
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:
ACB
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:
AC
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:
synchronousorasynchronous
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) → 22 → callback(2) → 43 → 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 → false2 → false3 → true4 → true5 → 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 valuefunction() → call the functioncallback → function passed to another functionhigher-order func → accepts/returns functionsarrow function → concise function syntaxmap/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.