Last active
December 28, 2015 06:09
-
-
Save raorao/7455054 to your computer and use it in GitHub Desktop.
Here's my attempt at explaining closures in JavaScript.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// As I've been know to yell, JavaScript is a stupid language, and I don’t mean that as an insult. | |
// For example, where Ruby has 50-ish enumerable methods to iterate over collections, | |
// JS has, basically, two. But that’s okay! That just means you have to build any complicated | |
// logical structures yourself in JavaScript, which makes your code more transparent and flexible. | |
// One important structure you often find yourself building from scratch is private functionality | |
// in object oriented programming. JavaScript closures are one way to achieve that goal. | |
var counter = (function() { | |
var currentCount = 0; | |
return { | |
increment: function() { | |
currentCount += 1 | |
console.log("the counter is now set to " + currentCount) | |
} | |
} | |
})() | |
counter.increment() // returns "the counter is now set to 1" | |
counter.increment() // returns "the counter is now set to 2" | |
var currentCount = 100 | |
counter.increment() // returns "the counter is now set to 3" | |
console.log(counter.currentCount) // undefined | |
console.log(currentCount) // returns "100" | |
//So wait, this is OO? where are the prototypes? | |
// "counter" returns an object that has no prototype. It's public interface is an | |
// object literal, but it has access to the private "currentCount" variable. | |
// This is an entirely distinct way to do object oriented programming in JS. | |
//So what is "counter", then? | |
// Well, its just a function, like any other in JavaScript. | |
// When called, it simply runs its code as written. | |
//So if its just a function, how can the internal function “increment” see the variable “currentCount”? | |
// When “increment” is defined in "counter", it immediately understands that “currentCount” | |
// is the thing defined inside the "counter" function. Even after "counter" is finished running, | |
// the object literal returned by "counter" knows to reference the "currentCount" value. | |
// That’s why we can call “increment” later and the variable "counter" isn’t reset to 0 every time. | |
//But we redefined "currentCount"! why doesn’t "counter" update its "currentCount" variable? | |
// This is why closures are so cool. “increment” knows to reference the variable "currentCount" | |
// in its environment, which points to a particular piece of data. even if we redefined the | |
// variable "currentCount" in the global scope, "increment" is still referencing the original value. | |
// That "increment" is a function being defined in one environment and run in another makes it a closure. | |
//What's up with the "counter" being wrapped in parantheses? | |
// That just makes "counter" run immediately when defined. These two examples would run identically: | |
var oneOff = function() { return "started from the bottom" } | |
oneOff(); // returns "started from the bottom" | |
var oneOff = ( function() { return "now we here" } )(); | |
oneOff // returns "now we here" | |
// The only difference being that, if called multiple times, the first example would run multiple times. | |
// In the second example, oneOff would always reference the result of the first run. | |
// Anonymous functions don't necessarily have anything to do with closures, | |
// but are often convention when you are not using closures for distinct object instantiation. | |
// Wrapped in an anonymous function, "counter" is set to the returned object literal. | |
// Its also important to note that closures and OO aren’t necessarily related. | |
// We could have just as easily returned the "increment" function directly, and we could have | |
// also made "counter" a traditional function and used it for distinct object instantiation. | |
// Closures, for better or worse, are simply a way to manage state. | |
// Closures are also a helpful way to keep information private in JavaScript. | |
// As you can see, we can’t access the "currentCount" variable that was defined inside of "counter". | |
// I’ll leave you with a somewhat more complicated example of using closures and OO: | |
var createTree = function(type) { | |
var age = 0; | |
var height = 5; | |
var fruit = 0; | |
var ageOneYear = function() { age += 1 }; | |
var growOneYear = function() { height += 1 }; | |
var growFruit = function() { fruit += (age*2) }; | |
var isAlive = function() { return (age < 40) }; | |
return { | |
grow: function() { | |
if(isAlive()) { | |
ageOneYear(); | |
growOneYear(); | |
growFruit(); | |
} | |
}, | |
report: function() { | |
console.log("Your " + type + " tree is " | |
+ age + " years old, " | |
+ height + " feet tall, and has grown " | |
+ fruit + " " + type + "s."); | |
} | |
} | |
}; | |
var OrangeTree = createTree('orange'); | |
var AppleTree = createTree('apple'); | |
OrangeTree.grow(); | |
OrangeTree.report(); | |
// "Your orange tree is 1 years old, 6 feet tall, and has grown 2 oranges." | |
OrangeTree.grow(); | |
OrangeTree.report(); | |
// "Your orange tree is 2 years old, 7 feet tall, and has grown 6 oranges." | |
AppleTree.grow(); | |
AppleTree.report(); | |
// "Your apple tree is 1 years old, 6 feet tall, and has grown 2 apples." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment