9

I'm learning javascript by reading "Eloquent Javascript" and am confused by the "Closures" section in chapter 3 (Functions).

In previous sections I learned about arrow functions, and how they can be used as anonymous functions. My initial thoughts were that this is an anonymous function example and I am simply not familiar yet.

In particular, I am confused on what "() => local" does to/for return.

function wrapValue(n) {
  let local = n;
  return () => local;
}

let wrap1 = wrapValue(1);
let wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2

Here is a link to the chapter: Eloquent Javascript - Ch. 3 "Functions"

Thanks in advance!

Tucker
  • 367
  • 1
  • 3
  • 11
  • 1
    It's just a function that returns the variable `local` when called. The point of the closure is that the variable `local` is set in the outer function, but continues to be within scope of the returned function even after `wrapValue` has returned. – Mark Dec 19 '18 at 23:59
  • Seems like a convoluted way of doing `const wrap1 = 1;`. – RobG Dec 20 '18 at 00:01
  • 1
    @RobG - not convoluted, it's "eloquent" :D – Jaromanda X Dec 20 '18 at 00:03
  • @MarkMeyer Oh ok, I see. So this is quit simply returning whatever it was given by assigning n to local, then returning local. Convoluted indeed! haha So why wouldn't I just use "return local"? – Tucker Dec 20 '18 at 00:07
  • @Tucker You could, but it wouldn't demonstrate how the variables are still accessible after the wrap function has returned. – Feathercrown Dec 20 '18 at 00:13
  • A closure is a function that accesses a variable that is not one of its parameters and not declared locally. The function returned by `wrapValue` is therefore a closure. Directly returning `local` would not demonstrate how closures work. – Felix Kling Dec 20 '18 at 00:20
  • @Tucker. There's a lot more to them once you get past the really simple example. See below for a slightly more involved answer. – Mark Dec 20 '18 at 00:20
  • Ironically, I believe this example within the book was over simplified and didn't relay the utility of Closures clearly. @MarkMeyer post below clarified it for me. – Tucker Dec 20 '18 at 00:47
  • Probably a duplicate of [*How do JavaScript closures work?*](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – RobG Dec 20 '18 at 05:17

2 Answers2

4

In javascript function create scope. For example:

function scoped(){
  let local = 10;
  console.log(local) // this works local is within scope
}

scoped() // logs:

console.log(local) // error -- local doesn't exist out here.

Outside of scoped local doesn't exist.

A function inside a function has access to the entire scope. So this works:

function scoped() {
  let local = 10;
  function f() {
    console.log(local) // this works local is within scope
  }
  f()
}

scoped()

But what happens if you return that function that references the scope of the function? It turns out that the inner function carries the whole scope with it. This is a closure:

function scoped(){
  let local = 10;
  function f(){
    console.log(local) // this works local is within scope
  }
  return f
}

f = scoped()
f() // can still see local

// but nobody else out here can:
console.log(local) // still an error

This allows you to do some very slick things — an import one is that you can keep certain variables private, but still manipulate them with the closure.

For example here's a counter that doesn't require a variable in global scope:

function counter(){
  let count = 0
  return () => count++
}

let c = counter()

console.log(c())  // it can count but there's not count variable in scape
console.log(c())
console.log(c())

Doing this with a global count variable is messy and risks some other part of the code clashing with the global variable. Above, nothing other than the c function can access the counter. In fact you can make several independent ones:

function counter(){
  let count = 0
  return () => count++
}

let c = counter()
let d = counter()

console.log("c:", c())  // it can count but there's not count variable in scape
console.log("c:", c())
console.log("d:", d())  // d has it's own closure
console.log("d:", d())
console.log("c:", c())

There's a lot you can do with closures and they're important in Javascript. It's worth taking the time to really understand them.

Mark
  • 90,562
  • 7
  • 108
  • 148
  • 1
    Ahhhhh, I think it clicked now. Please correct me if I am wrong, but what i got from this is that with Closures I have the ability to, in a sense, elevate the scope of a value. So rather than a variables value being limited to the scope of its function, I can use a Closer function within that function, and then call the INNER function independently from the OUTER function- which will utilize the outer function's scope variables and in a sense elevate the values to a higher scope (or at least make those values useful for/at a different scope)? – Tucker Dec 20 '18 at 00:34
  • 1
    Yeah @Tucker, that makes some sense to me. I think of it more like the inner function is able to tuck away the scope of the outer function and carry with it. Allowing it to use it without anyone else having access to it. – Mark Dec 20 '18 at 00:40
  • 1
    That totally clarified it. Thank you very much Mark! Cool photography by the way! :) – Tucker Dec 20 '18 at 00:43
  • 1
    All functions create closures to the variables in their outer scopes. It's just that persisting closures by returning a function makes them interesting. – RobG Dec 20 '18 at 05:11
3
function wrapValue(n) {
   let local = n;
   return () => local;
}

Is the same as writing (without the benefits of "this")

function wrapValue(n) {
   let local = n;
   return function() {
        return local;
   }
}

It's a function that returns another function that uses the passed parameters. This is called a Curry function

console.log(typeof wrapValue(2)); // prints "function"
console.log(wrapValue(2)()); // prints "2"

Here's a better example

function multiple(a) {
    return (b) => a*b;
}

console.log([1,2,3,4].map(multiple(2)); // prints "[2,4,6,8]"

You can use curried functions very easily with Arrays

Reactgular
  • 52,335
  • 19
  • 158
  • 208
  • I'd argue that this isn't actually currying. As the article you link to says, currying is the process if converting a function that takes multiple arguments, to a function that accepts one argument and returns a function. `multiple` is not curried here, it is already a function that accepts a single argument. So it's just a higher order function? Or my understanding of currying is incorrect... – Felix Kling Dec 20 '18 at 00:23
  • With regards to the second "function()" version, would the inner function still be able to access the "local" variable that is in the scope of the parent function? Or is that the difference between the two? – Tucker Dec 20 '18 at 00:26
  • @FelixKling *multiple* takes 2 parameters, but each parameter has it's own function. Higher order function is where each parameter is a function and it returns another function. – Reactgular Dec 20 '18 at 00:27
  • @Tucker: The answer says *"Is the same as writing (without the benefits of "this")"*... – Felix Kling Dec 20 '18 at 00:27
  • @FelixKling I apologize, I am yet to get to the chapter on "this" and it's functionality. – Tucker Dec 20 '18 at 00:28
  • @Tucker I don't know what the author's intention was to use a local variable. There are no function scope issues related to the example. `function wrapValue(n) { return ()=>n; }` is the same result. – Reactgular Dec 20 '18 at 00:29
  • Higher order functions do not have to accept functions as arguments: https://en.wikipedia.org/wiki/Higher-order_function. In your example `multiple` does not accept two arguments. Currying would be the *process* of taking a function `multiple(a,b)` and returning what is defined in your example. In other words, your example would be the *result* of currying, `multiple` is a curried function, but it is not currying.... if that makes sense. (I think I'm just being pedantic). – Felix Kling Dec 20 '18 at 00:30
  • @cgTag the map sample code is spot on. Thanks for a great example of the purpose. – Bibberty Dec 20 '18 at 00:31
  • @FelixKling I get it. Thanks. – Reactgular Dec 20 '18 at 00:33
  • "*…without the benefits of "this"*" seems like a red herring. – RobG Dec 20 '18 at 05:13