Closures

Understanding Closures

function greet(argument)
{
    return function(name){
        console.log(argument+' '+name);
    }
}
greet('Hi ')('Tony');

This will work

function gret(argument)
{
    return function(name)
    {
        console.log(argument + name);
    }
}

gret("Hello")("Abhishek")

closures are basically the functions that have access to the variables created inside the function in which they’re created. Making it easy, the function when gets created inside another parent function and returned, it still has the access to the variables created in parent function.

function magic(){
    var val=0;
    function closure(){
        if(val===0)
        console.log(true);
        else
        console.log(false);
    }
    setTimeout(function(){val=1;},5000);
    return closure;
}
function buildfunction(){
    arr=[]
    for( i=4;i<7;i++)
        {
            arr.push((function(j){return function(){console.log(j)};}(i)));
        }
    return arr;
}

a=buildfunction();

a[0]();
a[1]();
a[2]();

Output» 4 5 6

Implementing function overloading - Function Factories

function greeting(language){
    return function(firstname,lastname){
        if(language==='en')
        {
            console.log("Hello" + firstname + lastname);
        }
        else if(language === 'es')
        {
            console.log("Hola" + firstname + lastname);
        }
    }
}
var greetEnglish=greeting('en');
var greetspanish=greeting('es');
greetEnglish("Abhishek","Agarwal"); 

Closures and Callbacks

We are using closures in many areas that we were not knowing about.

function sayHiLater(){

    var greeting = "Hi"
    setTimeout(function(){
        console.log(greeting)
    },3000);
}
sayHiLater();

Here also this function inside setTimeout has the access to greeting even this function closed its execution.

Callbacks

These are the functions that you pass in another function that will get executed once another function is runned. In other words, you’ve runned a function and now you’re asking it to run another function in return.

See the following example for better intuition:

function tellMeWhenDone(callback){
    
    //some work
    //some work

    callback();
    
}

tellMeWhenDone(function(){
    console.log("Yes, I'm done!");
})

Now you are calling tellMeWhenDone and it starts working and it gets completed and then calls callback function.

Call(), Apply(), Bind()

All these functions are present as methods to all the function objects and all of them deals with ‘this’ keyword.

var name={
    firstname: "James"
    lastname: "Doe"
    var fullname=function(){
        return this.firstname + " " + this.lastname;
    }
}
function printfullname(){
    console.log(this.fullname);
}
//This would give an error because "this" in this function points to global object.
Was checking out call, apply and bind functions when I saw that "name" variable was not working properly. 

Never Use name variable in your working Always declare a variable by “var” keyword or else it will be declared in global scope.

Bind: Bind binds your function with an object so as to tell it explicitly that “this” would be pointing to this object. Example,

var myname={
firstname: 'Abhishek',
lastname: 'Agarwal',

fullname: function{
    var fname = this.firstname + ' ' + this.lastname;
    return fname;
}
}
function printname(){
    console.log(this.fullname());
}
var final=printname.bind(myname);
final();

It can be also taken for work as,

var myname={
firstname: 'Abhishek',
lastname: 'Agarwal',

fullname: function{
    var fname = this.firstname + ' ' + this.lastname;
    return fname;
}
}
var printname=function(){
    console.log(this.fullname());
}.bind(myname)
printname();

Call will take the object as a parameter and will call the function together.

var myname={
firstname: 'Abhishek',
lastname: 'Agarwal',

fullname: function{
    var fname = this.firstname + ' ' + this.lastname;
    return fname;
}
}
function printname(val1, val2){
    console.log(this.fullname());
}
printname.call(myname,val1_value,val2_value);

apply works in a similar fashion as call but it takes parameters in array.

var myname={
firstname: 'Abhishek',
lastname: 'Agarwal',

fullname: function{
    var fname = this.firstname + ' ' + this.lastname;
    return fname;
}
}
function printname(val1,val2){
    console.log(this.fullname());}

printname.apply(myname,[val1_value, val2_value])

This could also be used through function expressions.

var myname={
firstname: 'Abhishek',
lastname: 'Agarwal',

fullname: function{
    var fname = this.firstname + ' ' + this.lastname;
    return fname;
}
}
(function (val1,val2){
    console.log(this.fullname());
}).apply(myname,[val1,val2]);

When are these useful?

Function Borrowing

Suppose a function is present in an object but I want it to use it for another object then I will use this concept. Example,

var person = {
    firstname: 'John',
    lastname: 'Doe',
    getfullname=function(){
        var fullname = this.firstname + this.lastname;
        return fullname;
    }
};
var person2 = {
    firstname: 'Abhishek',
    lastname: 'Agarwal',
}

person.getfullname.bind(person2);

Function Currying

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

var multiplybytwo = multiply.bind(this,2)
multiplybytwo(4);

OUTPUT:8

This has fixed the value for a and that will be 2.

function multiply(a,b){
    return a*b;
}
var multiplybytwo=multilpy.bind(this,2,2);
multiplybytwo(5);

OUTPUT:4

function multiply(a,b){
    return a*b;
}
var multiplybytwo=multilpy.bind(this);
multiplybytwo(5,2);

OUTPUT: 10

Functional Programming

Having first class functions means that you can implement functional programming.

Normal Program:
var a = [1,2,3]
var newarr = []
for(var i=0;i<a.length;i++)
{
newarr.push(a[i]*2);
}
Functional Program:
function foreach(arr, fc)
{
    var newarr=[]
for(var i=0;i<arr.length;i++)
{
    newarr.push(fc(arr[i]));
}
return newarr;
}
a=[1,2,3]
var newarr= foreach(a,function(i){return i*2});
console.log(newarr);

Whenever you have to fix a parameter you have to use bind along with a parameter

var checkpastlimit = function(limiter,item){
    return item>limiter;
}

var arr4 = mapforeach(arr1,checkpastlimit.bind(this,1));

Challenge: Mess around with underscore library and use underscore annoted version