这是否包含JavaScript闭包?

时间:2010-04-15 14:25:13

标签: javascript jquery closures

在尝试学习JavaScript闭包时,我有点困惑。

从我在网上收集的内容来看,关闭是......

在另一个函数中声明一个函数,并且该内部函数可以访问其父函数的变量,即使该父函数已经返回

以下是最近项目中的一小部分脚本示例。它允许div中的文本通过按钮上下滚动。

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);

}());

此示例是否使用闭包?我知道它在函数中有函数,但有没有使用保留的外部变量?

我是否在不知情的情况下使用它们?

由于

更新

如果我把它放在$(document).ready(init);语句下面,这会成为一个闭包吗?

return {
    scrollDown: scrollDown
};

那么,如果我想让文本从JavaScript中的任何其他地方向下滚动,我可以做到

pageScroll.scrollDown();

另一次更新

哇,看起来很有效!以下是JSbin上的代码。请注意,页面滚动条在这个示例中并不完全有效(它似乎没有出现在文本的底部),但是没关系:)任何可以告诉我它为什么不滚动到底部的人的奖励积分。 / p>

10 个答案:

答案 0 :(得分:12)

每当你定义一个函数时,该函数将“包围”它所定义的范围链。当执行该函数时,范围链在其体内可用。当在另一个函数内定义函数时,作用域链包括:

  • 父函数的本地定义变量
  • 父函数的参数
  • 父函数的父函数的本地变量和参数等。
  • 全局变量

闭包的真正价值在于返回一个内部函数,该函数捕获了一个不会改变的封闭变量,如下所示:

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

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

闭包的一个危险是它们将引用包含在封闭的属性中,而不是它们的值的快照,因此请注意在循环内创建一个封闭函数,如下所示:

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"

在循环中,您需要找到一种方法来使您的封闭变量保持不变。下面是使用嵌套闭包将重用计数器复制到单次使用参数的示例:

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"

答案 1 :(得分:3)

看看Dmitry A. Soshnikov关于ECMA-262('javascript')的博客系列。

Post 6 http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/是关于闭包的,如果您阅读之前的帖子,您应该对它有很好的把握。

短篇小说是;函数声明或放置在函数(声明或表达式)中的函数表达式将父函数作用域添加到其作用域链中。因此,它可以访问范围链中每个函数中包含的所有变量(以全局范围结束)。但到目前为止,我们并没有谈论关闭。

'闭包'的概念在您通过返回对函数的引用创建对此类包含函数 范围链的引用时出现,或者通过在范围链中更高的对象上添加对它的引用作为属性。 [这样你通过调用函数(通过该引用)可以修改不可访问范围内的变量 hidden 。]

此引用还将导致所述scopechain中包含的所有变量至少具有一个引用(来自每个范围),这意味着范围链中存在的所有变量都不能被垃圾收集。这是一个所谓的内存泄漏(除非你实际上计划使用变量!)。

答案 2 :(得分:2)

我认为你没有使用任何闭包,因为你没有从你的外部函数返回内部函数,所以这些变量在被调用时总是在范围内。换言之,必须从init()内调用scrollDown()scrollUp()pageScroll()。如果pageScroll()返回其三个内部函数中的任何一个,然后您在pageScroll()之外调用它们,则使用$page$next$prev等变量将是闭包。我想。

答案 3 :(得分:1)

我不是Javascript的专家,但是从我在这里读到的,是的,你有封闭。

请注意,例如,所有内部函数都共享变量$ page。 init分配给它,稍后滚动函数更新它。所以原则上内部函数是外部函数的闭包。外部函数将init传递给document.ready,因此在一些Javascript-ish方式中,它返回一个闭包。

也就是说,我还要说这段代码是函数式编程的一个非常糟糕的例子,因为纯函数不应该有副作用(比如分配给共享变量)。然而,它有封闭。

答案 4 :(得分:1)

您附加了onclick元素#page-next元素的一个函数scrollDown,它保留了对象canScroll的变量pageScroll的引用。所以是的,你正在使用闭包。

事实上,您的示例中有4个闭包:发送到$next.click的函数,发送到$prev.click的函数,发送到$page.animate的函数和发送到$(document).ready的函数{1}}。

答案 5 :(得分:1)

是的,你确实有一个闭包,看看这个简单的例子

         <script type="text/javascript">
    var t1=null;
    function test(){
        alert('Test');
        var x  = 10;
        t1 =  function(){
            alert('x='+ x);
        };  
    };
    function test2(){
        t1();
    }
    </script>
    <a href="javascript:test();">Test me 1 </a>  

<a href="javascript:test2();">Test me 2</a>

在这种情况下,闭包可以访问其父变量,我们的函数't1'访问私有声明的var x;正如你所说'内部函数可以访问其父函数的变量,即使在父函数返回后也是如此。'当你单击'Test me 2'时,你会得到一个x = 10的警报,即使那些函数test()已经返回了,这是否有意义。

答案 6 :(得分:1)

是的,示例代码中有许多闭包。混淆可能来自对您的定义中的闭包要求过于严格的要求。虽然闭包 可以在闭包的生命周期内访问其绑定的自由变量,但闭包不是必需,以延长这些变量的生命周期,使其超出正常环境将该函数视为闭包。

例如,假设我们有一个函数findAllPeople,它在列表中找到所有具有名字的人,该名字作为函数的参数提供。我们可以创建一个内部函数,它接受一个人并根据提供的名称测试他们的名字。该内部函数将有两个自由变量 - 即要测试的人和名称。要测试的名称绑定到提供给findAllPeople的参数。因此,内部函数关闭超过该自由变量。内部函数在执行findAllPeople时被创建,使用和丢弃的事实并没有在findAllPeople之外的任何地方使用,这与内部函数是闭包这一事实无关。 / p>

答案 7 :(得分:0)

是的,确定有关闭。例如,您使用在外部作用域中声明的$page, $next等,例如init(使用它们)。

答案 8 :(得分:0)

今天不要带我的Javascript:Good Parts一书工作......

我认为简短的答案是“否”,因为定义的这一部分:“即使在父函数返回后也是如此。”但我不是百分百肯定。

但这可能会有所帮助。

Javascript的好剪辑:好的部分

http://www.youtube.com/watch?v=hQVTIJBZook&feature=player_embedded

答案 9 :(得分:0)

显然是的...... 当你在另一个函数中嵌套一个函数时,你有一个闭包.. 多数民众赞成

var func = (function(){
             var txt = 'Hi There!'
             return function(){
               alert(txt);
             }
           })();

 func(); //alert 'Hi There', eventhough the outter function has exit