Christine
Christine

Reputation: 93

Updating/Retrieving private counter in function

I've created a function below that updates a private counter which can only be accessed through another nested function within this function.

var private = function() {
  var counter = 0;
  return {
    add: function(increment) { counter += increment},
    retrieve: function() { console.log('the counter is currently set to ' + counter)}
  }
}

This is a syntactic question. I have trouble updating/retrieving the counter by accessing the function this way:

private().add(5);
private().retrieve(); // should return 5, but returns 0

However, calling the function in the same way but adding variable "p" somehow makes it work!

var p = private();
p.add(5);
p.retrieve(); //actually returns 5

Can't for the life of me figure this out. Any guidance on this would be greatly appreciated.

Tried searching other questions on this website but couldn't really find the solution here either:

accessing variables in javascript closures

Upvotes: 2

Views: 784

Answers (1)

user4639281
user4639281

Reputation:

This is called a "factory function", it creates a "safe" lexical scope, meaning that the variables contained within the function cannot be altered or otherwise accessed from the global scope.

Every call to the factory function (every instance of private() in your code) invokes the factory function again, creating a new scope. When you assign the returned value of private() it saves the returned object, and its access to the lexical scope in which it was created, to that variable.

Using the Immediately Invoked Function Expression syntax (hereafter referred to as IIFE) we can expand the examples to show what is happening.

The first example:

private().add(5);
private().retrieve();

is expanded as follows:

(function(){
  var counter = 0;
  return {
    add: function(increment) { counter += increment},
    retrieve: function() { console.log('the counter is currently set to ' + counter)}
  };
})().add(5);
(function(){
  var counter = 0;
  return {
    add: function(increment) { counter += increment},
    retrieve: function() { console.log('the counter is currently set to ' + counter)}
  };
})().retrieve();

While the second example:

var p = private();
p.add(5);
p.retrieve();

is expanded as follows:

var p = (function(){
  var counter = 0;
  return {
    add: function(increment) { counter += increment},
    retrieve: function() { console.log('the counter is currently set to ' + counter)}
  };
})();
p.add(5);
p.retrieve();

That is why the first example does not provide the expected output, and the second example does.

So, if you only want one safe scope, and you won't need to create a new scope later on, an IIFE will suffice.

var private = (function() {
  var counter = 0;
  return {
    add: function(increment) { counter += increment},
    retrieve: function() { console.log('the counter is currently set to ' + counter)}
  }
})();

private.add(5);
private.retrieve(); // the counter is currently set to 5
private.add(5);
private.retrieve(); // the counter is currently set to 10

Upvotes: 8

Related Questions