1

I'm trying to get a solid understanding of closures, and I'm struggling with the mechanics of it. I've looked on w3schools (https://www.w3schools.com/js/js_function_closures.asp) and MDN (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) and a few other places. I understand what a closure is and how to make it work but I don't understand why subsequent calls after the first call to the outer function just seem to go straight to the inner one instead.

Here is my code - a simple, working closure:

  var add = (function () {
    var a = 0;

    alert('hi');

    function addInner(){
      a += 1;
      return a;
    }
    return addInner;
  })();

  function getAdd() {
    document.getElementById("test").innerHTML = add();
  }

I have two questions that probably both have the same answer: Why does a not get reset every time I call add()? And why does the alert not pop up except for on the first time? I attached getAdd() to a button, and it works great, but it but doesn't pop up with the alert past the first time. Why does it do this?

Edit: I also found the first answer to this question (Why Don't Variables Reset in a Closure (Javascript)) really helpful.

Jamie Sauve
  • 270
  • 1
  • 4
  • 20

5 Answers5

2

add is a reference to addInner, not the anonymous "outer" function, since the "outer" function returns addInner. Then, you call that anonymous function once - that last set of ()- and store the resulting function, with its own private 'a', in add.

Imagine that the outer function was a named function called constructAdder, and you called

var add = constructAdder();
var add2 = constructAdder();

That's basically what you've done, but with an anonymous function, if that makes it any clearer. You have an outer function that constructs an inner function that can count, due to the magic of closures.

dsolimano
  • 8,870
  • 3
  • 48
  • 63
1

Those questions do both have the same answer. And it's fairly simple. This statement:

var val = (function() {
  return 42
}())

sets val to 42. Not a function. This is called an IIFE, or immediately-invoked function expression. We are declaring a function and calling it immediately. We never save that function. We just use it once to get the value it returns:

val // 42

Contrast that to:

var val = function() {
  return 42
}

In this case we set val to a function that, when called, returns 42.

val() // 42
val() // 42

We can get pretty crazy with IIFEs:

var val = (function() {
  return (function() {
    return (function() {
      return 42
    }())
  }())
}())

The value returned by that mess is still 42.

val // 42

All those functions are declared once, used once, and thrown away. But IIFEs are capable of so much more:

var add = (function() {
  var counter = 0

  return function() {
    return counter++
  }
}())

We're now using the IIFE to create a private variable! counter cannot be accessed outside the scope of the IIFE. It's safe. But the inner function can access and modify it. It's a fully encapsulated, stateful function!

add() // 0
add() // 1
counter // Uncaught ReferenceError: counter is not defined

This is what your code is doing:

var add = (function () {
  var a = 0;

  alert('hi');

  function addInner(){
    a += 1;
    return a;
  }
  return addInner;
})();

It is encapsulating the a variable so that it cannot be accessed outside the scope of the IIFE. The addInner function, however, does have access to a and can modify it at will.

The alert() is only called once because that IIFE is only called once, then thrown away. Note that the function itself is the only thing thrown away. Since addInner maintains a reference to the scope (closure) created by the IIFE, that scope is safe from garbage collection.

In fact, that's a helpful way to think about IIFEs:

var val = function() {}

creates a function and

val()

creates a closure (the context that the function's body runs in). IIFEs are used when we don't care about the function, we just want the closure it creates.

Hope this helps. Have fun out there!

bowheart
  • 4,616
  • 2
  • 27
  • 27
  • Thanks! I didn't realize the outer function was being thrown away. It makes sense now. I'll keep practicing with these. – Jamie Sauve Feb 12 '18 at 15:42
0
var add = (function () {
    var a = 0;

    alert('hi');

    function addInner(){
      a += 1;
      return a;
    }
    return addInner;
  })(); <-- This

The () at the end means that add is added forever, and if you look at the line above, then it says return addInner. This means that add() is actually addInner(), the good thing with this is that addInner has access to a. Actually writing the function like this is more proper.

var add = (function () {
    this.a = 0;

    alert('hi');

    var addInner = function(){
      this.a += 1;
      return this.a;
    }.bind(this);
    return addInner;
  })();

Because now you would think that add.a would make a available because it is written as this.a, but since addInner is returned, then it is not available.

Pavlo
  • 1,157
  • 7
  • 13
0

You should focus on the

(function(){
    // impl
})()

block. It is called an Immediately Invoked Function Expression or IIFE.MDN reference

So when the code executes to line 1, it first evaluate the right side and the IIFE returns a closure which was then assigned to the variable add. Meanwhile, an alert was invoked in the IIFE. Understanding the IIFE can help you solve the problems.

obfish
  • 673
  • 4
  • 17
0

In my opinion , the key is IIFE, after the code below is executed at the first time,

var add = (function () {
    var a = 0;

    alert('hi');

    function addInner(){
      a += 1;
      return a;
    }
    return addInner;
  })();

(Look out ! There is no add() in the code above because IIFE will be Invoked Immediately and the return addInner finished the initialization of variable add )

the function add has been changed to :

add = function addInner() {
    a += 1;
    return a;
}

Of course, the alert() only executed one time because the add has been changed at the begining.

Why does a not get reset every time I call add()?

And why does the alert not pop up except for on the first time?

that is the answer of your question.

variable a didn't destroy because the function add still have a reference, and this is about the closure.

Community
  • 1
  • 1
xianshenglu
  • 4,943
  • 3
  • 17
  • 34