Progress5 of 20 topics

25% complete

JavaScript Functions

Functions are one of the fundamental building blocks in JavaScript. They allow you to encapsulate code that can be reused throughout your program. Think of functions as small machines that take input, do something with it, and produce output.

Real-World Analogy

Think of a function like a coffee machine:

  • Input (parameters): Coffee beans, water, settings
  • Process: The machine grinds beans, heats water, and brews coffee
  • Output (return value): A cup of coffee
  • You can use the machine repeatedly without knowing how it works internally

Creating Functions

There are several ways to create functions in JavaScript. Let's explore them one by one.

Function Declaration

The most common way to define a function is using a function declaration.

JavaScript
1// Basic function declaration
2function greet() {
3 console.log("Hello, world!");
4}
5
6// Calling (invoking) the function
7greet(); // Outputs: Hello, world!
8
9// Function with parameters
10function greetPerson(name) {
11 console.log("Hello, " + name + "!");
12}
13
14greetPerson("Alice"); // Outputs: Hello, Alice!
15greetPerson("Bob"); // Outputs: Hello, Bob!
16
17// Function with multiple parameters
18function add(a, b) {
19 console.log(a + b);
20}
21
22add(5, 3); // Outputs: 8

Function Expression

Another way to define a function is using a function expression, where you assign a function to a variable.

JavaScript
1// Function expression
2const multiply = function(a, b) {
3 return a * b;
4};
5
6// Calling the function
7const result = multiply(4, 5);
8console.log(result); // Outputs: 20
9
10// Anonymous function expression (no name)
11const square = function(number) {
12 return number * number;
13};
14
15// Named function expression
16const factorial = function calcFactorial(n) {
17 if (n <= 1) return 1;
18 return n * calcFactorial(n - 1); // The name is only accessible inside the function
19};
20
21console.log(factorial(5)); // Outputs: 120

Declaration vs. Expression

The main difference between function declarations and expressions is hoisting. Function declarations are hoisted (moved to the top of their scope), so you can call them before they're defined in your code. Function expressions are not hoisted in the same way.

JavaScript
// This works (hoisting)
sayHello(); // Outputs: "Hello!"
function sayHello() {
console.log("Hello!");
}
// This doesn't work (not hoisted the same way)
// sayHi(); // Error: sayHi is not a function
const sayHi = function() {
console.log("Hi!");
};
// This works (after definition)
sayHi(); // Outputs: "Hi!"

Arrow Functions

Arrow functions provide a shorter syntax for writing functions. They were introduced in ES6 (2015) and are commonly used in modern JavaScript.

JavaScript
1// Arrow function with parameters
2const subtract = (a, b) => {
3 return a - b;
4};
5
6console.log(subtract(10, 5)); // Outputs: 5
7
8// Shorter syntax for single expressions (implicit return)
9const double = (number) => number * 2;
10
11console.log(double(7)); // Outputs: 14
12
13// With a single parameter, parentheses are optional
14const square = x => x * x;
15
16console.log(square(8)); // Outputs: 64
17
18// No parameters requires empty parentheses
19const sayHello = () => console.log("Hello!");
20
21sayHello(); // Outputs: Hello!
22
23// Multi-line arrow functions require curly braces and explicit return
24const calculateArea = (width, height) => {
25 const area = width * height;
26 return area;
27};
28
29console.log(calculateArea(5, 10)); // Outputs: 50

Parameters and Arguments

Parameters are variables listed in the function definition. Arguments are the values passed to the function when it's called.

JavaScript
1// Parameters vs. Arguments
2function greet(name, time) { // name and time are parameters
3 console.log(`Good ${time}, ${name}!`);
4}
5
6greet("Alice", "morning"); // "Alice" and "morning" are arguments
7
8// Default parameters (ES6)
9function greetWithDefault(name = "friend", time = "day") {
10 console.log(`Good ${time}, ${name}!`);
11}
12
13greetWithDefault(); // Outputs: Good day, friend!
14greetWithDefault("Bob"); // Outputs: Good day, Bob!
15greetWithDefault("Charlie", "evening"); // Outputs: Good evening, Charlie!
16
17// Rest parameters (ES6)
18function sum(...numbers) {
19 let total = 0;
20 for (let number of numbers) {
21 total += number;
22 }
23 return total;
24}
25
26console.log(sum(1, 2)); // Outputs: 3
27console.log(sum(1, 2, 3, 4, 5)); // Outputs: 15

The arguments Object

In traditional functions, you can access all arguments using the special arguments object.

JavaScript
function oldSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(oldSum(1, 2, 3)); // 6

Modern Approach: Rest Parameters

In modern JavaScript, it's better to use the rest parameter syntax.

JavaScript
function newSum(...numbers) {
let total = 0;
for (let number of numbers) {
total += number;
}
return total;
}
console.log(newSum(1, 2, 3)); // 6

Return Values

Functions can return values using the return statement. If no return statement is provided, the function returns undefined.

