Functions

Functions

ยท

7 min read

Functions are a way to solve a larger problem by converting them into smaller sub-problems. It makes a problem easy to solve and understand.
It also prevents increasing code complexity.

Defining a Function

The function definition is just like a regular binding which we discussed before. It's just their value is function.

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

console.log(add(1, 2))
-> 3

A function created always starts with the keyword function. Then, in the parentheses, you can put any number of parameters, and also it can be without any parameter.

const add = function(){
     return (1+2);
}

console.log(add());
-> 3

The function should be wrapped in curly braces always. It consists of a return statement which is also called the side effect of the function.
A function always returns a single value despite the fact that it containing how many parameters. The control jumps out of the function whenever it encounters return. For the parametrized function, it's always the caller who gives the initial values to the function.

Bindings and Scopes

Every binding has a scope. The bindings that are visible before or after the function or block are called global bindings. They have scope throughout the program and you can access them anywhere.
The bindings that are defined in function parameters, inside the function or block are called local bindings. They have scope only in that function or block means you can't access them outside that.
Each function call creates a new instance and acts in its own local environment. Bindings created from the let and const keywords are local to the function.
In old JavaScript, the var keyword is used which is global to the whole program and creates conflicts in the program.
That's why, nowadays, most JavaScript developers use let and const.

let x = 10;
if(true)
{
   let y = 20;
   var z = 30;
   console.log(x+y+z);          //output: 60
}

console.log(x+z);                  //output: 40
console.log(y);                      //exception: y is not defined

Since, x is outside the if block that's why it is global to the whole snippet. But in the case of y it will be accessible within the if block only and even if we try to, it will give a "y" is not defined" error.
Since, z has the binding of var it will also be accessible throughout the program.

When we create multiple bindings with the same name. It takes the innermost value only.

const halve = function(n){
    return n/2;
}

let n = 10;
console.log(halve(50));           //output: 25
console.log(n);                         //output: 10

In the above example, when halve referring to n, it has its own value of n. So, it uses that rather than global n.

Nested Scope

Blocks and functions can contain other blocks and functions creating multiple degrees of the locality.

const add = function(a)
{
    const secondArg= function(b){
        let total = a+b;
        console.log(a+b);
    };
    secondArg(4);
};
add(2);

The code inside the secondArg can see the binding of the outer function but its local binding b cannot be visible in the outer function.
Each local scope can see all the local scopes that are inside it and also the global scope. This approach is called lexical scoping.

Functions as Values

Function value can do all the things like a normal value. You can store it in a new binding, pass it as an argument in another function. Similarly, function binding can contain another function value if not declared constant.

let writeName= function(){
     name.display("now");
}
if(!string)
{
      writeName = function();         //do nothing
}

Declaration Notation

There is another way to declare a function by putting the function keyword at the start.

function sum(x, y){                 //function declaration
     return x+y;
}

sum(2, 3);                               //function calling

This statement defines the binding sum. It's easier to write and it doesn't require ; at the end. You can call it just by putting its name followed by parentheses and the parameters if it needs any.
One more benefit of this declaration is that it doesn't have a local scope and we can call it before its definition. The scope will be transferred to where it will be called.

console.log("My name is:", name());

function name(){
     return "Nikita";
}

This will work even it is called before the declaration. This is sometimes useful because we can order the program in a way it seems meaningful.

Arrow Functions

This is the third way of writing functions. Almost everything remains the same except it doesn't use the function keyword. Instead, it uses =>(equal sign followed by greater sign).
When there is one parameter, you can omit parentheses. If there is one statement in curly braces you can omit that too.

const displayName = (name) => {
       console.log(name);
};

displayName("nikita");

The above code sample can be written like this as well ๐Ÿ‘‡

const displayName = name => console.log(name);

displayName("Nikita");

So, the basic syntax of arrow functions can be like this:

const {functionName} = (/*parameters if any*/) => {
    //actions
}

The Call Stack

The way the control flow works in functions is somewhat involved. Let's understand with the example how.

function greet(who)
{ 
      console.log('"Hello" + who);
}
greet("Harry");
console.log("Bye");

The call to greet function will make it jump to the start of the function. This function will call the console.log() function and it will do its works and return the result to the main function.
After that, we reach the end of the program. The control will go to the place from where it is called. The line after that calls the console.log function again.
Since the function has to jump to the place from where it is called it needs to remember the context. For that function maintains a stack called a call stack. It requires extra memory in the computer.
Every time function calls happen the context stored at the top of the stack and every time the function returns any value it got removes from the top.
When the stack grows too big the computer gives an error of "out of stack space".

Optional Arguments

JavaScript is broad-minded so whenever we pass extra arguments than required it just ignores them rather than giving any type of error.

function square(x){
   return x*x;
}

console.log(4, true, "hedgehog");    //16

In the above case, JS ignores the extra arguments and computes the result to 16. So, what exactly JavaScript is doing?
If you pass extra arguments it ignores the rest and takes only the required number. And if you pass fewer arguments it assigns undefined to the missing parameters.
There is both advantage and disadvantage to this functionality of JavaScript.

The advantage is that it can be called with a different number of arguments.

function minus(a, b)
{
     if(b === undefined)
     {
           return -a;
     }
     else {
           return a-b;
    }
}

console.log(minus(4));                                 //-4
console.log(minus(5, 4));                            //1

The disadvantage of this type of functionality if sometimes you passed the wrong number of arguments it will give no error and it will affect the further program.

If we write an "=" operator after a parameter, the value of that expression will replace the argument when it is missing.

function power(base, exp=2)
{
     let result = 1;
     for(let i = 0; i < exp; i=i+1)
     {
          result=result*base;
     }
     return result;
}

console.log(power(5));                         //25
console.log(power(5, 3));                    //125

Closure

It's a JavaScript feature in which the inner function has access to the outer function variable.
Closure can have access to outer function variables, local variables, and global variables.

function fun()
{
     let a=5;
     function innerfun()
     {
          return a;
     }
return innerfun();
}

var output = fun();
console.log(output());

In the above example, function fun() created a local variable a and innerfun(). The innerfun is the closure and have access to the outer function and variable a.
The outer function returns the whole body of the inner function and stored in the output. But it will not return anything until you put on braces ().

##Recursion The simplest definition of recursion is the function calling itself until the base condition hasn't arrived.

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

console.log(factorial(5));

So, the above program will call itself again and again until the value will be 0. It is important to note here that having a base condition is necessary because recursion uses extra memory as we discussed earlier.
If there will be no base condition then the function will go in an infinite calling which eventually gives the stack overflow error.

So, with the recursion, we came to an end of discussion on this topic.

Thanks for reading! ๐Ÿ˜Š

ย