What are the use cases for closures/callback funct

2019-01-30 02:10发布

问题:

I was listening to Crockford's talk on JavaScript closures and am convinced of the benefit of information hiding, but I do not have a firm understanding of when to use callback functions.

It is mostly a true statement that a person could accomplish the same functionality with or without callbacks.

As someone who is writing code, what heuristics or cues should I keep in mind when determining when to use callbacks/closures?

I am not looking for the blanket statement 'Closures make more secure code', rather a list of practical examples or rules of thumb for when callbacks are the right idea.

Crockford's Presentation: http://www.yuiblog.com/blog/2010/04/08/video-crockonjs-5/

回答1:

Firstly:

  • Callback: A function passed as an argument to another function, usually to be called as a result of an event occurring.
  • Closure: A retained scope. I.e. the concept that when you declare a function within another function, the outer function's scope is accessible within the inner function.

Callbacks can also be closures, but are not always.

This is a callback:

someProcess(myCallback);

function myCallback() {
    alert('Done...');
}

function someProcess(callback) {
    // does stuff...
    // ...
    callback();
}

A closure:

function foo(msg) {

    function bar() {
        // I can access foo's scope
        // (i.e. bar can access everything that foo can access)
        alert(msg);
    }

    return bar;

}

foo('hello')(); // alerts "hello"

One common usage of closures is to provide information-hiding, which is helpful in bringing some kind of encapsulation to the language. Have a look at the module pattern to see this in action.

Another common usage is when binding event handlers to elements. E.g.

var myElements = [ /* DOM Collection */ ];

for (var i = 0; i < 100; ++i) {
    myElements[i].onclick = function() {
        alert( 'You clicked on: ' + i );
    };
}

That wouldn't work. By the time the element is clicked, the variable i is 99. To make this work properly we cold use a closure to capture the value of i:

function getHandler(n) {
    return function() {
        alert( 'You clicked on: ' + n );
    };
}

for (var i = 0; i < 100; ++i) {
    myElements[i].onclick = getHandler(i);
}


回答2:

Let's say you want a function that you can use to return a unique "id" value to use when you create new DOM elements. Now, in something like Java, you could create a class with an internal private counter, and then have a method that appends the counter to some prefix string. Well, in Javascript:

var getId = (function() {
  var counter = 0;
  return function() {
    return "prefix" + counter++;
  };
})();

Now the variable "getId" is bound to a function that's created by another function, and created in such a way that it has a persistent variable to use between invocations. Similarly, if I wanted to have a family of "getId" functions (say, one for each type of DOM element I might add), I could do this:

var getIdFunc = function(prefix) {
  var counter = 0;
  return function() {
    return prefix + counter++;
  };
};
var getId = {
  'div': getIdFunc('div'),
  'span': getIdFunc('span'),
  'dl': getIdFunc('dl'),
  // ...
};

Now I can call getId.div() to get a new "id" value for a new <div>. The function was created by calling a function that provides two values stashed away in a closure: the prefix string (passed in as an argument) and the counter (a var declared in the closure scope).

Once you get used to it, the facility is so flexible and useful that you'll feel pain at moving back to an environment without it.

Oh, and here's a tip to help keep you off StackOverflow should you try this out: it's an issue that pops up all the time:

for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = function() {
    alert("hello from element " + i);
  };
}

What's the problem here? Well, that "i" variable that's referenced by that function is the "i" from the scope in which that loop runs. That variable, you'll note, gets incremented through the loop (duhh, right?). Well, every single one of those little functions created and assigned as event handlers will share that same, single variable "i" in the closure scope. Oops! The solution is to do something like this:

for (var i = 0; i < 10; ++i) {
  var id = "foo" + i;
  var element = document.getElementById(id);
  element.onclick = (function(iCopy) {
    return function() {
      alert("hello from element " + iCopy);
    };
  })(i);
}

We make a copy of the outer "i" into a closure scope of its own, so now each event handler has its own!

To summarize: the technique of leveraging closures comes up all the freaking time once you get used to it. It's not a free ticket into a new wonderland of error-free programming; don't get me wrong. It is, however, a very useful and flexible paradigm.



回答3:

This writeup from Mozilla may answer why use closures and when

Also, see this set of examples (especially "What can be done with Closures?" section that has the following exmples):

  • Example 1: setTimeout with Function References
  • Example 2: Associating Functions with Object Instance Methods
  • Example 3: Encapsulating Related Functionality

I have e feeling that this can be traced to Crockford, but the classic use of closures is to emulate private instance or static variables (which JavaScipt lacks)