Object Literals and ze Module Pattern

What are Object Literals?

Object Literals are literally JUST objects.

No fancy OOP, or closures, it's just {}

You can only EVER have one instance of an object using an object literal

You don't use the function keyword to define an object literal

//Object literal

var myObject = {};

var myInformation = {
    name: "Jason",
    age: 82,
    alias: "jaywon"
};
//NOT AN Object literal

var myFunction = function(){};

var myInformation = function(){
    var name = "Jason";
    var age = 82;
    var alias = "jaywon";
};

Object Literal

Function Expression

  • Both use {} to define scope but a function must have the function keyword
  • An object literal will never have a var keyword unless a function is defined as a property
  • Equal signs and semi-colons on the end of lines also an indicator

Object literals DO have context and this and can have functions as values

var calculator = {

    total: 0,

    name: "TI-85",
    
    clear: function(){
        this.total = 0;
    },

    add: function(num){
        this.total += num;
    },

    subtract: function(num){
        this.total -= num;
    },

    square: function(num){
        this.total = Math.sqrt(num);
    }
};

Remember an Object is simply a sequence of keys and values or key-value pairs separated by commas.

var calculator = {

    total: 0,

    name: "TI-85",
    
    clear: function(){
        this.total = 0;
    }
};

calculator.serialNumber = "JSK245EE";

calculator.add = function(num){
    this.total += num;
};

calculator.subtract = function(num){
    this.total -= num;
};

calculator.square = function(num){
    this.total = Math.sqrt(num);
};

You can also add additional keys or properties to an object outside it's definition

The Module Pattern

Your very first design pattern

The Module Pattern can be thought of a way to implement "privacy" on our object literals, or private and public properties using closures.

We can still only ever have one instance of an object using this pattern just like an object literal.

The Module Pattern allows us "class like" benefits and keeps parts of our object out of the global scope.

The Module Pattern

With the Module Pattern we protect certain properties of our object and only return(expose) certain properties.

This can be thought of as an API for your object and the properties our object is returning is known as it's interface.

The values of the private properties are protected and kept alive within the closure.

The practice of protecting or keeping values private/protected is known as encapsulation.

The Module Pattern vs. Object Literal

The Module Pattern uses an Immediately-Invoked Function Expression(IIFE) to return an object literal.

We still cannot call new on our object.

What we have access to looks and behaves very much the same as an object literal.

We can still only have 1 instance of this object.

It's just how we created it that changed.

It's just a function that returns an object to our variable.

var myObject = {

    name: "Jason",
    
    getName: function(){
        return this.name;
    }
};

// We can access ALL properties/members 
// of an object

myObject.name = "Jon";

myObject.getName(); // "Jon"

Object Literal

var myObject = (function(){

    var name = "Jason";

    return {
        age: 23,

        getName: function(){
            return name;
        }
    };
})();

// We can access only exposed 
// properties/members of an object

myObject.age = 75; // 75

//does not change internal name
myObject.name = "Jon";

myObject.getName(); // "Jason"

Module Pattern

myObject is an Object

myObject is an Object

var mySecretModule = (function () {

    //private variables
    var module = {};
    var privateSecret;

    //public variables
    module.publicSecret = "I like Tegan and Sara.";

    //public methods
    module.protectSecret = function(secret) {
        privateSecret = secret;
        console.log("Your secret is safe.");
    };

    return module; //return the object literal we created
})();

mySecretModule.protectSecret("I like clowns."); // "Your secret is safe";

console.log(mySecretModule.privateSecret); //undefined

console.log(mySecretModule.publicSecret); // "I like Tegan and Sara."

You can create your object before your return for greater readability.

var mySecretModule = (function ($, config) {

    var module = {};

    module.findDomElement = function(){
        return $('#someIdentifier');
    }

    module.doSomeImportantThings = function() {
       if(config && !config.production){
           console.log('some testing variables');
       }
    };

    return module; //return the object literal we created
})(jQuery, {production:true});

Mix-ins or Dependency Injection

This is a way for us to pass in variables to our modules that they depend on without having to depend on global scope or naming convention.

Our module is now dependent on the internal existence of those objects but not the external implementation

var mySecretModule = (function () {

    //private variables
    var module = {};
    var privateSecret;

    //public variables
    module.publicSecret = "I like Tegan and Sara.";

    //public methods
    module.protectSecret = function(secret) {
        privateSecret = secret;
        console.log("Your secret is safe.");
    };

    module.changePublicSecret = function(secret) {
        module.publicSecret = secret; // <----- EWWWWWW
    };

    return module;
})();

Revealing Module Pattern

The Problem

Access to other variables/functions in module must be prefaced with module name which can be cumbersome

var mySecretModule = (function () {

    //ALL variables are private 
    var privateSecret;
    var publicSecret = "I like Tegan and Sara.";

    //All methods are private
    function protectSecret(secret) {
        privateSecret = secret;
        console.log("Your secret is safe.");
    }
  
    function getPublicSecret(){
      return publicSecret;
    }

    function changePublicSecret(secret) {
        publicSecret = secret;
    }

    //REVEAL only what you want to be public
    return {
      getSecret: getPublicSecret,
      protectSecret: protectSecret,
      changeSecret: changePublicSecret
    };
})();

Revealing Module Pattern

The Solution

All functions/variables are private and interact with each other without object reference notation and only expose(reveal) pointers.

Module Pattern vs. Revealing Module Pattern

Solution:

Use what you prefer and know why you chose it.

After all they're just patterns.

Things to Remember

  • This is a good way to implement encapsulation or private variables.

  • You cannot access private members in methods that are added to the object later.

  • You cannot write tests for private members directly.

  • Because our object was created in a function we are persisting state with a closure.

Additional Resources

deck

By Jason Sewell