Docs
README
5.2 Function Expressions
Overview
A function expression is an alternative way to define functions in JavaScript. Unlike function declarations, function expressions are not hoisted, and the function is created when the execution reaches that line. Function expressions can be anonymous or named.
Table of Contents
- ā¢Function Expression Syntax
- ā¢Anonymous vs Named Functions
- ā¢Hoisting Differences
- ā¢When to Use Function Expressions
- ā¢IIFE - Immediately Invoked Function Expressions
- ā¢Callback Functions
- ā¢Higher-Order Functions
- ā¢Function Expression Patterns
- ā¢Best Practices
Function Expression Syntax
Basic Syntax
const functionName = function (parameters) {
// Function body
return value;
};
Comparison with Declaration
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Function Declaration vs Expression ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Declaration: Expression: ā
ā āāāāāāāāāāā āāāāāāāāāāā ā
ā ā
ā function greet(name) { const greet = function(name) {
ā return "Hi " + name; return "Hi " + name;
ā } }; ā
ā ā
ā ⢠Hoisted ⢠NOT hoisted ā
ā ⢠Has own name ⢠Anonymous or named ā
ā ⢠Statement ⢠Expression (value) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Examples
// Anonymous function expression
const add = function (a, b) {
return a + b;
};
// Named function expression
const subtract = function sub(a, b) {
return a - b;
};
// Using the functions
console.log(add(5, 3)); // 8
console.log(subtract(5, 3)); // 2
Anonymous vs Named Functions
Anonymous Function Expression
const greet = function (name) {
return 'Hello, ' + name;
};
// The function has no intrinsic name
console.log(greet.name); // "greet" (inferred from variable)
Named Function Expression
const greet = function sayHello(name) {
return 'Hello, ' + name;
};
console.log(greet.name); // "sayHello"
// sayHello("World"); // Error: sayHello is not defined
greet('World'); // "Hello, World"
Benefits of Named Function Expressions
| Benefit | Description |
|---|---|
| Stack traces | Error messages show the function name |
| Recursion | Function can call itself by name |
| Debugging | Easier to identify in dev tools |
| Self-documentation | Code is more readable |
Recursion with Named Expressions
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // Use internal name
};
console.log(factorial(5)); // 120
// console.log(fact(5)); // Error: fact is not defined
Hoisting Differences
Function Declaration - Hoisted
// Works - declaration is hoisted
sayHello();
function sayHello() {
console.log('Hello!');
}
Function Expression - NOT Hoisted
// Error: Cannot access 'sayHi' before initialization
// sayHi();
const sayHi = function () {
console.log('Hi!');
};
sayHi(); // Works after definition
Variable Hoisting with var
console.log(greet); // undefined (var is hoisted but not initialized)
// greet(); // TypeError: greet is not a function
var greet = function () {
return 'Hello';
};
greet(); // Works
Temporal Dead Zone with let/const
// console.log(greet); // ReferenceError: Cannot access before initialization
const greet = function () {
return 'Hello';
};
When to Use Function Expressions
1. Callbacks
setTimeout(function () {
console.log('Executed after 1 second');
}, 1000);
[1, 2, 3].map(function (num) {
return num * 2;
});
2. Conditional Function Creation
let operation;
if (mode === 'add') {
operation = function (a, b) {
return a + b;
};
} else {
operation = function (a, b) {
return a - b;
};
}
3. Passing Functions as Values
const handlers = {
success: function (data) {
console.log('Success:', data);
},
error: function (err) {
console.log('Error:', err);
},
};
4. Immediately Invoked (IIFE)
const result = (function (x) {
return x * x;
})(5);
5. Closures
const counter = (function () {
let count = 0;
return function () {
return ++count;
};
})();
IIFE - Immediately Invoked Function Expressions
An IIFE is a function expression that runs immediately after being defined.
Syntax
(function () {
// Code executed immediately
})();
// Alternative syntax
(function () {
// Code executed immediately
})();
With Parameters
(function (name) {
console.log('Hello, ' + name);
})('World');
Named IIFE
(function init() {
console.log('Initializing...');
// Initialization code
})();
Use Cases
// 1. Create private scope
(function () {
const privateVar = 'secret';
// privateVar is not accessible outside
})();
// 2. Avoid polluting global scope
(function (global) {
global.myApp = {
version: '1.0.0',
};
})(window);
// 3. Module pattern
const module = (function () {
const private = 'hidden';
return {
getPrivate: function () {
return private;
},
};
})();
console.log(module.getPrivate()); // "hidden"
// console.log(module.private); // undefined
Callback Functions
A callback is a function passed as an argument to another function.
Basic Callback
function processData(data, callback) {
const result = data.toUpperCase();
callback(result);
}
const logResult = function (result) {
console.log('Result:', result);
};
processData('hello', logResult);
// "Result: HELLO"
Inline Callbacks
processData('hello', function (result) {
console.log('Inline result:', result);
});
Callback with Multiple Functions
function fetchData(onSuccess, onError) {
const success = Math.random() > 0.5;
if (success) {
onSuccess({ data: [1, 2, 3] });
} else {
onError(new Error('Failed to fetch'));
}
}
fetchData(
function (data) {
console.log('Success:', data);
},
function (error) {
console.log('Error:', error.message);
}
);
Higher-Order Functions
Functions that take functions as arguments or return functions.
Taking Functions as Arguments
function repeat(n, action) {
for (let i = 0; i < n; i++) {
action(i);
}
}
repeat(3, function (i) {
console.log('Iteration:', i);
});
Returning Functions
function createGreeter(greeting) {
return function (name) {
return greeting + ', ' + name + '!';
};
}
const sayHello = createGreeter('Hello');
const sayHi = createGreeter('Hi');
console.log(sayHello('Alice')); // "Hello, Alice!"
console.log(sayHi('Bob')); // "Hi, Bob!"
Function Composition
function compose(f, g) {
return function (x) {
return f(g(x));
};
}
const addOne = function (x) {
return x + 1;
};
const double = function (x) {
return x * 2;
};
const addOneThenDouble = compose(double, addOne);
console.log(addOneThenDouble(5)); // 12 (5+1=6, 6*2=12)
Function Expression Patterns
1. Module Pattern
const calculator = (function () {
// Private
let result = 0;
// Public interface
return {
add: function (n) {
result += n;
return this;
},
subtract: function (n) {
result -= n;
return this;
},
multiply: function (n) {
result *= n;
return this;
},
getResult: function () {
return result;
},
reset: function () {
result = 0;
return this;
},
};
})();
calculator.add(10).multiply(2).subtract(5);
console.log(calculator.getResult()); // 15
2. Factory Pattern
const createPerson = function (name, age) {
return {
name: name,
age: age,
greet: function () {
return "Hi, I'm " + this.name;
},
};
};
const person = createPerson('Alice', 30);
console.log(person.greet()); // "Hi, I'm Alice"
3. Revealing Module Pattern
const userModule = (function () {
let users = [];
function add(user) {
users.push(user);
}
function getAll() {
return [...users];
}
function findById(id) {
return users.find(function (u) {
return u.id === id;
});
}
return {
add: add,
getAll: getAll,
findById: findById,
};
})();
4. Singleton Pattern
const Database = (function () {
let instance;
function createInstance() {
return {
connection: 'Connected',
query: function (sql) {
console.log('Executing:', sql);
},
};
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
},
};
})();
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true (same instance)
Best Practices
1. Use const for Function Expressions
// ā
Prevents reassignment
const greet = function (name) {
return 'Hello, ' + name;
};
// ā Can be reassigned accidentally
let greet = function (name) {
return 'Hello, ' + name;
};
2. Name Complex Functions
// ā Hard to debug
element.addEventListener('click', function (e) {
// Complex logic
});
// ā
Easier to debug
element.addEventListener('click', function handleClick(e) {
// Complex logic
});
3. Extract Repeated Callbacks
// ā Repeated anonymous functions
[1, 2, 3].map(function (x) {
return x * 2;
});
[4, 5, 6].map(function (x) {
return x * 2;
});
// ā
Reusable named function
const double = function (x) {
return x * 2;
};
[1, 2, 3].map(double);
[4, 5, 6].map(double);
4. Use Declarations for Top-Level Functions
// ā
Use declaration for main functions
function processOrder(order) {
// Main business logic
}
// ā
Use expression for callbacks/helpers
const formatPrice = function (price) {
return '$' + price.toFixed(2);
};
5. Prefer Arrow Functions for Simple Callbacks
// ā Verbose for simple operations
[1, 2, 3].map(function (x) {
return x * 2;
});
// ā
Concise arrow function
[1, 2, 3].map((x) => x * 2);
Summary
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Function Expressions Quick Reference ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā // Anonymous expression ā
ā const add = function(a, b) { return a + b; }; ā
ā ā
ā // Named expression ā
ā const add = function sum(a, b) { return a + b; }; ā
ā ā
ā // IIFE ā
ā (function() { /* runs immediately */ })(); ā
ā ā
ā Key Differences from Declarations: ā
ā ⢠NOT hoisted ā
ā ⢠Can be anonymous ā
ā ⢠Are expressions (values) ā
ā ⢠Better for callbacks ā
ā ā
ā Common Uses: ā
ā ⢠Callbacks ā
ā ⢠IIFE for private scope ā
ā ⢠Higher-order functions ā
ā ⢠Module pattern ā
ā ⢠Conditional function creation ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Next Steps
- ā¢Practice with the examples in
examples.js - ā¢Complete the exercises in
exercises.js - ā¢Learn about arrow functions in the next topic
- ā¢Explore closures and scope in depth