4

In my quest to understand closures in the context of JS, I find myself asking why do you even need to use closures? What's so great about having an inner function be able to access the parent function's variables even after the parent function returns? I'm not even sure I asked that question correctly because I don't understand how to use them.

Can someone give a real world example in JS where a closure is more beneficial vs. the alternative, whatever that may be?

Edit: the question Why is closure important for JavaScript? did not answer clearly enough what I want to understand. So, I don't consider this a duplicate.

I'll take a look at all of the answers/resources, and I'll mark an accepted one. Thanks to everyone!

Mark Bubel
  • 141
  • 1
  • 1
  • 4

4 Answers4

16

Javascript is asynchronous by nature. Your code can't wait. Instead, you say, "when this happens, call this function". This means that Javascript code is full of functions. Functions that are passed to other functions, called by them, and sometimes even returned by them to be called later. These functions often want to use some data that their defining scope has available, but their caller (the browser) does not.

function getSize(src, callback){
  var img = new Image();
  img.onload = function(event){
     console.log(event);
     callback(img.width, img.height);
  }
  img.src = src
}

Now, this looks like a normal code. Except it uses the closure scope! img and callback are both closed over by img.onload. Now, imagine you didn't have closures. What are the other options? You might find img in the event object, but definitely not the callback. You need a function that takes two kinds of arguments: those that are set when you define a function, and those that are set when you call it. Or, you can tell the system everything you need. Or, you need a native currying mechanism*. You don't want to tell foreign code everything you know just because you need to react to its events, so let's assume that such mechanism exists. ES5 introduces bind:

* currying is a technique where a new function is created by binding arguments to an existing function in advance, and sending the arguments as the remaining arguments to the original function. The original function's return value is then returned

function getSize(src, callback){
  var img = new Image();
  img.onload = function(callback, img, event){
     callback(img.width, img.height);
  }.bind(null, callback, img); //not needed if you have closures
  img.src = src
}

What are the arguments to bind? null sets the this context. Our hypothetical curry method would not. Then, callback and img. These must be in the same order as the arguments to img.onload. This is acceptable every now and then, but not if you have to curry uphill in both directions. More importantly, it is acceptable if you have three arguments, but not if you have thirty.

Now, for the funny part: The global scope is technically a closure scope too. But, even if it wasn't, you still don't want to define your module's utility functions in the global scope (namespacing issues). Functions are stored in variables!

getSize = function(Image, src, callback){
  var img = new Image();
  img.onload = function(callback, img, event){
     callback(img.width, img.height);
  }.bind(null, callback, img); //not needed if you have closures
  img.src = src
}.bind(null, Image) //not needed if you have closures

Now, assume you want to transform the image coordinates for some reason, and that transformation is dependent on the source. Where do we need to add transform and src?

getSize = function(Image, transform, src, callback){ //here
  var img = new Image();
  img.onload = function(transform, src, callback, img, event){ //here
     var coord = transform(img.width, img.height, src); //used here
     callback(coord.x, coord.y);
  }.bind(null, transform, src, callback, img); //here
  img.src = src;
}.bind(null, transform, Image); //here

Can you spot the bug? Yep, the outer bind arguments are in the wrong order.

You can circumvent this by holding all parameters in a global object. Of course, you don't want to modify the caller's scope, so you define your own scope whenever you want to pass a few extra arguments. With a few good conventions, this may be reasonably bug-free:

var scope = {Image: Image, ...}

getSize = function(scope, src, callback){
  var img = new scope.Image();
  var scope = {scope: scope, src: src, callback: callback, img:img};
  img.onload = function(scope, event){
     var coord = scope.transform(scope.img.width, scope.img.height, scope.src);
     callback(coord.x, coord.y);
  }.bind(null, scope);
  img.src = src;
}.bind(null, scope)

Can you spot the bug now? scope.transform is undefined. We need scope.scope.transformThere's another one. Can you spot it too? Now this is getting ridiculous, even if we used one-letter variable for scope here. Maybe we could utilise the prototype chain:

var scope = {Image: Image, ...}

getSize = function(scope, src, callback){
  var img = new scope.Image();
  var scope = scope.Object.create(scope)
  scope.src = src;
  scope.callback = callback;
  scope.img = img;
  img.onload = function(scope, event){
     var coord = scope.transform(scope.img.width, scope.img.height, scope.src);
     callback(coord.x, coord.y);
  }.bind(null, scope);
  img.src = src;
}.bind(null, scope)

of course, we could define our local variables directly in the scope so that we don't have to declare them twice if some inner function might use it. Unfortunately, there's no such trick for the arguments. There is the arguments variable, but using it for our purpose is kinda messy (scope.getSizeArgs[0]; quick - which one is it?). However, so far, to circumvent the lack of closures we have:

  1. for each scope, created a special scope variable, with the parent's scope as its prototype.
  2. added all arguments to it
  3. used it for all local variables that might be needed in an inner function.

