Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does this incorporate JavaScript closures?

In trying to learn JavaScript closures, I've confused myself a bit.

From what I've gathered over the web, a closure is...

Declaring a function within another function, and that inner function has access to its parent function's variables, even after that parent function has returned.

Here is a small sample of script from a recent project. It allows text in a div to be scrolled up and down by buttons.

var pageScroll = (function() {

    var $page,
        $next,
        $prev,
        canScroll = true,
        textHeight,
        scrollHeight;

    var init = function() {

        $page = $('#secondary-page');

        // reset text 
        $page.scrollTop(0);

        textHeight = $page.outerHeight();

        scrollHeight = $page.attr('scrollHeight');

        if (textHeight === scrollHeight) { // not enough text to scroll

            return false;    

        };

        $page.after('<div id="page-controls"><button id="page-prev">prev</button><button id="page-next">next</button></div>');

        $next = $('#page-next');

        $prev = $('#page-prev');

        $prev.hide();

        $next.click(scrollDown);

        $prev.click(scrollUp);

    };

    var scrollDown = function() {

        if ( ! canScroll) return;

        canScroll = false;

        var scrollTop = $page.scrollTop();

        $prev.fadeIn(500);

        if (scrollTop == textHeight) { // can we scroll any lower?

            $next.fadeOut(500);

        }

        $page.animate({ scrollTop: '+=' + textHeight + 'px'}, 500, function() {

            canScroll = true;

        });    

    };

    var scrollUp = function() {

        $next.fadeIn(500);

        $prev.fadeOut(500);

        $page.animate({ scrollTop: 0}, 500);    


    };

    $(document).ready(init);

}());

Does this example use closures? I know it has functions within functions, but is there a case where the outer variables being preserved is being used?

Am I using them without knowing it?

Thanks

Update

Would this make a closure if I placed this beneath the $(document).ready(init); statement?

return {
    scrollDown: scrollDown
};

Could it then be, if I wanted to make the text scroll down from anywhere else in JavaScript, I could do

pageScroll.scrollDown();

Another Update

Wow, looks like that worked! Here is the code on JSbin. Note the page scroller doesn't exactly work in this example (it doesn't seem to go to the bottom of the text), but that's OK :) Bonus points for anyone that can tell me why it's not scrolling to the bottom.

like image 425
alex Avatar asked Apr 15 '10 14:04

alex


People also ask

Are there closures in JavaScript?

In JavaScript, closures are created every time a function is created, at function creation time.

How are closures implemented in JavaScript?

A closure is created every time an enclosing outer function is called. In other words, the inner function does not need to return for a closure to be created. The scope of a closure in JavaScript is lexical, meaning it's defined statically by its location within the source code.

What are the examples of closures in JavaScript?

Let's have a look at another example. In the above program, the calculate() function takes a single argument x and returns the function definition of the multiply() function. The multiply() function takes a single argument y and returns x * y . Both multiply3 and multiply4 are closures.

Are JavaScript closures important?

Closures are important because they control what is and isn't in scope in a particular function, along with which variables are shared between sibling functions in the same containing scope.


1 Answers

Any time you define a function, that function will "enclose" the scope chain it was defined in. When that function is executed that scope chain is available in its body. When a function is defined inside another function, the scope chain includes:

  • The parent function's locally defined variables
  • The parent function's arguments
  • The parent function's parent function's local vars and arguments, etc..
  • Global variables

The real value of closures is to return an inner function that has captured an enclosed variable that isn't going to change, like so:

        function test (value){
            return function(){
                alert(value); //using enclosed argument
            }
        }

        var t1 = test("first");
        var t2 = test("second");
        t1();//alerts "first"
        t2();//alerts "second"

One danger of closures is that they enclose a reference to the enclosed properties, not a snapshot of their values, so watch out for creating an enclosing function inside of a loop like so:

function makeArray (){
    var result=[]
    for (var i =0; i < 3; ++i){
        result.push(function(){
            alert("function #" +i); //using enclosed i
        })
    }
    return result;
}

var fnArray =makeArray();
fnArray[0]();//alerts "function #3"
fnArray[1]();//alerts "function #3"
fnArray[2]();//alerts "function #3"

In a loop you need to find a way to make your enclosed variables unchanging. Here is an example of using a nested closure to copy the re-used counter to a single-use argument:

function makeArray (){
    var result=[]
    var wrapFunction=function (counter){
        return  function(){
            alert("function #" +counter); //using enclosed counter
        }
    }
    for (var i =0; i < 3; ++i){
        result.push(wrapFunction(i))
    }
    return result;
}

var fnArray =makeArray();
fnArray[0]();//alerts "function #0"
fnArray[1]();//alerts "function #1"
fnArray[2]();//alerts "function #2"
like image 188
Mark Porter Avatar answered Oct 13 '22 00:10

Mark Porter