Включает ли это закрытие JavaScript? - PullRequest
10 голосов
/ 15 апреля 2010

Пытаясь выучить замыкания в 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 . Обратите внимание, что в этом примере прокрутка страницы не совсем работает (кажется, что она не доходит до конца текста), но это нормально :). Бонусные баллы для всех, кто может сказать мне, почему он не прокручивается вниз. 1031 *

Ответы [ 10 ]

12 голосов
/ 15 апреля 2010

Каждый раз, когда вы определяете функцию, эта функция будет «заключать» цепочку области видимости, в которой она была определена. Когда эта функция выполняется, эта цепочка области видимости становится доступной в ее теле.Когда функция определена внутри другой функции, цепочка областей действия включает в себя:

  • Локальные переменные родительской функции
  • Аргументы родительской функции
  • Родительская функция родительской функциилокальные переменные и аргументы и т. д.
  • Глобальные переменные

Реальное значение замыканий - возвращать внутреннюю функцию, которая захватила вложенную переменную, которая не будет изменяться,вот так:

        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"
3 голосов
/ 15 апреля 2010

Взгляните на серию блогов Дмитрия А. Сошникова о ECMA-262 («javascript»).

Пост 6 http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/ посвящен замыканиям, и если вы прочитаете предыдущие посты, вы должны хорошо понять их.

Короткая история; В объявлениях функций или выражениях функций, размещенных внутри функции (объявление или выражение), область родительских функций будет добавлена ​​в цепочку областей действия. Для этого он имеет доступ ко всем переменным, содержащимся в каждой функции в цепочке областей видимости (оканчивающейся на global scope). Но пока мы не говорим о закрытии.

Понятие 'замыкания' появляется, когда вы создаете ссылку на такую ​​содержащуюся функцию вне цепочки областей действия, либо возвращая ссылку на функцию, либо добавляя ссылка на него как на свойство объекта выше в цепочке областей действия. [чтобы вы, вызывая функцию (по этой ссылке), могли изменять переменные hidden внутри недоступной области видимости.]

Эта ссылка также приведет к тому, что все переменные, содержащиеся в указанной области видимости, будут иметь хотя бы одну ссылку (из каждой области видимости), что означает, что ни одна из переменных, присутствующих в цепи области видимости, не может быть собрана сборщиком мусора. Это так называемая утечка памяти (если только вы не планируете использовать переменные, которые есть!).

2 голосов
/ 15 апреля 2010

Я не думаю, что вы используете какие-либо замыкания, потому что вы не возвращаете внутренние функции из вашей внешней функции, поэтому эти переменные всегда будут в области видимости при вызове. Другими словами, init(), scrollDown() и scrollUp() должны вызываться из pageScroll(). Если pageScroll() вернул какую-либо из трех своих внутренних функций, а затем вы вызывали их вне pageScroll(), использование переменных $page, $next, $prev и т. Д. Было бы замыканием. Я думаю.

1 голос
/ 15 апреля 2010

Да, в примере кода есть несколько замыканий. Путаница может возникнуть из-за слишком строгого требования к замыканиям из вашего определения. В то время как замыкание имеет доступ к своим связанным свободным переменным в течение времени жизни замыкания, для замыкания необязательно не требуется продлевать время жизни этих переменных за пределы их обычной среды для функция считается закрытием.

Например, допустим, у нас есть функция findAllPeople, которая находит в списке всех людей, чье имя указано в качестве параметра функции. Мы могли бы создать внутреннюю функцию, которая принимает человека и проверяет его имя на соответствие предоставленному имени. Эта внутренняя функция будет иметь две свободные переменные - человека и имя для проверки. Имя для проверки связано с параметром, указанным для findAllPeople. Следовательно, внутренняя функция закрыта по этой свободной переменной. Тот факт, что внутренняя функция создается, используется и отбрасывается во время выполнения findAllPeople и не используется где-либо за пределами findAllPeople, не имеет никакого отношения к тому факту, что внутренняя функция является замыканием.

1 голос
/ 15 апреля 2010

Да, у вас есть замыкания, посмотрите на этот быстрый пример

         <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 (), имеет ли это смысл.

1 голос
/ 15 апреля 2010

Вы присоединяете к событию onclick элемента #page-next функцию scrollDown, которая хранит ссылку на переменную canScroll вашего объекта pageScroll.Так что да, вы используете замыкания.

На самом деле, в вашем примере есть 4 замыкания: функция, отправленная на $next.click, отправка на $prev.click, отправка на $page.animateтот, который отправлен на $(document).ready.

1 голос
/ 15 апреля 2010

Я не эксперт по Javascript, но из того, что я здесь прочитал, да, у вас есть замыкания.

Обратите внимание, что все внутренние функции, например, используют переменную $ page. init назначает ему, а позже функции прокрутки обновляют его. Таким образом, в принципе внутренние функции являются замыканиями внешней функции. Внешняя функция передает init в document.ready, поэтому некоторым способом Javascript возвращает закрытие.

Тем не менее, я также должен сказать, что этот код является очень плохим примером функционального программирования, поскольку чистые функции не должны иметь побочных эффектов (например, присваивание общей переменной). Тем не менее, у него есть замыкания.

0 голосов
/ 15 апреля 2010

очевидно, да ... когда вы вложили функцию в другую функцию, у вас есть замыкание .. вот и все

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

 func(); //alert 'Hi There', eventhough the outter function has exit
0 голосов
/ 15 апреля 2010

Какой день, чтобы не принести мою книгу Javascript: The Good Parts на работу ...

Я полагаю, что короткий ответ «Нет» из-за этой части определения: «даже после того, как родительская функция вернулась» Но я не уверен на 100%.

Это может помочь, хотя.

ролик YouTube Javascript: The Good Parts

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

0 голосов
/ 15 апреля 2010

Да, конечно, есть замыкания. Например, вы используете $page, $next и т. Д., Объявленные вне области действия, например init (которая их использует).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...