Why is it called “callback function“
The Crucial Difference Between func(arg)and () => func(arg)
These two patterns have fundamentally different behaviors in JavaScript/TypeScript:
1. Immediate Execution vs. Delayed Execution
// Immediately executes func with arg
func(arg); // Creates a function that WILL execute func(arg) when called later
const delayedFunc = () => func(arg);2. Call Site Behavior
// Scenario: Passing to event handler
button.addEventListener('click', func(arg));
// ❌ WRONG: func executes IMMEDIATELY during setupbutton.addEventListener('click', () => func(arg));
// ✅ CORRECT: func executes WHEN clicked3. Argument Binding
let value = 10;// Captures CURRENT value
const now = () => func(value); // Will use FUTURE value when executed
const later = func(value); value = 20;
now(); // 20 (current value when created)
later; // 10 (value at creation time)Key Differences Table
Characteristic |
|
|
|---|---|---|
Execution Timing | Immediate | Deferred until called |
Return Value | Result of | Function reference |
Use in Callbacks | ❌ Usually wrong | ✅ Correct |
Argument Binding | Static (value at call time) | Dynamic (closure captures) |
| Depends on calling context | Inherits from surrounding |
Memory Efficiency | No closure created | Creates closure |
When to Use Each
Use func(arg)When:
// 1. Immediate execution needed
const result = calculateTotal(price);// 2. Inline in expressions
if (validateInput(input)) { ... }// 3. No callback involved
logger.log("Event fired");Use () => func(arg)When:
// 1. Event handlers
button.onclick = () => submitForm(data);// 2. Asynchronous callbacks
setTimeout(() => refreshData(), 1000);// 3. Preserving arguments in loops
items.forEach(item => {button.addEventListener('click', () => selectItem(item));
});// 4. Maintaining context
class Component {constructor() {this.value = 42;// Without arrow: this would be undefinedbutton.onclick = () => this.handleClick(); }handleClick() {console.log(this.value); // 42}
}Common Pitfall & Solution
// ❌ Problem: All alerts show "3"
for (var i = 0; i < 3; i++) {setTimeout(() => alert(i), 100);
}// ✅ Solution 1: IIFE captures current i
for (var i = 0; i < 3; i++) {(function(j) {setTimeout(() => alert(j), 100);})(i);
}// ✅ Solution 2: Use let (block-scoped)
for (let i = 0; i < 3; i++) {setTimeout(() => alert(i), 100);
}Performance Considerations
() => func(arg)creates a new function each time it's calledFor hot code paths, avoid creating functions in loops:
// ❌ Creates new function on every iteration items.forEach(item => {element.addEventListener('click', () => handle(item)); });// ✅ Better: Single function reference const handler = (item) => () => handle(item); items.forEach(item => {element.addEventListener('click', handler(item)); });
Real-World Example
// Search component
class SearchBox {constructor() {this.input = document.getElementById('search');this.input.addEventListener('input', () => this.filterResults());// NOT this.input.addEventListener('input', this.filterResults());}filterResults() {console.log(`Filtering: ${this.input.value}`);}
}TypeScript Considerations
// Explicit typing for arrow functions
const handler: (event: MouseEvent) => void = () => processClick(data);// Generic function preservation
const wrapper = <T>(arg: T) => () => process(arg);Summary
func(arg) = Execute now, get result
() => func(arg) = Create a recipe to execute later
Always use arrow functions for callbacks to:
Control execution timing
Preserve arguments and context
Create closures for dynamic values
The arrow function pattern is essential for proper asynchronous and event-driven programming in JavaScript/TypeScript.
