0

I was reading an article about requestAnimationFrame here, and I realized that I was having trouble following the scope and persistence of a variable. Slightly modified code below:

(function() {

    //Note: lastTime is defined up here.
    var lastTime = 0;

    var vendors = ['ms', 'moz', 'webkit', 'o'];

    //Okay, so they're trying to set window.rAF to the appropriate thing based on browser
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelRequestAnimationFrame = window[vendors[x]+
          'CancelRequestAnimationFrame'];
    }

    //...and if that doesn't work, turn it into a setTimeout
    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {

            //I guess this is a way to make sure that the wait time
            //between renders is consistent

            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
              timeToCall);

            lastTime = currTime + timeToCall;

            //Wait. They just assigned lastTime a value.
            //Why is this going to persist between calls to window.rAF?
            //The variable lastTime was defined inside this function, not in the window.

            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}())

My guess is it has something to do with the function being placed inside parentheses, but how? What does this accomplish and what other effects can I expect with this coding style? Is this something I should start using more regularly, and if so, when?

Rstevoa
  • 271
  • 4
  • 17

1 Answers1

3

The variable lastTime here is captured via closure. This means it is kept alive beyond the scope of the function it is defined in.

A closure gets created anytime an anonymous function body references a variable outside of its own scope. Closures are extremely useful in JavaScript, as they allow you to maintain state without exposing it all globally.

To give a simplified example,

function foo() {
    var count = 0;

    setInterval(function bar() {
        console.log(count++);
    }, 100);
}

By closing over the count variable here, I can use it in setInterval without exposing count to the global scope as I would otherwise have to do.

MgSam
  • 12,139
  • 19
  • 64
  • 95
  • Does this mean that window.requestAnimationFrame now has scope including the anonymous function? The thing I had in mind was as follows: (1) Global generally means inside the `window` object (2) This anonymous function exists in its own space because its variables cannot be accessed globally (3) By assigning the function to window.requestAnimationFrame, the function now exists in the `window` object, away from the anonymous function, and so it can't access the anonymous function's variables – Rstevoa Sep 20 '13 at 18:49
  • Yes, all of these points are generally correct. For 3, it can only access the variables it has closed over. Variables are closed over when the callback function is defined; so your callback function couldn't, say, later on (via `eval`) try to grab another one of the anonymous function's variables, as it's already too late. – MgSam Sep 20 '13 at 19:47