All you need to know about “Functions in JavaScript”

Functions in Javascript

Functions in JavaScript allow developers to create reusable code blocks, encapsulate functionality, and design modular applications. They are the building blocks of JavaScript, and understanding their nuances is crucial for mastering the language

JavaScript, the cornerstone of modern web applications, thrives on its powerful and flexible function capabilities. Functions in JavaScript allow developers to create reusable code blocks, encapsulate functionality, and design modular applications. Here’s a comprehensive look into the world of JavaScript functions.

Definition and Declaration

A JavaScript function is a block of reusable code designed to execute a particular task. They can be invoked (called) multiple times with different arguments to produce various results. Functions can also be assigned to variables, passed as arguments, and returned from other functions, making them first-class citizens in the language.

In JavaScript function can be written in different ways though the calling would be same for each variation. An example of traditional way of writing functions in JavaScript is as follows

function sayHello(name) {
    return "Hello, " + name + "!";
}

Here “sayHello” is the name of the function and “name” is a parameter which has been passed to the function. The function returns a string.

Calling a function

Once declared, a function does not execute on its own. It needs to be called or invoked.

sayHello("John"); // Hello, John

Function Expression

A function expression is a way to define a function as part of an expression rather than a standalone declaration. This often involves assigning a function to a variable.

A typical function expression looks like this:

const functionName = function(parameters) {
    // function body
};

The function does not have a name to the left of the parameters and the function keyword. This is why it’s sometimes called an anonymous function. However, you can also have named function expressions:

const functionName = function myFunc(parameters) {
    // function body
};

In the above example, “myFunc" is the name of the function, but it’s only available within the body of the function itself.

One important thing to note about function declaration is unlike function declarations, function expressions are not hoisted. Therefore, you cannot use the function before it’s defined. An example will keep things more clearer

hello();               // TypeError: hello is not a function
const hello= function() {
    console.log("Hello!");
};

To know more about hoisting you can check the following article and look for “Difference between var, let and constants in JavaScript”

Javascript/Typescript/Node.js Interview Questions Part -1 JavaScript questions

Arrow Function (ES6)

Arrow functions, introduced in ECMAScript 6 (ES6), provide a new, concise syntax to declare functions in JavaScript. Apart from the abbreviated syntax, arrow functions also have distinctive behavior when it comes to the handling of the this keyword. We will check in detail about arrow functions in this section

An arrow function is defined using a set of parentheses containing the arguments, followed by a fat arrow => and then the function body.

const sum = (a, b) => a + b;

If the function body contains more than one expression, you can use curly braces:

const sayHello = name => {
    const message = "Hello, " + name + "!";
    console.log(message);
};

If a function is returning the

Parentheses and Parameters

Single Parameter: Parentheses are optional.

const square = x => x * x;

No Parameter or multiple Parameter : Parentheses are required.

const greetAll = () => console.log("Hello, everyone!");
const add = (a, b) => a + b;

Implicit Return: Short and Sweet

Arrow functions offer another convenience: implicit return. If the function body consists of a single expression, you can omit the curly braces and the return keyword. The expression’s result will be automatically returned:

// Regular Function with Explicit Return
const double = function(x) {
  return x * 2;
};

// Arrow Function with Implicit Return
const doubleArrow = x => x * 2;

Arrow Function and ‘this’ keyword

In JavaScript, the this keyword inside a function refers to the object that the function is a property of or the object that the function is called on. However, the behavior of the this keyword inside an arrow function is different from regular functions. In an arrow function, the this keyword is lexically scoped, meaning it takes on the value of the this keyword in the surrounding code. The this keyword in an arrow function does not get rebound when the function is invoked, unlike regular functions. It keeps the same value as the this keyword in the surrounding code.

const obj ={
    name: "Joe",
    sayHello: ()=>{
        console.log(`Hello my name is ${this.name}`); 
    }
}

obj.sayHello(); // Output: undefined

const objarrow = {
    name:"Jane",
    sayHello(){
        console.log(`Hello my name is ${this.name}`);
    }
}

objarrow.sayHello(); // Output: Hello my name is Jane

Closures

A closure is a function bundled with references to its surrounding state, the lexical environment. This means, even after the outer function has completed its execution, the inner function still retains access to its surrounding scope. In simpler terms, closures allow JavaScript functions to have “private” variables.

Let’s see in more detail with an example

function outerFunction() {
    let count = 0;

    return function innerFunction() {
        count++;
        return count;
    }
}

const counter = outerFunction();
console.log(counter());  // Outputs: 1
console.log(counter());  // Outputs: 2

In the above code:

  1. outerFunction returns the innerFunction.
  2. innerFunction accesses the count variable, which is in the scope of outerFunction.
  3. Every time counter (an instance of innerFunction) is invoked, it has access to the count variable, even though outerFunction has finished its execution.

This ability of innerFunction to “remember” the environment in which it was created is the essence of closures.

Usage of Closure

Data encapsulation

By using closures, developers can mimic private methods, a concept from object-oriented programming. This encapsulation ensures that certain variables are not directly accessible from outside the function, preventing unintended modifications.

function bankAccount(initialBalance) {
    let balance = initialBalance;

    return {
        deposit: function(amount) {
            balance += amount;
            return balance;
        },
        withdraw: function(amount) {
            balance -= amount;
            return balance;
        }
    };
}

const account = bankAccount(100);
account.deposit(50);  // 150
account.withdraw(20); // 130

Here balance variable can’t be directly modified from outside the function hence encapsulated

Creating Factory Functions:

Closures allow developers to create factory functions—functions that return other functions with specific configurations.

