Skip to content

Latest commit

 

History

History
131 lines (95 loc) · 4.15 KB

File metadata and controls

131 lines (95 loc) · 4.15 KB

Closures

Encapsulation and privacy

One of the core tenets of Object Oriented programming is encapsulation:

  • state (properties) and behavior (methods) are bundled in a single object
  • data is "hidden"
  • access to state is provided only through predicatable methods
    • getter methods return state values (or copies of state values if they are arrays/objects)
    • setter methods update state values

Privacy

Back in the day, to indicate a value in an object was "private" (and shouldn't be changed), we put a _ in front of the property name:

function makeVault(code) {
    return {
        _secretCode: code, // _ means that this is a "private value" and shouldn't be changeds
        open: function(code) {
            if (code === this._secretCode) {
                console.log("access granted")
            }
            else {
                console.log("access denied")
            }
        }
    }
}
const myVault = makeVault(12345);
myVault.open(12345);

This idea of "privacy" is only an illusion... nothing stops me from modifying the private property:

myVault._secretCode = 54321;
console.log(myVault.open(12345)); // access granted!

Closures are a feature of higher order functions

Remember, Higher Order Functions are functions that...

Reveal the answer!
  • Accept functions as arguments
  • Return functions

Each time we create a function, we create a closure. A closure is the combination of a function bundled together (enclosed) with references to its surrounding state. In other words, a closure gives you access to an outer function's scope from an inner function.

To use a closure, we return an inner function that references variables in the outer function's scope.

function getRandomPrinter() { // outer function
    // a closure is created around this variable
    const randomValue = Math.random();
    
    function printRandom() { // inner function
        
        console.log(randomValue);
        // this reference saves a reference to randomValue that would otherwise be lost when the function finishes
    };
    
    // the returned function will retain a reference to the closure variables
    return printRandom;
}

const randomPrinter1 = getRandomPrinter();

Q: Try invoking the returned function multiple times. Create a second printer function using getRandomPrinter() and invoke it. Observe what happens. How does the closure work to produce this result?

Answer

Once a printer function is returned from getRandomPrinter(), the returned function will hold onto the random value generated by getRandomPrinter(). That random value will NOT change throughout the lifetime of that returned function.

Each printer function returned from getRandomPrinter will have its own unique random value.

Closures allow us to create truly private values.

When working with factory functions, if our object has methods, those methods count as "inner functions" that can access the factory function's closure.

We use this to create truly private variables.

Notice that the object returned does NOT have a count property.

function makeCounter() {

    // a closure is created "around" this variable
    let count = 0;
    
    // the returned object will retain a reference to count, even after it is returned from the function.
    return {
        getCount() {
            return count;
        },
        increment() {
            count++;
        }
    }
}

// each instance has its own count value
const counter1 = makeCounter();
const counter2 = makeCounter();

counter1.increment();
counter1.increment();
counter1.increment();

counter2.increment();

console.log(counter1.getCount()); // 3
console.log(counter2.getCount()); // 1

Challenge:

Create a factory function for an object that has

  • A private (closure) variable
  • A getter method (returns the current value of the private variable)
  • A setter method (updates the value of the private variable).

Ideas:

  • A vault object with a private secretCode number
  • A shoppingCart object with a private items array
  • A user object with a private password string