IntroductionDeclarations vs ExpressionsFunctions Calling Other FunctionsArrow FunctionsParametersArgumentsRecursionClosureFunction Constructors & Prototypal InheritanceCall, Apply, BindPollyfillingFunction CurryingIIFEMemoizationFunction Factories
Introduction
A function is a code block that can be run multiple times, emphasizing DRY Don’t Repeat Yourself principle
A function is kind of a machine that takes in some input, does some processing and produces an output 🥔
Input at function declaration =
parametersInput at function invocation =
argumentThe parameter is like a local variable only accessible inside the function body
// declaring a function function logger(name){ // name is parameter return `Hey ${name}`; } /// invoking, calling, running logger('Arun'); // argument
Declarations vs Expressions
There are different ways of writing functions
- Function Declaration: Using the
functionkeyword
- Function Expression: Storing in a variable (anonymous function)
In JS functions are just values that’s why they can be stored as expression
Key Difference: Function declarations are hoisted (can be called before they’re defined).
// function declaration function getAge(birthYear, currYear){ return currYear - birthYear; } // function expression const getAge2 = function (birthYear, currYear){ return currYear - birthYear; }
Functions Calling Other Functions
Functions can call other functions to break down complex logic into smaller, reusable pieces.
Arrow Functions
Introduced in ES6 and is a function expression
Arrow functions don’t get their own
this keyword (they inherit it from the surrounding scope).const calcAge3 = birthYear => 2030 - birthYear; // birthYear argument, implicit return // for a code block const yearsUntilRetirement = (birthYear, currYear) => { // multiple arguments need () const age = currentYear - birthYear; const retirement = 65 - age; return retirement; // explicit return }
Parameters
The
arguments object allows access to all arguments passed to a function.Default Parameters: Set default values for parameters.
Rest Parameters: Use
... to collect all remaining arguments into an array.function sum(...nums) { return nums.reduce((acc, curr) => acc + curr, 0); } console.log(sum(1, 2, 3, 4)); // 10
Arguments
Earlier we only had an arguments variable inside a function
This is an array like structure but not exactly an array and lacks the built-in callback methods
The problems with this old approach is: we don’t know how many arguments to except, isn’t very readable and lacks array built-in methods
function sum(){ console.log(arguments); // 3, 4, 5 } num(3, 4, 5);
Recursion
A recursive function is a function that calls itself until it reaches a base condition.
Often used for problems like factorials, Fibonacci numbers, and tree traversal.
function factorial(n) { if (n === 1) return 1; // Base condition return n * factorial(n - 1); // Recursive call } console.log(factorial(5)); // 120
Base Condition: Ensures the recursion stops; otherwise, it leads to a stack overflow.
Closure
When an internal function retains access to variables in its outer function, even after the outer function has finished executing, it’s called a closure.
// Function to get a random color without repeating until all colors are used function generateColor() { const colors = 'orange pink purple black blue violet gray'.split(' '); const generated = []; return function () { if (generated.length === colors.length) { return "All colors covered"; } let color = colors[Math.floor(Math.random() * colors.length)]; while (generated.includes(color)) { color = colors[Math.floor(Math.random() * colors.length)]; } generated.push(color); return color; }; } const randomColor = generateColor(); console.log(randomColor()); console.log(randomColor()); // Outputs random colors until "All colors covered"
Function Constructors & Prototypal Inheritance
Function constructors are used to create objects and allow for prototypal inheritance.
jsx Copy code function Person(name, birthYear) { this.name = name; this.birthYear = birthYear; } Person.prototype.calcAge = function () { return new Date().getFullYear() - this.birthYear; }; const arun = new Person('Arun', 1995); console.log(arun.calcAge());
Call, Apply, Bind
Call: Allows calling a function with a specific
this value and arguments passed individually.Apply: Similar to
call, but arguments are passed as an array.Bind: Returns a new function with
this bound to a specific object.const obj1 = { name: "Alice" }; const obj2 = { name: "Bob" }; function greet(greeting) { return `${greeting}, ${this.name}!`; } // Call console.log(greet.call(obj1, "Hello")); // "Hello, Alice!" // Apply console.log(greet.apply(obj2, ["Hi"])); // "Hi, Bob!" // Bind const boundGreet = greet.bind(obj1); console.log(boundGreet("Hey")); // "Hey, Alice!"
Pollyfilling
Creating a fallback for functionality not supported by some browsers.
if (!Function.prototype.bind) { Function.prototype.bind = function (obj) { const fn = this; return function (...args) { return fn.apply(obj, args); }; };
Function Currying
Currying is breaking down a function into multiple functions that take one parameter at a time.
function add(a) { return function (b) { return function (c) { return a + b + c; }; }; } console.log(add(2)(3)(5)); // 10
IIFE
Functions that are executed immediately after their definition.
(function () { console.log("I am executed immediately!"); })();
Memoization
Memoization is caching the result of expensive function calls to reuse them.
function memoizedFactorial() { const cache = {}; return function factorial(n) { if (n in cache) return cache[n]; if (n === 1) return 1; cache[n] = n * factorial(n - 1); return cache[n]; }; } const factorial = memoizedFactorial(); console.log(factorial(5)); // 120
Function Factories
Return a new function based on input arguments.
function greetFactory(greeting) { return function (name) { return `${greeting}, ${name}!`; }; } const sayHello = greetFactory("Hello"); console.log(sayHello("Arun")); // "Hello, Arun!"
🤷🏻