JavaScript
1// Function with return value
2function multiply(a, b) {
3 return a * b; // Returns the product of a and b
4}
5
6const result = multiply(4, 5);
7console.log(result); // Outputs: 20
8
9// Early return
10function isEven(number) {
11 if (number % 2 === 0) {
12 return true;
13 }
14 return false;
15
16 // Any code here would never run (unreachable)
17}
18
19// Multiple returns
20function getAbsoluteValue(number) {
21 if (number >= 0) {
22 return number;
23 } else {
24 return -number;
25 }
26}
27
28console.log(getAbsoluteValue(-5)); // Outputs: 5
29console.log(getAbsoluteValue(10)); // Outputs: 10
30
31// Function without a return statement
32function logMessage(message) {
33 console.log(message);
34 // No return statement, so it returns undefined
35}
36
37const returnValue = logMessage("Hello");
38console.log(returnValue); // Outputs: undefined

Function Scope and Closures

Variables defined inside a function are only accessible within that function. This is known as function scope.

JavaScript
1// Function scope
2function scopeExample() {
3 const localVar = "I'm local";
4 console.log(localVar); // Accessible
5}
6
7scopeExample();
8// console.log(localVar); // Error: localVar is not defined
9
10// Global vs. local scope
11let globalVar = "I'm global";
12
13function accessVariables() {
14 let localVar = "I'm local";
15 console.log(globalVar); // Can access global variables
16 console.log(localVar); // Can access local variables
17}
18
19accessVariables();
20console.log(globalVar); // Can access global variables
21// console.log(localVar); // Error: localVar is not defined

Closures

A closure is a function that remembers the variables from the place where it was created, even after that place's execution has finished.

JavaScript
1// Closure example
2function createGreeter(greeting) {
3 // The inner function forms a closure
4 return function(name) {
5 console.log(`${greeting}, ${name}!`);
6 };
7}
8
9const sayHello = createGreeter("Hello");
10const sayHi = createGreeter("Hi");
11
12sayHello("Alice"); // Outputs: Hello, Alice!
13sayHi("Bob"); // Outputs: Hi, Bob!
14
15// Practical closure example: counter
16function createCounter() {
17 let count = 0; // This variable is "captured" by the closure
18
19 return {
20 increment: function() {
21 count++;
22 return count;
23 },
24 decrement: function() {
25 count--;
26 return count;
27 },
28 getCount: function() {
29 return count;
30 }
31 };
32}
33
34const counter = createCounter();
35console.log(counter.increment()); // 1
36console.log(counter.increment()); // 2
37console.log(counter.decrement()); // 1
38console.log(counter.getCount()); // 1

Why Closures Matter

Closures are powerful because they let you:

  • Create private variables that can't be accessed directly from outside
  • Create function factories that generate specialized functions
  • Maintain state between function calls without using global variables
  • Implement the module pattern in JavaScript

Higher-Order Functions

Higher-order functions are functions that operate on other functions, either by taking them as arguments or by returning them.

JavaScript
1// Function as an argument
2function doOperation(x, y, operation) {
3 return operation(x, y);
4}
5
6function add(a, b) {
7 return a + b;
8}
9
10function multiply(a, b) {
11 return a * b;
12}
13
14console.log(doOperation(5, 3, add)); // Outputs: 8
15console.log(doOperation(5, 3, multiply)); // Outputs: 15
16
17// Using anonymous functions
18console.log(doOperation(5, 3, function(a, b) {
19 return a - b;
20})); // Outputs: 2
21
22// Using arrow functions
23console.log(doOperation(5, 3, (a, b) => a / b)); // Outputs: 1.6666...
24
25// Function returning a function
26function createMultiplier(factor) {
27 return function(number) {
28 return number * factor;
29 };
30}
31
32const double = createMultiplier(2);
33const triple = createMultiplier(3);
34
35console.log(double(5)); // Outputs: 10
36console.log(triple(5)); // Outputs: 15

Common Higher-Order Functions

JavaScript arrays have several built-in higher-order functions that are extremely useful.

JavaScript
1// Array.prototype.map()
2const numbers = [1, 2, 3, 4, 5];
3const doubled = numbers.map(num => num * 2);
4console.log(doubled); // Outputs: [2, 4, 6, 8, 10]
5
6// Array.prototype.filter()
7const evenNumbers = numbers.filter(num => num % 2 === 0);
8console.log(evenNumbers); // Outputs: [2, 4]
9
10// Array.prototype.reduce()
11const sum = numbers.reduce((total, num) => total + num, 0);
12console.log(sum); // Outputs: 15
13
14// Array.prototype.forEach()
15numbers.forEach(num => {
16 console.log("Number:", num);
17});
18
19// Array.prototype.find()
20const firstEven = numbers.find(num => num % 2 === 0);
21console.log(firstEven); // Outputs: 2
22
23// Array.prototype.some() and Array.prototype.every()
24const hasEven = numbers.some(num => num % 2 === 0);
25console.log(hasEven); // Outputs: true
26
27const allEven = numbers.every(num => num % 2 === 0);
28console.log(allEven); // Outputs: false

