Wednesday, September 7, 2016

JavaScript closures - basic examples

Closures topic can be not obvious for developers which came from another languages like Java. So let's try to understand "why?".

1. What is "closure"? 


In shorts: JavaScript doesn't have PRIVATE access modifier. So closures it's the only way to limiting access.

From Wiki:
In programming languagesclosures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functionsOperationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.


Summary: function have access to variables defined on the upper levels:

var counter=0;
  function inc() {
     return++counter;
  }

Everything is pretty simple here. But what is we have the same inside another function? Like this:

function CreateCounter() {
  var counter=0;
  return function() {
     return++counter;
  }
}

Let's do this in developer console and after that define variable:
var inc=CreateCounter()

Value of this variable is:
>inc
function() {
     return++counter;
  }

So "inc" - is now function which "closed" around  scope of "parent" function. Scope of parent function has just one variable: "counter". And by every next invocation on inc()  this variable will be increased. We can create another counter by next call: var anotherInc=CreateCounter()
CreateCounter() function will create new variable   var counter=0 and return function which will be "closed" around it(linked) with it. So new counter will be independent of the previous one.

Let's try it in practice.

2. Preparations. 

First of all we need several files:
- html file for displaying test results: test.html
- our script file with algorythm implementation: MyScript.js
- file with test scripts for previous step: MyScriptTest.js
- libraries mocha.js and chai.js - they will be used as external scripts.
All this files I put to one directory. Here is the content of test result file: test.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">

  <!-- Mocha css -->  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.1.0/mocha.css">
  <!-- Mocha dependency -->  <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/2.1.0/mocha.js"></script>
  <!-- Mocha: setup BDD -->  <script> mocha.setup('bdd'); </script>
  <!-- chai dependency -->  <script src="https://cdnjs.cloudflare.com/ajax/libs/chai/2.0.0/chai.js"></script>
  <!-- export assert -->  <script>  var assert = chai.assert;  </script>
</head>

<body>
  <!-- script which should be tested -->  <script src="MyScript.js"></script>
  <!-- test itself -->  <script src="MyScriptTest.js"></script>
  <!-- element with id="mocha" for test results -->  <div id="mocha"></div>
  <!-- run tests! -->  <script>
    mocha.run();
  </script>
</body>
</html>

As you can see, mocha and chai libraries will be downloaded by SRIPT tag. Also we set up mocha for using BDD, exported "assert" for simple usage and executed mocha test running.
In addition, we need just 2 files:
<!-- script which should be tested --><script src="MyScript.js"></script>
<!-- test itself --><script src="MyScriptTest.js"></script>

- we will be creating them step-by-step during implementation. First - function for MyScript, next - testblock for that function in MyScriptTest.js.

3. Without closures.

First of all, for creating COUNTER functionality, we can try to use approach popular for other languages: just create an object for that:

function CounterObject() {
    this.currentValue = 0;

    this.inc = function () {
        return ++this.currentValue;
    }
}

Let's test it:
describe("CounterObject", function () {
    it("should be independent counters on every creation of counter object", function () {
        var counter1 = new CounterObject();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.inc(), 2);

        var counter2 = new CounterObject();
        assert.equal(counter2.inc(), 1);
        assert.equal(counter2.inc(), 2);
    });

});


It's working! But unfortunately we have one vary bad thing here, developer console is returning:
>c = new CounterObject()
CounterObject {currentValue: 0}
    currentValue: 0
    function inc: ()

- beside inc() function we have access to currentValue variable as well. So everyone can just change it by re-assigning new value. We can not protect it from changing outside, because JavaScript doesn't have PRIVATE variable access. Let's create another test:
    it("should be possible to change current value because it unprotected at all", function () {
        var counter1 = new CounterObject();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.inc(), 2);

        counter1.currentValue = 9;
        assert.equal(counter1.inc(), 10);
    });

Current value was changed and we can do nothing with that. So this approach will not work for JavaScript.

4. Safe counter with closures.

With new version of counter we are protected form direct variable changes:
function SafeCounter() {
    var currentValue = 0;
    return function () {        
         return ++currentValue;    
     }
}

>c = SafeCounter(): 
function() { return ++currentValue; }
- we have just one function inside "c" variable without access to local variable currentValue.


Tests:
describe("SafeCounter", function () {
    it("should be independent counters on every call of SafeCounter() function", function () {
        var counter1 = new Object();
        counter1.inc = SafeCounter();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.inc(), 2);

        var counter2 = new Object();
        counter2.inc = SafeCounter();
        assert.equal(counter2.inc(), 1);
        assert.equal(counter2.inc(), 2);
    });
});


5. Extended version of safe counter. 

On previous step we created counted which fit our expectations, but we are returning only one function - so we can perform only one operation. Sometimes we may need more. It's possible to do that by returning an object:

function SafeExtendedCounter() {
    var currentValue = 0;

    return {        
          inc: function () {            
              return ++currentValue;        
          },        
          get: function () {            
              return currentValue;        
          },        
          reset: function () {            
              currentValue = 0;        
          }    
    };
}

Test:
describe("SafeExtendedCounter", function () {
    it("should be independent counters on every call of SafeExtendedCounter() function", function () {
        var counter1 = SafeExtendedCounter();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.inc(), 2);

        var counter2 = SafeExtendedCounter();
        assert.equal(counter2.inc(), 1);
        assert.equal(counter2.inc(), 2);
    });

    it("should be GET function for getting current value", function () {
        var counter1 = SafeExtendedCounter();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.get(), 1);
    });

    it("should be RESET function for reseting counter to zero", function () {
        var counter1 = SafeExtendedCounter();
        assert.equal(counter1.inc(), 1);
        assert.equal(counter1.inc(), 2);
        counter1.reset();
        assert.equal(counter1.get(), 0);
    });
});


6. Closures for "modules"(singleton objects)

Sometimes we may need a library or module with some functionality. Implementation should be hidden from outside and only interface functions should be visible. That's exactly what we created on previous step, but with the one exception: it's creating new object on every call, and it can consume a lot of resources. Sometimes it's better to have just one object implementation: "singleton" object.

We can implement that by creation on anonymous function :
(function() {......}), call it just after creation by "()" and assign it to variable:


var sayHelper = (function () {
    var name = "";
    var HI_MESSAGE = "Hi, ";
    var HELLO_MESSAGE = "Hello, ";
    return {
        setName: function (nameValue) {
            name = nameValue;
        },
        sayHi: function () {
            return HI_MESSAGE + name;
        },
        sayHello: function () {
            return HELLO_MESSAGE + name;
        }
    }
})();

Last "()" before ";" - for calling function bloc.

Now sayHelper is a singleton object. We can not create another instance, because function is anonymous, it has no name so we can not call it.


Test:
describe("sayHelper", function () {
    sayHelper.setName("Joe");
    it("should be [Hi, Joe] when we call sayHelper.sayHi()", function () {
        assert.equal(sayHelper.sayHi(), "Hi, Joe");
    });
    it("should be [Hello, Joe] when we call sayHelper.sayHello()", function () {
        assert.equal(sayHelper.sayHello(), "Hello, Joe");
    });
});


7. The end. 

In JavaScript closures used for making some resources "PRIVATE" and so on, it can be useful for modules/libraries. All source code can be downloaded from here.  

No comments:

Post a Comment