JavaScript functions can be categorized into 6 major families based on their declaration, usage, and behavior.
Understanding these different families of functions is crucial for effective JavaScript programming, as each type has its own use cases and can be leveraged to achieve different functionality in applications.
Function Declarations: These are the traditional functions declared using the function keyword. They are hoisted, meaning they can be called before they are defined in the code.
function greet() { console.log("Hello there!"); }
Function Expressions: (Named Function) These functions are created and assigned to variables. They can be named or anonymous and are not hoisted like function declarations.
const greet = function() { console.log("Hello there!"); }; Arrow Functions
Introduced in ES6 (ECMAScript 2015), arrow functions provide a concise syntax and do not have their own this, arguments, super, or new.target.
They are often used in scenarios where the behavior of this needs to be lexical, such as in callbacks and array methods. const greet = () => console.log("Hello there!"); Arrow Functions with Arrays
In this example, we'll create an array of fruits and use arrow functions to iterate over the array, generating and logging different kinds of pastries that can be made from each fruit.
This example demonstrates how arrow functions can make array manipulations and iterations more concise and readable.
// Array of fruits
const fruits = ['apple', 'banana', 'cherry', 'date'];
// Array of pastry types
const pastryTypes = ['pie', 'tart', 'muffin', 'cake'];
// Function to generate a list of pastries for a given fruit
const generatePastries = (fruit) => pastryTypes.map(pastry => `${fruit} ${pastry}`);
// Iterate over the array of fruits and log the pastries that can be made
fruits.forEach(fruit => {
const pastries = generatePastries(fruit);
pastries.forEach(pastry => console.log(`You can make a ${pastry}.`));
});
Explanation
Array of Fruits and Pastries: We start with an array of fruits and an array of pastry types.
const fruits = ['apple', 'banana', 'cherry', 'date'];
const pastryTypes = ['pie', 'tart', 'muffin', 'cake'];
Arrow Function to Generate Pastries: The generatePastries function uses an arrow function to create an array of strings, each representing a type of pastry made from the given fruit.
const generatePastries = (fruit) => pastryTypes.map(pastry => `${fruit} ${pastry}`);
pastryTypes.map(pastry => ...) iterates over each pastry type, generating a string like "apple pie", "apple tart", etc.
Iterate Over Fruits: We use the forEach method with an arrow function to iterate over the fruits array.
fruits.forEach(fruit => {
const pastries = generatePastries(fruit);
pastries.forEach(pastry => console.log(`You can make a ${pastry}.`));
});
For each fruit, we call generatePastries(fruit) to get the list of possible pastries.
We then use another forEach to iterate over the generated pastries array, logging each pastry to the console.
Output
The output of this code will be:
css
Copy code
You can make an apple pie.
You can make an apple tart.
You can make an apple muffin.
You can make an apple cake.
You can make a banana pie.
You can make a banana tart.
You can make a banana muffin.
You can make a banana cake.
You can make a cherry pie.
You can make a cherry tart.
You can make a cherry muffin.
You can make a cherry cake.
You can make a date pie.
You can make a date tart.
You can make a date muffin.
You can make a date cake.
This example shows how arrow functions can simplify array operations and make the code more readable and concise. By using arrow functions with map and forEach, we avoid the verbosity of traditional function expressions and make our intent clearer.
Generator Functions
Generators are a special class of functions that can be exited and later re-entered, preserving the context of their variables across re-entries. They are marked by the function* syntax and use the yield keyword.
function* numberGen() { yield 1; yield 2; return 3; }
Example of Generator Functions in a Dating App
Let's create an interesting example using generator functions to simulate a dating app that matches people based on their interests.
The generator function will yield potential matches one by one based on common interests until it finds a perfect match or runs out of candidates.
Code Example
// Define profiles with their interests
const profiles = [
{ name: 'Alice', interests: ['hiking', 'music', 'movies'] },
{ name: 'Bob', interests: ['sports', 'music', 'coding'] },
{ name: 'Charlie', interests: ['traveling', 'photography', 'movies'] },
{ name: 'Dana', interests: ['hiking', 'cooking', 'movies'] },
];
// Define the interests of the user looking for a match
const userProfile = { name: 'Eve', interests: ['hiking', 'movies', 'coding'] };
// Generator function to find matches
function* findMatches(user, profiles) {
for (const profile of profiles) {
const commonInterests = profile.interests.filter(interest => user.interests.includes(interest));
if (commonInterests.length > 0) {
yield { match: profile.name, commonInterests };
}
}
return 'No more matches available';
}
// Create an instance of the generator
const matchGenerator = findMatches(userProfile, profiles);
// Function to get matches and display them
function getMatches(generator) {
let result = generator.next();
while (!result.done) {
console.log(`Match found: ${result.value.match}`);
console.log(`Common interests: ${result.value.commonInterests.join(', ')}`);
result = generator.next();
}
console.log(result.value); // 'No more matches available'
}
// Run the function to get and display matches
getMatches(matchGenerator);
Explanation
Profiles and Interests: We start with an array of profiles, each with a name and interests array. We also define the interests of the user who is looking for a match.
const profiles = [
{ name: 'Alice', interests: ['hiking', 'music', 'movies'] },
{ name: 'Bob', interests: ['sports', 'music', 'coding'] },
{ name: 'Charlie', interests: ['traveling', 'photography', 'movies'] },
{ name: 'Dana', interests: ['hiking', 'cooking', 'movies'] }, ];
const userProfile = { name: 'Eve', interests: ['hiking', 'movies', 'coding'] };
Generator Function: The generator function findMatches iterates over the profiles array, comparing interests with the user profile. It yields potential matches one by one based on common interests. function* findMatches(user, profiles) { for (const profile of profiles) { const commonInterests = profile.interests.filter(interest => user.interests.includes(interest)); if (commonInterests.length > 0) { yield { match: profile.name, commonInterests }; } } return 'No more matches available'; } Instance of the Generator: We create an instance of the generator function. const matchGenerator = findMatches(userProfile, profiles); Function to Get Matches: The getMatches function consumes the generator, logging each match and the common interests until there are no more matches. function getMatches(generator) { let result = generator.next(); while (!result.done) { console.log(`Match found: ${result.value.match}`); console.log(`Common interests: ${result.value.commonInterests.join(', ')}`); result = generator.next(); } console.log(result.value); // 'No more matches available' } Run the Function: Finally, we run the getMatches function to display the matches. getMatches(matchGenerator); Output
The output will be:
yaml
Copy code
Match found: Alice
Common interests: hiking, movies
Match found: Dana
Common interests: hiking, movies
No more matches available
Explanation
Generator Function: The generator function findMatches goes through each profile, checks for common interests, and yields the match if there are any common interests. Using the Generator: The getMatches function consumes the generator, displaying each match and its common interests until no more matches are available. This example demonstrates how generator functions can be used to create a controlled flow of operations, yielding results incrementally and handling complex iteration patterns in a clean and maintainable way.
This is especially useful in applications like a dating app where potential matches need to be evaluated one by one based on certain criteria.
Consider a linear, synchronous [Not asych] program flow:
function a() {
b();
return "I am function a"}
function b() {
c()
return "I am function a"}
function c() {return "I am function a"}
console.log(a());
Async Functions
These functions are an extension of generators, designed to work with promises and simplify asynchronous code. Declared using async function, they implicitly return a promise and allow the use of await to pause the function execution until the promise settles.
async function fetchData() {
const data = await fetch('url');
const json = await data.json();
return json; }
Async Code Lab:
Async In JavaScript: The wedding planner
IIFE (Immediately Invoked Function Expressions)
These are function expressions that are executed immediately after they are defined.
They are commonly used to create private scopes.
(function() { console.log("Hello there!"); })();
Example of IIFE in Guest Entertainment Services at Cancun Resort
Constructor Functions
Before the introduction of ES6 classes, constructor functions were used to create objects and implement inheritance. They are still used and are particularly important for understanding prototype-based inheritance. function Person(name) { this.name = name; } const person1 = new Person("Alice"); Introduction to Constructor Functions in JavaScript:
Higher-Order Functions
These are functions that take other functions as arguments or return them. Higher-order functions are a key aspect of functional programming in JavaScript. function map(arr, fn) { const result = []; for (let i = 0; i < arr.length; i++) { result.push(fn(arr[i])); } return result; }