function greetingFactory(greeting) {
    return function(name) {
        return `${greeting}, ${name}!`;
    }
}

const sayHello = greetingFactory('Hello');
const sayHi = greetingFactory('Hi');

sayHello('Alice');  // Outputs: "Hello, Alice!"
sayHi('Bob');       // Outputs: "Hi, Bob!"

JavaScript Functions are First Class Citizens

What is a First Class Function ?

A Programming language said to have First Class Functions if the functions in that programming language can be treated like any other variable or object. Which means that the functions can be assigned to any other variable, can be passed as a parameter to another function and can be returned from other functions

In JavaScript we can do the same with the functions so it is regarded as a “First Class Citizen” in the language

Let’s check each of the scenario in more details with examples

1. Assigned to variables: You can assign a function to a variable like any other value.

const greet = function(name) {
  console.log(`Hello, ${name}!`);
};

greet('John'); // Calls the function using the variable

2. Passed as arguments to other functions: You can pass functions as arguments to other functions.

function calculate(func, a, b) {
  return func(a, b);
}

function add(x, y) {
  return x + y;
}

function subtract(x, y) {
  return x - y;
}

console.log(calculate(add, 5, 3));      // 8
console.log(calculate(subtract, 10, 2)); // 8

3.Returned from other functions: Functions can also return other functions.

function multiplier(factor) {
  return function(x) {
    return x * factor;
  };
}

const double = multiplier(2);
console.log(double(5)); // 10

4.Stored in data structures: Functions can be stored in arrays or objects just like any other values.

const mathOperations = {
  add: function(x, y) {
    return x + y;
  },
  subtract: function(x, y) {
    return x - y;
  }
};

console.log(mathOperations.add(10, 5));      // 15
console.log(mathOperations.subtract(10, 5)); // 5

Function Currying

Function currying in JavaScript is a technique that allows you to transform a function that takes multiple arguments into a sequence of functions, each taking a single argument. JavaScript provides native support for currying through closures and the use of higher-order functions like bind or manually crafting curried functions.

In simpler words let’s say there is a function call mul(1,2,3) and after currying the function call will be like mul(1)(2)(3)

Lets look at the normal function with multiple arguments

function mul(x,y,z){
    return x*y*z;
}

console.log(x,y,

We can convert the above function to a curried function like the following

function mul(x){
  return function(y){
        return function(z){
          return x*y*z;
        }
    }
}

console.log(mul(2)(3)(4)) // Output: 24
          

Function currying empowers you to create flexible and reusable functions, making your code more modular and easier to maintain. It’s a valuable technique, especially in functional programming and when working with higher-order functions.

Higher Order Function

A Higher Order function is a function which accepts a function as an argument and/or return a function as an argument. Higher Order functions are only possible if the language itself having the concept the First Class function.

Let’s check with some examples

const arr = [1,2,3]
const double = x => x*2;
const doublearr = arr.map(double);
console.log(doublearr); // Output: [2,4,6]

In the above example map is an inbuilt JavaScript function which takes another function as an argument . So map can be considered as Higher Order function.

const  increaseby= (num)=>{
  return function(increment){
        return num+increment;
        }
    }
  
  console.log(increaseby(4)(2))// Output: 6
  console.log(increaseby(4)(3))//Output: 7

Here the increaseby function is a Higher Order function because it returns a function

Callback Function

A callback function is a function passed as an argument to another function, which is then invoked within the outer function. Callbacks are commonly used for asynchronous operations like handling AJAX requests, timers, and event handling.

Let’s check some examples to make our idea clear about callbacks

function fetchData(callback) {
  setTimeout(function () {
    const data = "Async data fetched";
    callback(data);
  }, 2000);
}

function processFetchedData(data) {
  console.log(`Processing: ${data}`);
}

fetchData(processFetchedData); // Outputs "Processing: Async data fetched" after 2 seconds

In this example, fetchData is an asynchronous function that accepts a callback processFetchedData. Once the data is fetched, it invokes the callback to process the result.

const numbers = [1, 2, 3, 4, 5];

const squaredNumbers = numbers.map(function (num) {
  return num ** 2;
});

console.log(squaredNumbers); // Outputs [1, 4, 9, 16, 25]

The map function takes a callback that is applied to each element in the numbers array, transforming them into squared numbers.

Recursive Functions

Recursive functions are functions that call themselves to solve a problem by breaking it down into smaller, similar subproblems. In JavaScript, recursion is a powerful technique often used for tasks that can be naturally divided into smaller, repetitive tasks. Here’s a note with examples of recursive functions:

function factorial(n) {
  if (n === 0 || n === 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

console.log(factorial(5)); // Outputs 120 (5! = 5 * 4 * 3 * 2 * 1)

In this example, the factorial function calculates the factorial of a number using recursion. It breaks down the problem into smaller subproblems until it reaches the base case (n equals 0 or 1), then builds the solution from there.

In conclusion, functions are the building blocks of JavaScript, and understanding their nuances is crucial for mastering the language. In this article, we’ve explored the fundamentals of functions, including their syntax, parameters, return values, and scopes. We’ve delved into advanced concepts like closures, first class functions, higher-order functions, function currying, callbacks and recursive functions etc. which empower developers to write more expressive, modular, and efficient code. Whether you’re a beginner learning the basics or an experienced developer looking to deepen your JavaScript knowledge, a solid grasp of functions is essential. Happy Coding!!

Node.js/TypeScript/MongoDB REST API Previous post Build A professional CRUD REST API with Node.js Typescript and MongoDB
Next post React Routing How To – Part 1