1
var add = (function () {
    var counter = 0;
    return function () { 
        var reset = function() {
            counter = 0;
        }
        return counter += 1;
    }
})();

This is a self-invoking function that creates a "private" variable. How would I create a function reset that will reset the counter to 0? I've tried declaring the function inside the closure, but when I call it using add.reset(), it tells me this method is undefined.

pppery
  • 3,731
  • 22
  • 33
  • 46
bluejay5
  • 55
  • 1
  • 6
  • You can't access a local variable from outside its scope. You need to expose it somewhere. – SLaks Jul 26 '15 at 23:21
  • Hi bluejay5, if you feel that we have answered your question fully, don't forget to mark an answer as accepted by using the gray checkmark next to the answer! – Maximillian Laumeister Jul 28 '15 at 01:49

4 Answers4

4

I would recommend that instead of trying to put the function inside your closure, you put your variable outside your closure, like this:

var counter = 0;

var add = function() {
    return counter += 1;
};

var reset = function() {
    counter = 0;
};

That way the variable has proper scope for what you are trying to accomplish with it.

Maximillian Laumeister
  • 19,884
  • 8
  • 59
  • 78
  • 1
    Simpler and more intuitive. I like it. – heartyporridge Jul 26 '15 at 23:23
  • Doesn't actually answer the question though. – RobG Jul 26 '15 at 23:38
  • 1
    @RobG I try to provide the best answer (to get code working intuitively, to learn best coding practices), not necessarily exactly what is asked for – Maximillian Laumeister Jul 26 '15 at 23:42
  • Fine, but you should also answer the question since the example might just be an inappropriate case, rather than what the OP is really trying to achieve. – RobG Jul 26 '15 at 23:50
  • 1
    Yes that is the most obvious way to do it. But the problem with this is now the counter is no longer private. I'm wondering if we can retain the privacy of `counter` with closures – bluejay5 Jul 27 '15 at 07:13
  • @bluejay5 If you need a solution where the counter is private, please see [my new answer here](http://stackoverflow.com/a/31659500/2234742) – Maximillian Laumeister Jul 27 '15 at 17:29
4

You should return the reset function as a method of the object returned by the IIFE. That object needs to be the add function, so put the reset method on it. Just do it like you would in a global context, but inside a closure and return the add function, e.g.:

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

  function add(n) {
    counter += n || 0;
    return counter;
  }

  add.reset = function(){
    counter = 0;
    return counter;
  }

  return add;
}())

console.log(add(1)) // 1
console.log(add(4)) // 5
console.log(add.reset()); // 0

However, it would make more sense (to me) to have a counter object that has add and reset methods.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • sorry, but what is IIFE? – bluejay5 Jul 31 '15 at 07:08
  • IIFE is short for Immediately-invoked function expression. It is a technique used in Javascript to prevent the pollution of global namespace. So in this example, the variable counter is private and not part of the global namespace. Wikipedia explains it well, see https://en.wikipedia.org/wiki/Immediately-invoked_function_expression. – PatS Nov 27 '17 at 23:49
1

If you want to explicitly keep the counter declared inside the closure, you need to declare reset (even if you don't give it a value) outside the closure. To use your code, it would look like this:

var reset;
var add = (function () {
    var counter = 0;
    return function () { 
        reset = function() {
            counter = 0;
        }
        return counter += 1;
    }
})();

Now reset is outside the scope of the add function, so it keeps the value assigned within it!

To be fair, though, there's no reason to assign reset every time you can the result of add... It might be better to do something like:

var reset;
var add = (function () {
    var counter = 0;
    reset = function() {
        counter = 0;
    }
    return function () { 
        return counter += 1;
    }
})();

Or better still, if you want add.reset() to work:

var counter = function () {
    var counter = 0;
    this.reset = function() {
        counter = 0;
    }
    this.add = function () { 
        return counter += 1;
    }
};
var add = new counter();

Then add is a full object, which more or less sounds like what you want.

Or if you want to stick with the self invoking function:

var add = (function () {
    var counter = 0;
    return function () { 
        this.reset = function() {
            counter = 0;
        }
        return counter += 1;
    }
})();

Would probably work. It would be a slightly unusual paradigm from what I've seen though...

tobriand
  • 1,095
  • 15
  • 29
  • NB, that you'll get away with having 2 variables called counter because they have different scopes - just don't expect to be able to create a new counter within counter() without renaming the internal one... – tobriand Jul 26 '15 at 23:57
0

If you would like to keep the privacy of your current count, here is an answer that uses an object:

function counter() {
   var count = 0;

   this.reset = function() {
       count = 0;
       return count;
   };

   this.add = function() {
       return ++count;
   };
}

Then for instance:

var counter1 = new counter();
counter1.add();
console.log(counter1.add());
console.log(counter1.reset());
Maximillian Laumeister
  • 19,884
  • 8
  • 59
  • 78