Of course, this is getting ridiculous. Maybe if the language somehow knew that if we use an undeclared variable, it should search for it in the defining scope, and even create such scope object for us when needed so that the variables can outlive their defining functions? Well, it does.

function getSize(src, callback){
  var img = new Image();
  img.onload = function(event);
     console.log(event);
     var coords = transform(img.width, img.height);
     callback(coords.x, coords.y);
  }
  img.src = src;
}

Also, this explicit passing has another disadvantage: whenever you write to the scope, you always write to a local variable. You cannot modify a variable from an inner function. Sure, you could use one-element arrays or a special Reference<T> type, but this smells of a non-awesome language. With closures, you can do this directly. Do you actually need this?

var width = window.clientWidth;
window.addEventListener('resize', function(){width = window.clientWidth});
setInterval(function(){
  ...

You might say that sometimes you want the caller's scope as the alternative would be to have 30 arguments. But, the caller does not want you to see their local scope, and they don't want to have to avoid variable names you might one day consider significant. You can still pass objects around, but you don't call them scopes. Some may say "data transfer object"; I prefer the term "named arguments"

$.ajax({
  url: "http://www.example.com/cors-api/echo.json",
  dataType: "json",
  success: function(response){
    ..

Now, for the classic example of closure scoping, complete with an immediately invoked function expression to limit the scope of the static variable nextId:

var getId = (function(){
  var nextId = 0;
  return function getId(){ //named for clarity
    return nextId++;
  }
})()

getId() //0
getId() //1
getId() //2
John Dvorak
  • 269
  • 2
  • 8
  • 10
    Don't let Node.JS fool you, javascript is not "asynchronous by nature", it just has higher order functions natively which allows for easy implementation of CPS, that doesn't mean it's "naturally all asynchronous", if you want to see "asynchronous by nature" look at the actor model, some languages *are* asynchronous by nature (erlang, and various esoterics), javascript is *not*. – Jimmy Hoffa Jul 20 '13 at 01:35
5

The really useful thing about closures is that they let you stick arbitrary data in a callback, independent of the function signature.

A good real-world example of where it comes in handy is animation. JS isn't my strongest language, so I won't even try to write code for this, but here's the general idea:

Let's say you've got a function called Timer that takes two parameters, an interval and a callback. (I know that the DOM has something like this, that's used for implementing animations in JavaScript.) And after the interval is up, it calls your callback like so: CallbackFunction();

See the obvious problem? It doesn't give you back data that might be needed inside your callback function. Some callback systems let you pass an arbitrary object in when you set it up, and then when the callback is called, it passes that object back as context. But then things get hairy if you need two pieces of data inside the callback, and so on...

The solution is to pass in a closure, and have it close around all the data you need. Then it can be called as a callback at a later time (after the timer has run, for example,) and it can keep all of the data (enclosed variables) that it needs in order to perform its work.

Mason Wheeler
  • 82,151
  • 24
  • 234
  • 309
  • 3
    Async IO is another common example. Eg, when I get this piece of data loaded, being able to process it *in the same environment* as when you made the original non-blocking call is essential. – daniel gratzer Jul 03 '13 at 04:41
  • 2
    Wouldn't an object containing the needed data achieve the same goal? – Giorgio Jul 03 '13 at 05:06
  • 3
    @Giorgio In fact, that's how closures in most languages are actually implemented! That doesn't change the fact that not having to pass such an object around explicitly makes a *huge* difference to code clarity, not to mention making refactoring much more uniform because you don't have to worry about how you're going to get access to the enclosed state. – Ptharien's Flame Jul 03 '13 at 05:34
0

I would like to give one scene for closures:

 function f1() {
      var n=999;

      setter=function() { n += 1 }
      function getter() {
           alert(n);
      }

      return f2;
 }

In this function, the variable n becomes private and it has getter and setter methods (like Java).

Aschratt
  • 254
  • 1
  • 14
0

This example is pretty similar to gravityplusplus'. Asynchronous code is the most common case, as Mason and Jan pointed out; but it's also useful for localized modules (module meaning "a block of code that does its own individual task). Here's some code all in the same scope (meaning, this is how NOT to do it).

counter = 0;

function incrementCounter() {
    counter += 1;
    return counter;
}

// Define Ryu's attack functions here
function lightPunch() {
    //TODO
}
function counter() {
    //TODO
}

function getCounterValue() {
    return counter;
}

What is getCounterValue() going to return? I'm not even sure; I think it's going to be the function that I declared, not the number. If these were seperated into closures in some way (and there are several ways to do so; gravity listed one good example) then there's no chance of variable names getting mixed up. And in a large, corporate project with pages consisting of 50+ JS files, there are going to be LOTS of variable names.

Katana314
  • 862
  • 1
  • 5
  • 18