GithubHelp home page GithubHelp logo

javascript-closures-v-000's Introduction

JavaScript Closures

#Was this a readme?

Objectives

  1. Explain what a closure is
  2. Explain how a closure works
  3. Practice using a closure
  4. Describe use cases for closures in JavaScript

Introduction — The Spy Who Ate Me

Fat Bastard

Let's talk about one of my favorite characters in the Austin Powers movies: Fat Bastard and his gluttonous appetite. If you haven't seen those movies yet, you should do so now. If it makes you feel better, pretend they're required viewing for this course!

In the movie, Fat Bastard wants to eat Mini-Me. Let's make this dream come true for our chubby assassin friend.

What's for dinner?

We'll represent Fat Bastard using a function that takes one argument, the thing he's going to eat:

function fatBastard(meal) {

}

Next, we'll add a way for Fat Bastard to tell us what he's having for dinner, by returning a function. We can then assign the returned function to a variable, and call it when we want to know what type of food we gave Fat Bastard for dinner.

function fatBastard(meal) {
  function whatsForDinner() { // whatsForDinner() is the inner function, a closure
    if (meal === 'Mini-Me') { // whatsForDinner() uses argument provided to the parent function 
      console.log('The wee man is in my belly!');
    } else {
      console.log(`I'm eatin' a bit of ${meal}! Burp.`);
    }
  }

  return whatsForDinner;
}

whatsForDinner() is an inner function of the fatBastard() function, and as such, it has access to all of the variables defined in its parent function (along with any variables in its own scope, if it has any). This means that we can access the meal argument in our whatsForDinner() function. However, meal isn't accessible outside of the fatBastard() function, giving us some semblance of 'private' variables. This is one possible use case for closures.

As you can see, we're not executing the whatsForDinner() function here, we're merely returning it. We can then run the whatsForDinner() function at a later point in time, when we're curious about what exactly is in Fat Bastard's belly.

The reason whatsForDinner() still has access to the variables within its parent function long after the parent function has executed is because whatsForDinner() is a 'closure'.

Fat Bastard in a moment of spiritual clarity.

To prove that Fat Bastard can still let us know what is in his belly after he's eaten it, let's feed him a nice, juicy steak.

const whatsForDinner = fatBastard('Kobe beef');
whatsForDinner(); // prints 'I'm eatin' a bit of Kobe beef! Burp.'

Keep in mind that since we're returning a function as a value, there's no need to use the returned function's name as the same name for our variable. Meaning, this would work too:

const whatsInHisTummy = fatBastard('Mini-Me');
whatsInHisTummy(); // prints 'The wee man is in my belly!'

Gotchas

When a closure is created, its environment is remembered and not discarded. That means that the variables in that scope are shared (just like regular variables), and can also be changed. This is something you need to be aware of. To illustrate, let's expand on what Fat Bastard can do with his meal, by allowing him to digest it:

function fatBastard(meal) {
  function whatsForDinner() { // whatsForDinner() is an inner function, a closure
    if (!meal) { // whatsForDinner() uses argument provided to the parent function 
      console.log('My belly is empty. Woe is me.');
    } else if (meal === 'Mini-Me') {
      console.log('The wee man is in my belly!');
    } else {
      console.log(`I'm eatin' a bit of ${meal}! Burp.`);
    }
  }

  function digest() { // digest() is an inner function, a closure
    meal = undefined; // digest() uses argument provided to the parent function 
  }

  return {
    whatsForDinner,
    digest
  };
}

Keep in mind that we're now returning an object with references to our whatsForDinner() and digest() functions, meaning that now we do need to use the key names to refer to them! Let's serve him another slab of beef, and give him some time to digest it:

const { whatsForDinner, digest } = fatBastard('ribeye');
whatsForDinner(); // prints 'I'm eatin' a bit of ribeye! Burp.'
digest();
whatsForDinner(); // prints 'My belly is empty. Woe is me.'

Note: If you have never seen the syntax above const { whatsForDinner, digest } = fatBastard('ribeye'); don't be alarmed! That's a new ES6 feature called (Object Destructuring)[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring]. Basically, developers frequently found themselves having a large JS object such as a response from an API and they might want to declare several variables where the name of the variable is the same as the name of the key of the object. Example:

