Functions in JavaScript

Important points to note about functions are given below
  • functions are object
  • this and arguments objects are available for functions
  • There are mainly 2 types of functions - named and anonymous
  • Function can return a value
  • Primitive types of parameters are passed to function by value
  • Reference type of Parameters are passed to function by reference
  • We can also set the default parameter to function
  • ... notation can be used to mark the variable length parameter

Function definition Examples


//standard functions
function calculate(){
console.log("calculate something")
}
calculate();

//Assigning function to variable.
let f1 = function calculate(){
    console.log("calculate something")
}
f1();

Function Parameters Examples

Below examples show how to pass the parameters to JavaScript functions. We have also demonstrated how to set default parameter and variable number of parameters.

//Passing param
function add(a,b){
    return a + b;
}
let result = add(2,3)
console.log(result) // output - 5

//Default param
function addDefault(a,b=2){
    return a + b;
}
result = addDefault(4)
console.log("Default param addition - > " + result) // output - 6

//Variable number of params
function addVariableParams(...a){
    let result = 0;
    for(let i=0;i<a.length;i++){
        result += a[i];
    }
    return result;
}
result = addVariableParams(4,5,7,5)
console.log("Variable param addition -  " + result) // output - 21


//simple procedural function
function add(a,b){
    // In function, "this" may refer to different objects based on how it is called
    // e.g. It may refer to global object, f1.call(newThis) or e.addEventListener("event", f1);
    // In the top-level code in a Node module, this is equivalent to module.exports. 
   
     console.log('this in function -> ', this);
    return a+b
}
console.log("Sum of 1,2 -> ", add(1,2)); // this will point to globalThis

let o1 = {x:10}
add.call(o1) // this will point to o1

//Arguments
function f1(a, b, c) {
    //arguments object is not an instance of Array
    console.log(arguments[0]);
    console.log(arguments[1]);  
    console.log(arguments[2]);


    //before rest syntax, we have to use below ways to convert arguments into Array
    //let a = Array.prototype.slice.call(arguments)
    //let a = [].slice.call(arguments)
    //let a = Array.from(arguments)

  }
f1(1, 2, 3);


 //variadic functions
  function f2(...theArgs) {
    //theArgs is a real array
    console.log(theArgs); 
  }
  
  console.log(f2(1, 2, 3));
  console.log(f2(1, 2, 3, "s"));

//Only the last parameter in a function definition can be a rest parameter.

    function f3(a,b,...args) {
        
    }
    

Factory functions

Factory functions are used to build and return the objects.

//factory function
function getFactoryObjects(name){
    console.log('this in factory function -> ', this);
    return {
        "name": name
    }
}
let f5 = getFactoryObjects("sagar") 
// this refers to globalThis like a normal function

Constructor Function

Constructor function are used to create objects using new operator When called with new operator, "this" refers to the new object that is created.


function User(name){
  this.name = name

  this.printName = function(){
      console.log('this in cons function and Name is -> ', this, this.name);
  }
}
let u1 = new User("sagar")
u1.printName();


// using Function constructor

const addFunction = new Function('a', 'b', 'return a + b');
console.log('Function const output ' , addFunction(2, 2));

//Function expression
let f8 = function (){
  console.log('This is a function expression');
}
f8();

Arrow functions - Lambda Functions

  • lambda functions or arrow functions - call, apply, bind does not work
  • lambda functions does not have prototype
  • "this" is lexically bound in lambda. In normal functions, this refers to object that called the function.
  • call() and apply() will not be able to provide another value for "this"
  • There is is no arguments variable inside lambda
  • Arrow function does not have its own bindings to this or super, and should not be used as methods.
  • Arrow function can not be used as constructors
  • Arrow function can not use yield, within its body


//fat arrow syntax
p = () => {console.log("fat arrow syntax")}
p()

let lambda = () => console.log('this here in lamda refers to module.exports->', this);
console.log('typeof l', typeof lambda);
lambda.call(u1); // ignores the object passed

IIFE - Immedietly invoked function expression - Anonymous functions

  • main benefit is that it does not pollute global scope. Useful to hide/protect data like private members in Java
  • used in most of old libraries like JQuery
  • If you want to expose some objects, you can use return statement from IIFE

 //anonymous functions - IIFE - call, apply bind does not work

(function x(){
    console.log("IIEF")
})();

let getDiscount = (function x(){
    let discount = 20
    return function(b){
        return b-(b*discount/100)
    }
})();
console.log(getDiscount(888))

//error discount can not be accessed
console.log(discount)

Closure

  • closure = function + lexical environment
  • benefits - memeory efficient and encapsulation
  • To add encapsulation, do not return those objects/functions from the function You should return (expose) only those things that you want to make publicly accessible
  • do not pollute global space
  • It's memory efficient because closure can access the heap that was created before. We can access old heap and reference it again and again
  • a function remembers where it was born in the special property [[Environment]]. It references the Lexical Environment from where it’s created

let f1 = function (index){

//each function call will create new array object and store on heap
let a = new Array(777).fill("dddd")
console.log("invoked")
return a[index]

}
console.log(f1(2))
//with closure, we can persist the lexical environment meaning Array is created only once.
// In subsequent calls, we can refer same array from within closure

let f1 = function (){
let a = new Array(777).fill("dddd")
console.log("invoked")
return function (index){        
        return a[index]
}

}
let x = f1();
console.log(x(2))
    

Currying


function multiply(a,b){
    return a*b;
}

let multiplybyseven = multiply.bind(this,7)
console.log(multiplybyseven(10))
//result will be 7*10 = 70 
//this is called as currying

Web development and Automation testing

solutions delivered!!