На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: ElcnU, ANDLL, fatalist
  
> Диалоги о JS, Новичок vs Гений
    Новичок: "Что такое замыкание?"
    Гений: "Замыкание это скрытая область с переменными на которую может ссылаться функция"

    Новичок: :wacko:
    Новичок: "Можно пример?"
    Гений: "Легко! :D"
    ExpandedWrap disabled
      function add(arg1) {
        return function (arg2) {
          return arg1 + arg2;
        };
      }
       
      var addFive = add(5);
      var result = addFive(10);
      // 15

    Новичок: "Пардон, а где тут замыкание? Это же называется частичное применения (partial application), читай карринг (carring)."
    Гений: "Да это пример карринга, но для его реализации используется замыкания. Посмотри на параметр arg1 видишь на него ссылается анонимная функция, когда выполняется операция сложения?"
    Новичок: "Да маста."
    Гений: "Маста :lool:"
    Гений: "Ок, так вот этот параметр сохраняется в так называемой замкнутой области, которая сохраняется и не исчезает, когда функция add делает возврат анонимной функции."
    Гений: "Ссылка на эту замкнутую область сохраняется во внутреннем свойстве [[Closure]] возвращенной функции. Так что используя эту ссылку можно потом обратиться к параметру arg1, что делается в операторе return arg1+arg2."
    Гений: "Небольшое уточнение свойство [[Closure]], сохраняется как элемент еще одного внутреннего свойства [[Scopes]] (область видимости)."
    Новичок: "Все так сложно маста :wacko:"
    Гений: "Ну это поначалу, потом все встанет на свои места :D"

    ПС. В данной серии `Диалоги о JS` я и мой воображаемый ученик будут юзать ECMAScript 3 поскольку ИМХО он лучше всего подходит для обучения JS. Кроме того диалоги будут носить не последовательный характер, т.е. темы диалогов будут идти не по нарастающей сложности JS, и иметь рандомный подход. Все будет зависеть от того что на меня найдет :D
    Да и Новичок это не новичок в JS, это скорее образ программиста имеющего вопросы по JS.
    Сообщение отредактировано: Cfon -
      Новичок: "Маста, я долго думал про замыкания и у меня возник вопрос"
      Гений: "Жги :D"
      Новичок: "Как получить доступ к замкнутым данным?"
      Гений: "Хороший вопрос! Для этого надо вернуть объект. Попробуй сам решить :D."
      Новичок: "Ок :huh:"
        Новичок: "Маста вот что у меня получилось"
        ExpandedWrap disabled
          function getCounter() {
            var i = 1;
            return {
              count: function() {
                return i++;
              },
              reset: function() {
                i = 1;
              }
            };
          }
           
          var counter = getCounter();
          counter.count(); // 1
          counter.count(); // 2
          counter.reset(); // 1


        Гений: "Новичок ты уже не новичок однако! :D"
        Гений: "Объясни что тут происходит."
        Новичок: "Значит так функция getCounter() возвращает объект в котором определены две функции. Функция count() возвращает значение переменной i, затем увеличивает на 1, а функция reset() восстанавливает значение i в 1. Здесь переменная i определена в замкнутой области и не доступна извне, кроме как через эти функции. Это и есть замыкание маста?"
        Гений: "Во истину так новичок! :D"
        Гений: "Позволь мне немного изменить твой код."
        Новичок: "Конечно маста."
        Гений: "Вспомни в JS функции являются также объектами, следовательно мы можем возвращать саму функцию предварительно присоединив к ней еще одну."
        ExpandedWrap disabled
          function getCounter() {
            var i = 1;
            var count = function() {
              return i++;
            }
            count.reset = function () {
              i = 1;
            };
            
            return count;
          }
           
          var counter = getCounter();
          counter(); // 1
          counter(); // 2
          counter.reset(); // 1

        Новичок: "Маста вы гений! :blink:"
        Гений: "Ну как бы так :lool:"
        Сообщение отредактировано: Cfon -
          Цитата Cfon @
          "Пардон, а где тут замыкание? Это же называется частичное применения (partial application), читай карринг (carring)."

          Открою секрет, что там нет ни замыкания, ни каррирования :whistle:

          Замыкание - это сохранение внешнего контекста внутри внутренней функции, читай как проброс данных в анонимочку. А каррирование - это процесс преобразования функции от N аргументов до композиции функций от N-1 аргументов (хотя "-1" не обязательно). Частичное применение примерно тоже самое, но в произвольном порядке и прочими задротскими шнягами.

          У тебя и Гений и Новичок никчёмные короч. Давай ещё раз попробуем :whistle:

          Добавлено
          А, да, вру, у тебя вложенная функция в первом примере является замыканием. Ок :D
          Сообщение отредактировано: Serafim -
          user posted image
            Цитата Serafim @
            Открою секрет, что там нет ни замыкания, ни каррирования :whistle:

            Новичок: "Маста что он несет? :huh:"
            Гений: "Не слушай его он сам не ведает что несет :D"
            Новичок: "Ок"
              Новичок: "Маста я прочел много книжек по JS про замыкания, но никак не могу понять каким-образом замыкания связаны с функциями обратного вызова, читай колбэк" :blink:
              Гений: "Хороший вопрос! Сразу видно ты уже не новичок раз прочел много книжек :D"
              Новичок: :blush:
              Гений: "Ок сейчас налью себе чашку кофа и расскажу"
              Гений: "Кстати а какие книжки ты читал?"
              Новичок: "Разные..." :scratch:
              Новичок: "Например `Изучаем программирование на JavaScript` Фримена и Робсон от 2014 года"
              Гений: "О! Хорошая книжка! Это моя первая книжка по JS" :D
              Гений: "Она как раз хорошо подходит для новичков"
              Гений: "Ну а что-нибудь более серьезное читал?"
              Новичок: "Конечно! Ну например `JavaScript подробное руководство` Фленагана"
              Гений: "Оцени ее"
              Новичок: "Ну книжка ничего себе, но изложение материала сухое, она более подходит для тех кто уже раньше программировал на других языках, или уже имеет начальные знания JS"
              Гений: "Вернемся к нашим баранам :jokingly:"
              Гений: "Замыкания в колбэках образуются, если они юзаются в асинхронных операциях"
              Новичок: "Эмм... например? :huh:"
              Гений: "Например обработка сетевых запросов, назначение обработчиков на события, ну или передача колбэка в функцию setTimeout"
              ExpandedWrap disabled
                function displayMessage(msg, delay) {
                  setTimeout(function () {
                    console.log(msg);
                  }, delay);
                }
                 
                displayMessage('Hello', 1000);
                // Hello

              Гений: "Сможешь объяснить где тут замыкание?"
              Новичок: "Думаю здесь будет замыкание анонимной функции на параметр msg?"
              Гений: "Точно!"
              Гений: "А параметр delay?"
              Новичок: "Это просто параметр :D"
              Сообщение отредактировано: Cfon -
                Новичок: "Маста после нашей последней встречи я многое осознал про замыкания, но..."
                Гений: "Что? :)"
                Новичок: "Меня по прежнему гложат смутные сомнения, что я что-то не понимаю про замыкания... :unsure:"
                Гений: "Продолжай :)"
                Новичок: "Ну вы мне объяснили все так доступно и понятно, а почему в книжках все так не понятно? :blink:"
                Гений: "Например?"
                Новичок: "Ну вот есть код"
                ExpandedWrap disabled
                  function foo() {
                    var x = 5;
                    
                    function bar() {
                      console.log(x);
                    }
                   
                    bar();
                  }

                Новичок: "В книжках пишут что и тут функция bar будет замыкать переменную x :wacko:"
                Гений: "Да все верно :yes:"
                Новичок: "Маста! И вы туда же! :blink:"
                Гений: "Все дело в том, что с технической стороны замыканиями являются все функции, которые ссылаются на внешние данные, но если смотреть на это с практической стороны, то настоящая польза от замыканий будет либо когда идет возрат функции или асинхронный колбэк."
                Гений: "Хотя замыкания можно образовать и в синхронном колбэке"
                ExpandedWrap disabled
                  function foo() {
                    var i = 1;
                    [10,20,30].forEach(function () {
                      console.log(i++);
                    });
                  }

                Гений: "Здесь переменная i будет замкнутой, хотя для нас этот факт не столь очевиден и не столь важен как для JS :D"
                Новичок: "Маста вы мне открыли глаза на мир замыканий!"
                Новичок: "В тех книжках которые я прочел не пишут об этом! :blink:"
                Гений: "Это связано с тем, что зачастую технический писатель, как правило является еще и программистом :D"
                Новичок: "А вы? :blink:"
                Гений: "Я Гений! :lool:"
                Сообщение отредактировано: Cfon -
                  Гений: "Новичок как думаешь какое значение x выведет следущий код?"
                  ExpandedWrap disabled
                    function foo() {
                      var x = 5;
                     
                      function bar(callback) {
                        var x = 10;
                        callback();
                      }
                     
                      bar(function () {
                        console.log(x);
                      });
                    }
                     
                    foo();

                  Новичок: "Хмм... :scratch:"
                  Новичок: "10? :unsure:"
                  Гений: "Нет не верно, подумай пока а я пойду отолью :D"

                  Новичок: "5 :blink:"
                  Гений: "Правильно! Этот пример раскрывает нам еще одну сторону замыкания, а именно лексическую область видимости"
                  Новичок: "Что это такое? :wacko:"
                  Гений: "Это область видимости, которая формируется лексически :D"
                  Новичок: "Шутите :blink:"
                  Гений: "Ок, лексической областью видимости переменной называется область видимости, которая определяет видимость переменной по ее расположению в коде, т.е. так как он написан, а не как он исполняется"
                  Новичок: "Что значит написан и исполняется? :wacko:"
                  Новичок: "Код JS сразу же исполняется интерпретатором разве нет?"
                  Гений: "Честно говоря я не хочу углубляться в то, что и как делает интерпретатор. Но могу сказать, что современные интерпретаторы например V8 перед запуском кода сначала его компилирует в машинный код! Что не может не сказаться на производительности JS кода."
                  Новичок: "Ого круто! :blink:"
                  Новичок: "Так а как это все влияет на область видимости?"
                  Гений: "А никак, это относится к его производительности :D"
                  Гений: "Что касается интерпретации кода, то он выполняет в два этапа, сначала интерпретатор создает объект контекста исполнения, в котором создается объект скоуп (область видимости), в которой заносятся ссылки на внешние по отношению данной функции объекты переменных (если конечно функции вложена в другие функции), а также ссылку на глобальный объект.
                  При втором этапе собственно код исполняется, при котором интерпретатор создает еще один объект, который называют объектом активации и ссылка на него также заносится в скоуп данной функции."
                  Новичок: " :blink: "
                  Новичок: "Маста пардон конечно, но это требует переваривания :wacko:"
                  Гений: "Не удивительно! Я сам долго в это вникал пока не вник по самое не балуй! :lool:"
                  Сообщение отредактировано: Cfon -
                    Новичок: "Маста доброе утро. Вчера вы мне основательно запудрили голову :wacko:"
                    Новичок: "Можно еще раз объяснить что происходит в приведенном выше коде?"
                    Гений: "Добре хлопчик. Слушай сюда :D"
                    Гений: "Начну по порядку. Для удобства я продублирую его здесь, чтобы видеть нумерацию строк нажми третью кнопочку над кодом. Нажал? ОК."
                    ExpandedWrap disabled
                      function foo() {
                        var x = 5;
                       
                        function bar(callback) {
                          var x = 10;
                          callback();
                        }
                       
                        bar(function () {
                          console.log(x);
                        });
                      }
                       
                      foo();

                    Гений: "Как я уже говорил интерпретатор JS делает два прохода по коду. В первой строчке он видит объявление функции foo и сохраняет его в глобальном скоупе (объект window в броузере), далее он встречает в 14ой строке вызов функции foo() и создает объект контекста исполнения данной функции, в котором сохраняет ссылки на внешние области видимости или говоря по другому объекты переменных, в которых находятся определение локальных переменных. В нашем случае сначала он добавит ссылку на глобальный объект (объект window), а затем создаст локальный объект переменных или объект активации, в который занесет значения и ссылки на локальные переменные и функции, и ссылку на этот объект заносит в контекст. Если посмотреть на наш код, то мы видим тут переменную x и определение функции bar, вот они и сохраняются в нашем объекте активации."
                    Гений: "Итак что мы имеем на текущий момент?"
                    Новичок: "Объект контекста, с ссылками на внешние данные или области видимости? :unsure:"
                    Гений: "Точно! Вот этот объект активации и носит название лексической области видимости (lexical environment)"
                    Гений: "Идем далее. В 9ой строчке мы видим вызов функции bar(), в который передается анонимная функция, напомню интерпретатор все еще выполняет первый этап! Т.е. объявление этой функции заносится в наш объект активации! Но это не простая анонимная функции, она создает замыкание на переменную x, т.е. интерпретатор создает область, которая ссылается на наш объект активации, в котором и находится объявление переменной x!"
                    Гений: "Скажи какое значение у нас имеет переменная х в объекте активации?"
                    Новичок: "5! :P"
                    Гений: "Теперь тебе ясно почему в результате в консоле выведет 5."
                    Гений: "Но продолжаем исследовать. Итак на данном этапе исполнения кода у нас есть все необходимое для выполнения функции foo. Это и делает интерпретатор на втором этапе, собственно выполняет функции foo."
                    Гений: "Исполнение функции foo приводит повторному циклу интерпретатора, т.е. он снова выполняет два прохода по коду, но уже функции bar"
                    Гений: "Что то я утомился печатать :D"
                    Гений: "Сможешь продолжить?"
                    Новичок: "ОК :scratch:"
                    Новичок: "Значит так интерпретатор создает новый контекст исполнения, в котором создает объект скоуп, в котором сохраняет ссылку на глобальный объект переменных и объект активации. В объект активации он сохраняет значение переменной x равной 10 и анонимную функцию, значение которого передается через параметр callback. Затем интерпретатор собственно выполняет функцию bar"
                    Гений: "Верно! Ты все правильно понял :)"
                    Новичок: "Но я не закончил :P"
                    Гений: "Продолжай :D"
                    Новичок: "Выполнение функции bar проводит к вызову переданной анонимной функции, вызов которой также приводит к созданию нового контекста исполнения. Но поскольку оно образует замыкание ранее интерпретатор создал объект замыкание, в котором сохранил ссылку на объект активации функции foo! Таким образом при выполнении анонимной функции обращение к переменной x оператора console.log(x) будет изъято значение х из объекта активации функции foo, т.е. 5"
                    Гений: "Браво Новичок! Well done! Аппладирую стоя :D"
                    Новичок: " :blush: "
                    Сообщение отредактировано: Cfon -
                    1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                    0 пользователей:


                    Рейтинг@Mail.ru
                    [ Script Execution time: 0,1260 ]   [ 14 queries used ]   [ Generated: 24.09.18, 19:46 GMT ]