Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.138.33.87] |
|
Сообщ.
#1
,
|
|
|
Новичок: "Что такое замыкание?"
Гений: "Замыкание это скрытая область с переменными на которую может ссылаться функция" Новичок: Новичок: "Можно пример?" Гений: "Легко! " function add(arg1) { return function (arg2) { return arg1 + arg2; }; } var addFive = add(5); var result = addFive(10); // 15 Новичок: "Пардон, а где тут замыкание? Это же называется частичное применения (partial application), читай карринг (carring)." Гений: "Да это пример карринга, но для его реализации используется замыкания. Посмотри на параметр arg1 видишь на него ссылается анонимная функция, когда выполняется операция сложения?" Новичок: "Да маста." Гений: "Маста " Гений: "Ок, так вот этот параметр сохраняется в так называемой замкнутой области, которая сохраняется и не исчезает, когда функция add делает возврат анонимной функции." Гений: "Ссылка на эту замкнутую область сохраняется во внутреннем свойстве [[Closure]] возвращенной функции. Так что используя эту ссылку можно потом обратиться к параметру arg1, что делается в операторе return arg1+arg2." Гений: "Небольшое уточнение свойство [[Closure]], сохраняется как элемент еще одного внутреннего свойства [[Scopes]] (область видимости)." Новичок: "Все так сложно маста " Гений: "Ну это поначалу, потом все встанет на свои места " ПС. В данной серии `Диалоги о JS` я и мой воображаемый ученик будут юзать ECMAScript 3 поскольку ИМХО он лучше всего подходит для обучения JS. Кроме того диалоги будут носить не последовательный характер, т.е. темы диалогов будут идти не по нарастающей сложности JS, и иметь рандомный подход. Все будет зависеть от того что на меня найдет Да и Новичок это не новичок в JS, это скорее образ программиста имеющего вопросы по JS. |
Сообщ.
#2
,
|
|
|
Новичок: "Маста, я долго думал про замыкания и у меня возник вопрос"
Гений: "Жги " Новичок: "Как получить доступ к замкнутым данным?" Гений: "Хороший вопрос! Для этого надо вернуть объект. Попробуй сам решить ." Новичок: "Ок " |
Сообщ.
#3
,
|
|
|
Новичок: "Маста вот что у меня получилось"
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 Гений: "Новичок ты уже не новичок однако! " Гений: "Объясни что тут происходит." Новичок: "Значит так функция getCounter() возвращает объект в котором определены две функции. Функция count() возвращает значение переменной i, затем увеличивает на 1, а функция reset() восстанавливает значение i в 1. Здесь переменная i определена в замкнутой области и не доступна извне, кроме как через эти функции. Это и есть замыкание маста?" Гений: "Во истину так новичок! " Гений: "Позволь мне немного изменить твой код." Новичок: "Конечно маста." Гений: "Вспомни в JS функции являются также объектами, следовательно мы можем возвращать саму функцию предварительно присоединив к ней еще одну." 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 Новичок: "Маста вы гений! " Гений: "Ну как бы так " |
Сообщ.
#4
,
|
|
|
Цитата Cfon @ "Пардон, а где тут замыкание? Это же называется частичное применения (partial application), читай карринг (carring)." Открою секрет, что там нет ни замыкания, ни каррирования Замыкание - это сохранение внешнего контекста внутри внутренней функции, читай как проброс данных в анонимочку. А каррирование - это процесс преобразования функции от N аргументов до композиции функций от N-1 аргументов (хотя "-1" не обязательно). Частичное применение примерно тоже самое, но в произвольном порядке и прочими задротскими шнягами. У тебя и Гений и Новичок никчёмные короч. Давай ещё раз попробуем Добавлено А, да, вру, у тебя вложенная функция в первом примере является замыканием. Ок |
Сообщ.
#5
,
|
|
|
Цитата Serafim @ Открою секрет, что там нет ни замыкания, ни каррирования Новичок: "Маста что он несет? " Гений: "Не слушай его он сам не ведает что несет " Новичок: "Ок" |
Сообщ.
#6
,
|
|
|
Новичок: "Маста я прочел много книжек по JS про замыкания, но никак не могу понять каким-образом замыкания связаны с функциями обратного вызова, читай колбэк"
Гений: "Хороший вопрос! Сразу видно ты уже не новичок раз прочел много книжек " Новичок: Гений: "Ок сейчас налью себе чашку кофа и расскажу" Гений: "Кстати а какие книжки ты читал?" Новичок: "Разные..." Новичок: "Например `Изучаем программирование на JavaScript` Фримена и Робсон от 2014 года" Гений: "О! Хорошая книжка! Это моя первая книжка по JS" Гений: "Она как раз хорошо подходит для новичков" Гений: "Ну а что-нибудь более серьезное читал?" Новичок: "Конечно! Ну например `JavaScript подробное руководство` Фленагана" Гений: "Оцени ее" Новичок: "Ну книжка ничего себе, но изложение материала сухое, она более подходит для тех кто уже раньше программировал на других языках, или уже имеет начальные знания JS" Гений: "Вернемся к нашим баранам " Гений: "Замыкания в колбэках образуются, если они юзаются в асинхронных операциях" Новичок: "Эмм... например? " Гений: "Например обработка сетевых запросов, назначение обработчиков на события, ну или передача колбэка в функцию setTimeout" function displayMessage(msg, delay) { setTimeout(function () { console.log(msg); }, delay); } displayMessage('Hello', 1000); // Hello Гений: "Сможешь объяснить где тут замыкание?" Новичок: "Думаю здесь будет замыкание анонимной функции на параметр msg?" Гений: "Точно!" Гений: "А параметр delay?" Новичок: "Это просто параметр " |
Сообщ.
#7
,
|
|
|
Новичок: "Маста после нашей последней встречи я многое осознал про замыкания, но..."
Гений: "Что? " Новичок: "Меня по прежнему гложат смутные сомнения, что я что-то не понимаю про замыкания... " Гений: "Продолжай " Новичок: "Ну вы мне объяснили все так доступно и понятно, а почему в книжках все так не понятно? " Гений: "Например?" Новичок: "Ну вот есть код" function foo() { var x = 5; function bar() { console.log(x); } bar(); } Новичок: "В книжках пишут что и тут функция bar будет замыкать переменную x " Гений: "Да все верно " Новичок: "Маста! И вы туда же! " Гений: "Все дело в том, что с технической стороны замыканиями являются все функции, которые ссылаются на внешние данные, но если смотреть на это с практической стороны, то настоящая польза от замыканий будет либо когда идет возрат функции или асинхронный колбэк." Гений: "Хотя замыкания можно образовать и в синхронном колбэке" function foo() { var i = 1; [10,20,30].forEach(function () { console.log(i++); }); } Гений: "Здесь переменная i будет замкнутой, хотя для нас этот факт не столь очевиден и не столь важен как для JS " Новичок: "Маста вы мне открыли глаза на мир замыканий!" Новичок: "В тех книжках которые я прочел не пишут об этом! " Гений: "Это связано с тем, что зачастую технический писатель, как правило является еще и программистом " Новичок: "А вы? " Гений: "Я Гений! " |
Сообщ.
#8
,
|
|
|
Гений: "Новичок как думаешь какое значение x выведет следущий код?"
function foo() { var x = 5; function bar(callback) { var x = 10; callback(); } bar(function () { console.log(x); }); } foo(); Новичок: "Хмм... " Новичок: "10? " Гений: "Нет не верно, подумай пока а я пойду отолью " Новичок: "5 " Гений: "Правильно! Этот пример раскрывает нам еще одну сторону замыкания, а именно лексическую область видимости" Новичок: "Что это такое? " Гений: "Это область видимости, которая формируется лексически " Новичок: "Шутите " Гений: "Ок, лексической областью видимости переменной называется область видимости, которая определяет видимость переменной по ее расположению в коде, т.е. так как он написан, а не как он исполняется" Новичок: "Что значит написан и исполняется? " Новичок: "Код JS сразу же исполняется интерпретатором разве нет?" Гений: "Честно говоря я не хочу углубляться в то, что и как делает интерпретатор. Но могу сказать, что современные интерпретаторы например V8 перед запуском кода сначала его компилирует в машинный код! Что не может не сказаться на производительности JS кода." Новичок: "Ого круто! " Новичок: "Так а как это все влияет на область видимости?" Гений: "А никак, это относится к его производительности " Гений: "Что касается интерпретации кода, то он выполняет в два этапа, сначала интерпретатор создает объект контекста исполнения, в котором создается объект скоуп (область видимости), в которой заносятся ссылки на внешние по отношению данной функции объекты переменных (если конечно функции вложена в другие функции), а также ссылку на глобальный объект. При втором этапе собственно код исполняется, при котором интерпретатор создает еще один объект, который называют объектом активации и ссылка на него также заносится в скоуп данной функции." Новичок: " " Новичок: "Маста пардон конечно, но это требует переваривания " Гений: "Не удивительно! Я сам долго в это вникал пока не вник по самое не балуй! " |
Сообщ.
#9
,
|
|
|
Новичок: "Маста доброе утро. Вчера вы мне основательно запудрили голову "
Новичок: "Можно еще раз объяснить что происходит в приведенном выше коде?" Гений: "Добре хлопчик. Слушай сюда " Гений: "Начну по порядку. Для удобства я продублирую его здесь, чтобы видеть нумерацию строк нажми третью кнопочку над кодом. Нажал? ОК." 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, вот они и сохраняются в нашем объекте активации." Гений: "Итак что мы имеем на текущий момент?" Новичок: "Объект контекста, с ссылками на внешние данные или области видимости? " Гений: "Точно! Вот этот объект активации и носит название лексической области видимости (lexical environment)" Гений: "Идем далее. В 9ой строчке мы видим вызов функции bar(), в который передается анонимная функция, напомню интерпретатор все еще выполняет первый этап! Т.е. объявление этой функции заносится в наш объект активации! Но это не простая анонимная функции, она создает замыкание на переменную x, т.е. интерпретатор создает область, которая ссылается на наш объект активации, в котором и находится объявление переменной x!" Гений: "Скажи какое значение у нас имеет переменная х в объекте активации?" Новичок: "5! " Гений: "Теперь тебе ясно почему в результате в консоле выведет 5." Гений: "Но продолжаем исследовать. Итак на данном этапе исполнения кода у нас есть все необходимое для выполнения функции foo. Это и делает интерпретатор на втором этапе, собственно выполняет функции foo." Гений: "Исполнение функции foo приводит повторному циклу интерпретатора, т.е. он снова выполняет два прохода по коду, но уже функции bar" Гений: "Что то я утомился печатать " Гений: "Сможешь продолжить?" Новичок: "ОК " Новичок: "Значит так интерпретатор создает новый контекст исполнения, в котором создает объект скоуп, в котором сохраняет ссылку на глобальный объект переменных и объект активации. В объект активации он сохраняет значение переменной x равной 10 и анонимную функцию, значение которого передается через параметр callback. Затем интерпретатор собственно выполняет функцию bar" Гений: "Верно! Ты все правильно понял " Новичок: "Но я не закончил " Гений: "Продолжай " Новичок: "Выполнение функции bar проводит к вызову переданной анонимной функции, вызов которой также приводит к созданию нового контекста исполнения. Но поскольку оно образует замыкание ранее интерпретатор создал объект замыкание, в котором сохранил ссылку на объект активации функции foo! Таким образом при выполнении анонимной функции обращение к переменной x оператора console.log(x) будет изъято значение х из объекта активации функции foo, т.е. 5" Гений: "Браво Новичок! Well done! Аппладирую стоя " Новичок: " " |