Immediately Invoked Function Expressions (IIFE)

An IIFE is a function that runs as soon as it is defined. It's a common pattern for creating private scope.

JavaScript
1// IIFE syntax
2(function() {
3 console.log("This function runs immediately!");
4})(); // The parentheses at the end invoke the function
5
6// IIFE with parameters
7(function(name) {
8 console.log("Hello, " + name + "!");
9})("Alice"); // Outputs: Hello, Alice!
10
11// IIFE with arrow function
12(() => {
13 console.log("This arrow function runs immediately!");
14})();
15
16// IIFE to create private scope
17const counter = (function() {
18 let count = 0; // Private variable
19
20 return {
21 increment: function() {
22 count++;
23 return count;
24 },
25 reset: function() {
26 count = 0;
27 return count;
28 }
29 };
30})();
31
32console.log(counter.increment()); // 1
33console.log(counter.increment()); // 2
34console.log(counter.reset()); // 0
35// console.log(count); // Error: count is not defined

Practice Exercises

Try these exercises to reinforce your understanding of JavaScript functions:

JavaScript
// 1. Create a function called 'calculateArea' that takes the radius of a circle
// as a parameter and returns the area of the circle (π * r²)
// 2. Create a function called 'reverseString' that takes a string as a parameter
// and returns the string reversed
// 3. Create a counter function using closures that has increment, decrement,
// and reset methods
// 4. Create a function that takes an array of numbers and returns a new array
// containing only the even numbers (use filter)
// 5. Create a function that takes an array of numbers and returns the sum of
// all numbers (use reduce)

Summary

In this tutorial, you've learned:

  • How to create functions using declarations, expressions, and arrow syntax
  • How to work with parameters and return values
  • How function scope and closures work
  • How to use higher-order functions
  • How to create and use immediately invoked function expressions (IIFEs)

Functions are a core concept in JavaScript that allow you to write modular, reusable code. As you continue your JavaScript journey, you'll find yourself using functions constantly to organize your code and make it more maintainable.

Modern Asynchronous Functions

Modern JavaScript has excellent support for asynchronous programming with Promises, async/await, and more. These features are unique to JavaScript and make handling asynchronous operations much cleaner.

JavaScript
1// 1. Promise-based function
2 function fetchUserData(userId) {
3 return new Promise((resolve, reject) => {
4 // Simulating API call
5 setTimeout(() => {
6 if (userId > 0) {
7 resolve({
8 id: userId,
9 name: 'User ' + userId,
10 email: `user${userId}@example.com`
11 });
12 } else {
13 reject(new Error('Invalid user ID'));
14 }
15 }, 1000);
16 });
17 }
18
19 // Using the Promise
20 fetchUserData(123)
21 .then(user => {
22 console.log('User data:', user);
23 return user.name;
24 })
25 .then(name => {
26 console.log('User name:', name);
27 })
28 .catch(error => {
29 console.error('Error:', error.message);
30 });
31
32 // 2. Async/await (ES2017)
33 async function getUserDetails(userId) {
34 try {
35 const user = await fetchUserData(userId);
36 console.log('User details:', user);
37
38 // We can use await multiple times, making async code look synchronous
39 const permissions = await fetchUserPermissions(user.id);
40 return { user, permissions };
41 } catch (error) {
42 console.error('Failed to get user details:', error.message);
43 return null;
44 }
45 }
46
47 // Function to fetch permissions (for the example)
48 function fetchUserPermissions(userId) {
49 return new Promise(resolve => {
50 setTimeout(() => {
51 resolve(['read', 'write', 'delete']);
52 }, 500);
53 });
54 }
55
56 // Using an async function
57 getUserDetails(456).then(result => {
58 if (result) {
59 console.log('Complete user profile:', result);
60 }
61 });
62
63 // 3. Async arrow functions
64 const getDataQuickly = async (id) => {
65 const result = await fetchUserData(id);
66 return result;
67 };
68
69 // 4. Parallel async operations with Promise.all
70 async function fetchMultipleUsers(userIds) {
71 try {
72 const userPromises = userIds.map(id => fetchUserData(id));
73 const users = await Promise.all(userPromises);
74 console.log('All users:', users);
75 return users;
76 } catch (error) {
77 console.error('One or more requests failed:', error);
78 return [];
79 }
80 }

Why Async/Await Is Special

The async/await syntax is one of JavaScript's most powerful features, allowing you to write asynchronous code that looks and behaves like synchronous code. This makes complex operations like API calls, file operations, and timers much easier to work with and understand.

Related Tutorials

Learn about conditional statements and loops in JavaScript.

Learn more

Learn about arrays and how to work with collections of data.

Learn more

Learn about objects and how to work with key-value pairs.

Learn more