js-201-module-supplement



js-201-module-supplement

0 0


js-201-module-supplement


On Github gdiminneapolis / js-201-module-supplement

JavaScript Module Pattern

Supplement to Intermediate JavaScript

Introduction

Yesterday, we covered how to use javascript libraries.

Today, I want to add a bit of information on structuring your javascript code.

I'm going to dicuss Modularity.

What is "Modularity"?

The degree to which a system's components may be separated and recombined.

Software Modularity

  • In software development, modularity refers to how the code is organized into self-contained units.
  • This improves the ability of the developers to test and maintain the software.
  • It decreases the likelihood of software collisions.

Modularity in JavaScript

  • In JavaScript, the Module Pattern has become a very common coding pattern.
  • It provides the means for modularity as discussed on the previous slide, as well as being the basis for all modern JS Libraries and Frameworks.

JavaScript Module Pattern

  
    var myModule = (function() {
      var vm = {};
      // module code implemented here
      return vm;
    })();
  

 

The vm variable refers to the term "View Model".

  • "View Model" is distinct from the Model-View-Controller paradigm. It refers more to a model (i.e. code) that implements the needs of a particular view.
  • vm is a convention for naming the inner variable used in a module that works on a particular view.

The IIFE

See this little thing on the end of our module?

  
    ...}());
  
  • That's an IIFE, which stands for "Immediately-Invoked Function Expression"
  • Since our module is implemented as a function, invoking it immediately defines the module, including all the inner function definitions
  • By returning the vm object we defined inside the module function, we gain access to all the methods and properties.
Remember that when we want to call a function, we add the parentheses pair after it's name. The IIFE calls the function we've just defined immediately after it's defined.

Defining the Module

We define the module by setting properties on the view model vm.

We can define module variables:

  
    vm.counterValue = 0;
  

We can define module functions:

  
    vm.increment = function() {
      // ...
    }
  

Private Module Parts

You can also define variables and methods that are only available in the module.

  
    var youCantSeeMe = "this is my own variable";

    function youCantCallMe(p1) {
      return "You can't call me";
    }
  

This keeps the scope of youCantSeeMe and youCantCallMe within the module, and not viewable or callable from outside.

Codepen Example: http://codepen.io/tamouse/pen/RaOeYY

Remember yesterday's Counter example?

  
    <div id="counter">
      <div id="result"></div>
      <button id="subtract">-</button>
      <button id="add">+</button>
    </div>
  

And yesterday's implementation (non-jQuery):

  
    var result = document.getElementById("result");
    var addButton = document.getElementById("add");
    var subtractButton = document.getElementById("subtract");

    addButton.addEventListener('click', function(){
      result.innerHTML = parseInt(result.innerHTML) + 1;
    });

    subtractButton.addEventListener('click', function(){
      result.innerHTML = parseInt(result.innerHTML) - 1;
    });
  

Counter Module (POJS)

  
var counter = (function() {
  var vm = {};
  var value = 0; // private!
  vm.init = function(addButton, subtractButton, resultArea) {
    vm.addButton = addButton;
    vm.subtractButton = subtractButton;
    vm.resultDiv = resultArea;

    vm.addButton.addEventListener('click', vm.add);
    vm.subtractButton.addEventListener('click', vm.subtract);
    vm.resultDiv.addEventListener('valueUpdated', vm.display);
    vm.reset();
  }
  vm.add = function() {
    value++;
    vm.display();
  }
  vm.subtract = function() {
    value--;
    vm.display();
  }
  vm.reset = function() {
    value = 0;
    vm.display();
  }
  vm.display = function() {
    vm.resultDiv.innerHTML = value;
  }
  return vm;
}());
counter.init(
  document.getElementById('add'),
  document.getElementById('subtract'),
  document.getElementById('result')
);
  

CodePen Example: http://codepen.io/tamouse/pen/rebvyP

  • "POJS" == "Plain Old JavaScript"
  • Separating the display function out separates the concerns of performing the counter operations from the concern of updating the data.
  • In a future refactoring, the display functionality would be moved even further and put into it's own module, and we'd use events to keep things decoupled.
  • The reason the three elements are passed in during the init() method invocation is to break the dependency between the module and the exact implmentation of the HTML.
  • In most circumstances, you want your modules to be reusable across projects, or even within the same project, without tying them directly to other parts of the project. You'll also likely be keeping the module code in a separate file, and invoking it in another place.
  • By passing the elements into init(), the module is able to deal with what it needs to, without being coupled to the HTML implementation.
  • This is generally referred to as injection.

Counter Module using jQuery

  
var counter = (function() {
  var vm = {};
  var value = 0; // private!
  vm.init = function(displayArea, incrementer, decrementer) {
    vm.displayArea = displayArea;
    vm.incrementer = incrementer;
    vm.decrementer = decrementer;

    vm.incrementer.on('click', vm.add);
    vm.decrementer.on('click', vm.subtract);

    vm.reset();
  }
  vm.add = function() {
    value++;
    vm.display();
  }
  vm.subtract = function() {
    value--;
    vm.display();
  }
  vm.reset = function() {
    value = 0;
    vm.display();
  }
  vm.display = function() {
    vm.displayArea.text(value);
    vm.displayArea.addClass("changing");
    setTimeout(function() {
      vm.displayArea.removeClass("changing");
    }, 1000);
  }
  return vm;
}());
counter.init($('#result'), $("#add"), $('#subtract'));;
  

Codepen Example: http://codepen.io/tamouse/pen/EKJpyQ

  • Similar to the POJS example, the jQuery example makes use of injection to decouple the counter module from the HTML implementation.
  • We still get to use the very nice jQuery methods instead of the clunky native DOM methods.

Resources

0