Play with closures
Here are two snippets of JavaScript code from class. Here is a Javascript playground you can use to run this code. (Note the Maximize button at the top, which makes it possible to see more of the code at once.)
Please play around with these examples — make changes, try experiments, see what works, see what breaks — until you get to a point where they’re starting to make sense. Post questions in the class Slack channel!
You might also want to skim over the Graphing Calculator activity from COMP 127, just to jog your memory. Java and Javascript have very similar syntax for anonymous functions / closures / lambdas. (“Lambda” or “lambda function” are just alternate words for “closure.”)
Example 1: Passing a function as a value
// `console.log` is a function, and a function is a value!
// That means we can assign that value to a new variable,
// making a nice alias for an existing function:
let print = console.log;
let x = 0;
function sayHi() {
print("hi number " + x + "!");
x++;
}
function thrice(f) {
f();
f();
f();
}
thrice(sayHi);
Check your understanding: why doesn’t this work??
thrice(sayHi());
Example 2: Function as return value; variable capture
let print = console.log;
function thrice(f) {
f();
f();
f();
}
function makeCounter(msg) {
let count = 0;
return () => {
print(msg + " is now " + count);
count++;
};
}
let penguinCounter = makeCounter("penguin");
let duckCounter = makeCounter("duck");
let lionCounter = makeCounter("lion");
thrice(penguinCounter)
duckCounter()
penguinCounter()
thrice(lionCounter)
thrice(duckCounter)
Things to note about that example:
- The return value of
makeCounteris an anonymous function. That’s what the(…) => { … }syntax means: “anonymous function.” That entire function is a single value! We can return it, pass it around, assign it to a variable, and put it in a larger expression just like any other value in the language. -
countis a local variable. Normally its lifetime would end with the closing brace ofmakeCounter. However, the anonymous function captures thecountvariable, which extends its lifetime! The returned function will continue to reference thatcountvariable even aftermakeCounterreturns. - Because
countis a local variable ofmakeCounter, we create a newcountvariable every time we callmakeCounter. That is whypenguinCounter,duckCounter, andlionCounterall count independently: each one captured a different instance of thecountvariable from a different call tomakeCounter. In this way, creating a closure with captures is a lot like creating an object with instance variables!
Example 2a: Bye bye function()
I didn’t point it out in class, but Javascript’s function syntax is just syntactic sugar. We could have also defined all of these functions by using the anonymous function literal syntax (…) => { … } and then assigning them to variables, like this:
let print = console.log;
let thrice = (f) => {
f();
f();
f();
}
let makeCounter = (msg) => {
let count = 0;
return () => {
print(msg + " is now " + count);
count++;
};
}
let penguinCounter = makeCounter("penguin");
let duckCounter = makeCounter("duck");
let lionCounter = makeCounter("lion");
thrice(penguinCounter)
duckCounter()
penguinCounter()
thrice(lionCounter)
thrice(duckCounter)