-1

closure is hurting me in my every interviews. finally I learned something about closure. But my question is what is the difference between these below snippets? Which scenarios should we use closures?

Closure:

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

console.log(add(1)(2)(3))

Instead of closure, Simply we can pass three parameter into a single method.

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

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

So why I need to use closure?

James Z
  • 12,209
  • 10
  • 24
  • 44
Ramesh Rajendran
  • 37,412
  • 45
  • 153
  • 234
  • 1
    That's very little to do with *closures*. They are used, yes, but a closure doesn't necessarily mean to structure your code like the first example. The topic you are after is "currying" and/or "partial application of functions". – VLAZ Jan 30 '19 at 12:23

4 Answers4

2

In this example, there is no real difference in the output. However, let's make it even simpler:

function add(a) {
  return function(b) {
    return a+b;
  }
}


console.log(add(1)(2));
function add(a, b) {
  return a+b;
}

console.log(add(1, 2));

For simplicity, the function takes two parameters. One time you have to pass them both when invoking, the other you have to pass them one by one. Let's look at how that could be useful:

function add(a) {
  return function(b) {
    return a+b;
  }
}

let numbers = [1, 2, 3, 4, 5, 6];

//lets add the same number to all members of the array:
const numberToAdd = 5;

//traditionally
const newArray = []; //make a new array

for (let num of numbers) { //define a loop
  newArray.push(num + numberToAdd); //populate the array
}

console.log(newArray);

//functional
const mappedArray = numbers.map(num => num + numberToAdd); //using .map to handle everything
console.log(mappedArray);

//using the function we have
const betterMappedArray = numbers.map(add(numberToAdd));
console.log(betterMappedArray);

So, the functional approach via .map is shorter and easier but can be even further improved by passing a function. Without the add function you still pass a function, you define a new addition function every time you want to just add something to an array. If you want to add variable amounts, then without add, you have to create two new functions that fundamentally all do the same thing

function add(a) {
  return function(b) {
    return a+b;
  }
}

let numbers = [1, 2, 3, 4, 5, 6];


console.log("using .map() with new functions");
console.log(
  numbers
    .map(num => num + 5)
    .map(num => num + 10)
);

console.log("using .map() with add()");
  
console.log(
  numbers
    .map(add(5))
    .map(add(10))
);

As you can see, put side-by-side it seems quite wasteful to make new functions every time. Even having the signature as add(a, b) so it takes two arguments means you would have to call numbers.map(num => add(num, 5)) which does not improve anything.

Bear in mind that add is really simple - you might have a function that is more complex. Still, going with the simple example, let's re-write the example to show how it could be useful:

function add(a) {
  return function(b) {
    return a+b;
  }
}

const shoppingCart = [
  {name: "item1", price: 1},
  {name: "item2", price: 2},
  {name: "item3", price: 3},
  {name: "item4", price: 4},
  {name: "item5", price: 5},
  {name: "item6", price: 6},
];

const applyHandlingTax = add(5);
const applyShippingTax = add(10);

const numbers = shoppingCart.map(item => item.price); //extract prices from the shopping cart

const finalPrices = numbers
  .map(applyHandlingTax)
  .map(applyShippingTax);

console.log(finalPrices);

It's a naive example with exaggerated numbers but it just serves to show what we can do here. This is functionally the same as the previous snippet but as you can see, we now have business logic with barely any changes.

  • We've defined applyHandlingTax as adding 5. We've also defined applyShippingTax to be adding 10.
  • Both of these are functions, so we can apply them via .map directly onto the result set we have, reducing the amount of code we have to write.
  • The business rule is easy to understand, as you are writing pretty human readable code - you need very little knowledge of programming to understand numbers.map(applyHandlingTax).map(applyShippingTax) is applying shipping and handling tax to each of the numbers. And as a developer, it's clear that you are adding a shipping and a handling tax.
  • If we are sure that add works correctly, then by definition anything deriving from it would also work - there is no need to test applyShippingTax. In fact, there is nothing to test - there is no body of the function, we haven't written any logic for it, we only use the result of a function for it.
  • the only meaningful code we've written is for defining add and to extract the prices from the items. Even then, the latter can easily be abstracted in the same way that we've done with add, so we can apply it against any sort of result set

function extract(name) {
  return function(item) {
    return item[name];
  }
}

const shoppingCart = [
  {name: "item1", price: 1},
  {name: "item2", price: 2},
  {name: "item3", price: 3},
  {name: "item4", price: 4},
  {name: "item5", price: 5},
  {name: "item6", price: 6},
];

const people = [
  {name: "Alice",  drives: "Audi"},
  {name: "Bob",    drives: "BMW"},
  {name: "Carol",  drives: "Citroen"},
  {name: "David",  drives: "Dodge"},
  {name: "Esther", drives: "EDAG"},
  {name: "Fred",   drives: "Ford"},
];

const prices = shoppingCart.map(extract('price'));
console.log(prices);

const names = people.map(extract('name'));
const cars = people.map(extract('drives'));
console.log(names);
console.log(cars);

Even with a pretty trivial example of add we can get pretty far. The particular style of how add is written is known as currying - instead of taking X amount of parameters at once, the function returns another function that takes X - 1 until it's satisfied.

Functions that work with other functions - either producing them (like add does), consuming them, or both are called higher-order functions. If we use those and currying consistently we get into the territory of functional programming. Writing in a completely functional style is not a requirement to get benefit from these - as we saw with add, you can write a simple application without using lots of code even if we are applying business rules.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
1

That is a pretty synthetic and mostly useless example. In a nutshell, you need closures because Javascript uses a lot of callback based code, and you want to retain accessibility to the former scope in those callbacks:

function foo(bar) {
    setTimeout(function () {
        alert(bar);
    });
}

The closure mechanism here allows alert(bar) to still have access to bar, even though foo has already finished executing and all of its variables should have gone out of scope. That's what a closure is, it closes over the scope and keeps it available.

deceze
  • 510,633
  • 85
  • 743
  • 889
1

This particular example shows a curried function to enable partial application. It might not seem useful for a single function call, but also allows you to write

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

const add2 = add(2);
console.log(add2(1)); // 3
console.log(add2(40)); // 42

Sure, if the add function wasn't curried you could have used

function add2(y) {
  return add(2, y);
}

but that's unnecessarily verbose.


Of course, this highlights only a single use case of closures, there are many many more.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

'add(1)(2)(3)' and 'add(1,2,3)' have same result. but I can do many things with the former one. Let me give you an example.

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

const WELCOME_POINT = 10;
const VIP_POINT = 20;
function myPointCaculator(currentPoint, predicate) {
  if(typeof predicate === 'number')
    return currentPoint + predicate;
  else if(typeof predicate === 'function') {
    return currentPoint + predicate(WELCOME_POINT);
  } else {
    return currentPoint;
  }
}
  
const eventPoint = add(WELCOME_POINT)(VIP_POINT);
myPointCaculator(WELCOME_POINT, eventPoint);
'add(1,2,3)' is going to execute directly but 'add(1)(?)(?)' can be passed to function as parameter. That is different. This is about 'currying' and 'first order function' First order function in javascript can make you code with functional programming.
Junsuk Park
  • 193
  • 1
  • 13