Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > JavaScript, DOM/DHTML > Диалоги о JS


Автор: Cfon 01.09.18, 03:22
Новичок: "Что такое замыкание?"
Гений: "Замыкание это скрытая область с переменными на которую может ссылаться функция"

Новичок: :wacko:
Новичок: "Можно пример?"
Гений: "Легко! :D"
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 01.09.18, 16:22
Новичок: "Маста, я долго думал про замыкания и у меня возник вопрос"
Гений: "Жги :D"
Новичок: "Как получить доступ к замкнутым данным?"
Гений: "Хороший вопрос! Для этого надо вернуть объект. Попробуй сам решить :D."
Новичок: "Ок :huh:"

Автор: Cfon 02.09.18, 03:56
Новичок: "Маста вот что у меня получилось"
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 функции являются также объектами, следовательно мы можем возвращать саму функцию предварительно присоединив к ней еще одну."
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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:"

Автор: Serafim 03.09.18, 09:42
Цитата Cfon @
"Пардон, а где тут замыкание? Это же называется частичное применения (partial application), читай карринг (carring)."

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

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

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

Добавлено
А, да, вру, у тебя вложенная функция в первом примере является замыканием. Ок :D

Автор: Cfon 04.09.18, 17:49
Цитата Serafim @
Открою секрет, что там нет ни замыкания, ни каррирования :whistle:

Новичок: "Маста что он несет? :huh:"
Гений: "Не слушай его он сам не ведает что несет :D"
Новичок: "Ок"

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

Гений: "Сможешь объяснить где тут замыкание?"
Новичок: "Думаю здесь будет замыкание анонимной функции на параметр msg?"
Гений: "Точно!"
Гений: "А параметр delay?"
Новичок: "Это просто параметр :D"

Автор: Cfon 08.09.18, 03:21
Новичок: "Маста после нашей последней встречи я многое осознал про замыкания, но..."
Гений: "Что? :)"
Новичок: "Меня по прежнему гложат смутные сомнения, что я что-то не понимаю про замыкания... :unsure:"
Гений: "Продолжай :)"
Новичок: "Ну вы мне объяснили все так доступно и понятно, а почему в книжках все так не понятно? :blink:"
Гений: "Например?"
Новичок: "Ну вот есть код"
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    function foo() {
      var x = 5;
      
      function bar() {
        console.log(x);
      }
     
      bar();
    }

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

Гений: "Здесь переменная i будет замкнутой, хотя для нас этот факт не столь очевиден и не столь важен как для JS :D"
Новичок: "Маста вы мне открыли глаза на мир замыканий!"
Новичок: "В тех книжках которые я прочел не пишут об этом! :blink:"
Гений: "Это связано с тем, что зачастую технический писатель, как правило является еще и программистом :D"
Новичок: "А вы? :blink:"
Гений: "Я Гений! :lool:"

Автор: Cfon 11.09.18, 03:22
Гений: "Новичок как думаешь какое значение x выведет следущий код?"
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 13.09.18, 06:35
Новичок: "Маста доброе утро. Вчера вы мне основательно запудрили голову :wacko:"
Новичок: "Можно еще раз объяснить что происходит в приведенном выше коде?"
Гений: "Добре хлопчик. Слушай сюда :D"
Гений: "Начну по порядку. Для удобства я продублирую его здесь, чтобы видеть нумерацию строк нажми третью кнопочку над кодом. Нажал? ОК."
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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: "

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)