var firstName = response.firstName;
var lastName = response.lastName;
var url = response.url;
// ...

ES6 provides a shortcut for doing just this task! What do you think are the values of the variables a, b, and c here:

var obj = {a: 1, b: 2, c: 3};
var {a, b, c} = obj;

Check out the documentation and experiment in the console.

Another thing to watch out for is that closures are the most common source of performance issues and memory leaks. Since the variables that are closed over might still be in use, they're either never or barely picked up by the garbage collection.

Note:

Garbage collection (GC) is basically something that happens in the background to automatically manage the memory used by our application. Stuff that's no longer being used by the program takes up unnecessary memory, and the GC is responsible for cleaning it up.

Garbage collection is something that you generally don't have to worry about, unless you're writing UI code that gets executed a lot (e.g. the people working on Angular, React, ... do keep an eye on performance).

Pirates and passwords

What's the password?

Modularization and abstracting away implementation details is key to writing good, clean code. For example, imagine you're in a back alley. There's a door on the left, with a shady looking character looking out the peephole. As you look at him, he yells out in a raspy voice. "Wot's the password?". He knows the password, but there's no way for us to obtain that information from him. He's certainly not going to tell us. Let's represent our new friend in code:

function raspyDoorGuy() {
  const password = 'yarr'; // password is a local variable created by raspyDoorGuy()

  function givePassword(givenPassword) { // givePassword() is the inner function, a closure
    if (givenPassword === password) { // givePassword() uses variable declared in the parent function 
      console.log('Ye may enter.');
    } else {
      console.log('Begone, landlubber!');
    }
  }

  return {
    givePassword
  };
}

Let's try to access the password variable:

console.log(password); // prints `undefined`

Dang it. Well, it was worth a try. It makes sense though, since password is enclosed within the raspyDoorGuy() function, but it's not returning it. There's no way for us to know what the right password is. Let's try to give him a password, since that's something that our raspyDoorGuy() function does return:

const { givePassword } = raspyDoorGuy();
givePassword('kittens'); // prints 'Begone, landlubber!'

The givePassword() function can still access the password variable defined in the scope of its parent function because givePassword() is a closure. This is how the man behind the door can check if the password we gave is the right one. Unfortunately for us, we still don't know the right password. What if we could bribe him? That would help us out. Since everyone has a set of internal moral beliefs, we can abstract away that functionality. All we care about is if he thinks our bribe is enough.

function raspyDoorGuy() {
  const password = 'yarr'; // password is a local variable created by raspyDoorGuy()

  function givePassword(givenPassword) { // givePassword() is an inner function, a closure
    if (givenPassword === password) { // givePassword() uses variable declared in the parent function 
      console.log('Ye may enter.');
    } else {
      console.log('Begone, landlubber!');
    }
  }

  function willBreakPrinciples(bribeAmount) { // willBreakPrinciples() is the private method
    return bribeAmount >= 50;
  }

  function bribe(amount) { // bribe() is an inner function, a closure
    if (willBreakPrinciples(amount)) { // bribe() uses private method created in the parent function
      return password; // bribe() uses variable declared in the parent function 
    } else {
      console.log("Pssht. That won't work.");
    }
  }

  return {
    givePassword,
    bribe
  };
}

If we take a look at the code above, we see that there are now three functions, but we only return two. This means that the willBreakPrinciples() function is private — it can only be accessed by the two closures (givePassword() and bribe()) that share the same environment. This sort of emulates private methods you see in other programming languages. There's no way for us to know how much a good bribe would be, but if we give him enough money, we'll get the password:

const { bribe } = raspyDoorGuy();
bribe(80); // returns 'yarr'

And now we're in! That's about it for closures. It's important to know how they work and why they're useful, as well as their implications.

Rachel on closures

Resources

View Closures on Learn.co and start learning to code for free.

javascript-closures-v-000's People

Contributors

pletcher avatar crwhitesides avatar thomastuts avatar dkennell avatar jjseymour avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.