0

So we have this code, found at this page, which I am using to try to understand variable scopes, IIFEs, and so:

// Because this function returns another function that has access to the
// "private" var i, the returned function is, effectively, "privileged."

function makeCounter() {
  // `i` is only accessible inside `makeCounter`.
  var i = 0;

  return function() {
    console.log( ++i );
  };
}

// Note that `counter` and `counter2` each have their own scoped `i`.

var counter = makeCounter();
counter(); // logs: 1
counter(); // logs: 2

var counter2 = makeCounter();
counter2(); // logs: 1
counter2(); // logs: 2

i; // ReferenceError: i is not defined (it only exists inside makeCounter)

This way produces a similar result as using a global variable and using it on each function calling, in a more safe, elegant way, isn´t it?

2 questions

1-I can´t see how the i variable scope works to maintain the counter value and not to reset it to 0 each time function is invoked again. I know this is used to avoid global variables but I can´t see how it is done to maintain the counter value between different calls to

counter();

2-why the funcion returned shows console.log(value) only when you have assigned the function to another variable?

var counter2 = makeCounter();
    counter2(); // logs: 1

But

makeCounter(); //logs nothing

I don´t know why. Ok, I guess on

makeCounter(); 

you are only declaring the returning function, not calling it... But I don´t know why is different when I do

var counter2 = makeCounter();
    counter2();

3 Answers3

0

What happens is that within the function makeCounter:

function makeCounter() {
  var i = 0;
  return function() {
    console.log( ++i );
  };
}

You are returning a function, then to use it is the following:

const counter = makeCounter(); // execute function and return other function
counter(); // This executes the function that you returned

If for example you did not return a function, it would work as expected:

function makeCounter() {
    var i = 0;
    console.log( ++i );
}
makeCounter(); // this execute logs
Joel Jaime
  • 459
  • 2
  • 9
0

It's easy to understand closures once you compare them with what the equivalent singleton using globals would be:

(full disclosure, this example is bad practice)

var i = 0;

function counter() {
  console.log(++i);
}

counter();
counter();

The point is, above is the approach that closures avoid in order to not pollute the global namespace.

You simply create a function that initializes a closure that acts similar to the example above, but has its own scope which is hidden from the global namespace:

function makeCounter() {
  // these lines look familiar
  var i = 0;

  // we have to `return` the
  // reference to this function
  return function counter() {
    console.log( ++i );
  };
}

// here we are initializing a new scope
// and storing a reference to the function
// `counter()` defined within the closure
var counter = makeCounter();

// from here it's the same as the example above
counter();
counter();
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Ah! So, each time we execute counter(); we execute the returning function! We only declare i when we assign `counter=makeCounter();` isn´t it? But It is kind of weird for me... I still don´t get that way of initializing new scopes and execute the returning function when you use `counter()`. I guessed you were executing the whole passed function `makeCounter()` –  Dec 20 '17 at 02:50
  • 1
    @FranP Look at the entire body of `makeCounter()`, excluding the body of the inner `counter()`, that's what's invoked when you execute `makeCounter()`. You define a variable `i` and a function `counter()` with `i` in its scope, and then return a reference to that inner function. Each time `makeCounter()` is invoked, it's a new `i` and a new `counter()`. The returned value, when we store it to `counter1` and `counter2`, allows us to increment and print each function's respective `i`, because they're completely separate and independent instances. – Patrick Roberts Dec 20 '17 at 03:12
  • Yes, I understand that. The hard point for me is to see how the value of i is stored between `counter()` executions, how the `var i=0` is only executed the first time and not every time we call `counter()`. Now i know that once we made `var counter=makeCounter()` , then `counter()`only executes the returning function, not the whole code inside `makeCounter()` and this returning function is able to store the value of i without returning to 0. Step by step it comes more clear to me but still hard to see. –  Dec 20 '17 at 03:23
  • 1
    Well refer to the global equivalent I provided in the first example. You only run `var i = 0;` once, and define the function. Then each time the function is invoked, it increments `i` and prints it. The closure is identical except it's not in the global scope. – Patrick Roberts Dec 20 '17 at 03:37
  • Yes @Patrick Roberts, only wanted to know how it works to do the same task without global variables, that´s what I don´t understand yet, but it slowly comes –  Dec 20 '17 at 03:47
  • Like replicating global scope in a private, more protected, more secure scope? Suddenly it comes much more clear! Maybe my problem is that I was (still am) confused about the way creating new scopes assigning functions to vars... –  Dec 20 '17 at 04:04
  • @FranP you could call it more "private", sure, but I want to clarify that this is about properly _organizing_ code, it has _nothing_ to do with making it _secure_. – Patrick Roberts Dec 20 '17 at 23:47
-1

For question "why the function returned shows console.log(value) only when you have assigned the function to another variable?"

var counter2 = makeCounter();
    counter2(); // logs: 1

makeCounter(); //logs nothing

As you would notice the code inside "makeCounter" function returns another function. Only executing "makeCounter()" is actually returning a new function rather than executing it.

When we write "var counter2 = makeCounter();" actually we are assigning the returned function to a variable and the further executing "counter2()". To have more clarity we can try "makeCounter()()" this would execute the returned function.

Referring to other question, every function in JavaScript is a "Function Object" so two objects cannot be same and hence have different scope. Consider below scenario:

var a = {"name":"Joe"}
var b = {"name":"Joe"}
console.log(a === b) // return false
Nitin Sinha
  • 179
  • 3