Включает ли это закрытие JavaScript?
В попытке изучить закрытие JavaScript я немного смутил себя.
Из того, что я собрал по сети, закрытие...
Объявление функции внутри другой функции, и эта внутренняя функция имеет доступ к своим родительским функциональным переменным даже после возвращения этой родительской функции.
Вот небольшой пример script из недавнего проекта. Он позволяет прокручивать текст в 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);
}());
В этом примере используются замыкания? Я знаю, что он имеет функции внутри функций, но существует ли случай, когда используются внешние переменные?
Я использую их, не зная об этом?
Спасибо
Update
Будет ли это делать замыкание, если я разместил это под оператором $(document).ready(init);
?
return {
scrollDown: scrollDown
};
Может быть, если бы я хотел, чтобы текст прокручивался из любого места в JavaScript, я мог бы сделать
pageScroll.scrollDown();
Другое обновление
Ничего себе, похоже, что это сработало! Вот код на JSbin. Обратите внимание, что скроллер страницы не работает точно в этом примере (он, похоже, не подходит к нижней части текста), но это нормально:) Бонусные баллы для всех, кто может сказать мне, почему он не прокручивается донизу.
Ответы
Ответ 1
Всякий раз, когда вы определяете функцию, эта функция будет "заключать" цепочку областей, в которой она была определена. Когда эта функция выполняется, цепочка цепей доступна в своем теле. Когда функция определена внутри другой функции, цепочка областей видимости включает в себя:
- Родительская функция, локально заданная переменная
- Аргументы родительской функции
- Родительская функция родительская функция локальных варов и аргументов и т.д.
- Глобальные переменные
Реальное значение замыканий заключается в возврате внутренней функции, которая захватила закрытую переменную, которая не изменится, например:
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"
Ответ 2
Взгляните на блог-дневник Дмитрия А. Сошникова о ECMA-262 ( "javascript" ).
Сообщение 6 http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/ касается закрытий, и если вы читаете предыдущие сообщения, вы должны хорошо понять его.
Краткая история; объявления функций или выражения функций, размещенные внутри функции (декларация или выражение), будут обладать расширенными возможностями родительских функций в цепочке областей видимости. Поэтому он имеет доступ ко всем переменным, содержащимся в каждой функции, до цепочки областей видимости (заканчивается глобальной областью). Но пока мы не говорим о закрытии.
Понятие "замыкание" появляется, когда вы создаете ссылку на такую содержащуюся функцию за пределами цепочки видимости, либо путем возврата ссылки на функцию, либо путем добавления ссылки на нее как свойство на объект выше в цепочке охвата. [чтобы вы, вызвав функцию (по этой ссылке), могли изменять переменные, скрытые внутри недоступной области.]
Эта ссылка также приведет к тому, что все переменные, содержащиеся в указанной области охвата, будут иметь по крайней мере одну ссылку (из каждой области), что означает, что ни одна из переменных, присутствующих в цепочке областей видимости, не может быть собрана мусором. Это так называемая утечка памяти (если вы на самом деле не планируете использовать переменные, которые есть!).
Ответ 3
Я не думаю, что вы используете какие-либо закрытия, потому что вы не возвращаете внутренние функции из своей внешней функции, поэтому эти переменные всегда будут в области видимости, когда их вызывают. Другими словами, init()
, scrollDown()
и scrollUp()
должны быть вызваны из pageScroll()
. Если pageScroll()
возвращал любую из трех своих внутренних функций, а затем вы вызывали их вне pageScroll()
, использование переменных $page
, $next
, $prev
и т.д. Было бы закрытием. Я думаю.
Ответ 4
Я не эксперт по Javascript, но из того, что я прочитал здесь, да, у вас есть закрытие.
Обратите внимание, что все внутренние функции совместно используют переменную $page, например. init назначает ему, а затем функции прокрутки обновляют его. Поэтому в принципе внутренними функциями являются замыкания внешней функции. Внешняя функция передает init в document.ready, поэтому в некоторых вариантах Javascript-ish возвращается закрытие.
Тем не менее, я также должен сказать, что этот код является очень плохим примером функционального программирования, поскольку эти чистые функции не должны иметь побочных эффектов (например, назначение общей переменной). Тем не менее, у него есть замыкания.
Ответ 5
Вы присоединяете к событию onclick
элемента #page-next
функцию scrollDown
, которая хранит ссылку на переменную canScroll
вашего объекта pageScroll
. Так что да, у вас есть закрытие.
На самом деле в вашем примере есть 4 закрытия: функция, отправленная на $next.click
, отправленная на $prev.click
, отправленная на $page.animate
, и отправленная на $(document).ready
.
Ответ 6
Да, у вас есть закрытие, посмотрите на этот быстрый пример
<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>
Closures имеют доступ к своим родительским переменным, в этом случае наша функция 't1' обращается к объявленному частным образом var x; также как вы заявили, что "внутренняя функция имеет доступ к своим родительским функциям, даже после того, как родительская функция вернулась". когда вы нажмете "Test me 2", вы получите предупреждение с x = 10, даже те, которые функция test() уже вернула, это имеет смысл.
Ответ 7
Да, в примере кода есть несколько замыканий. Путаница может возникнуть в результате применения слишком строгого требования к закрытию вашего определения. Хотя у замыкания есть доступ к своим связанным свободным переменным для времени жизни замыкания, нет необходимости, чтобы замыкание увеличивало время жизни этих переменных за пределами их нормальной среды, чтобы функция считалась замыканием.
Например, скажем, у нас есть функция findAllPeople
, которая находит в списке всех людей, которые имеют первое имя, которое поставляется в качестве параметра функции. Мы могли бы создать внутреннюю функцию, которая берет человека и проверяет свое имя на указанное имя. Эта внутренняя функция будет иметь две свободные переменные, а именно: лицо и имя для тестирования. Имя для проверки связано с параметром, указанным в findAllPeople
. Следовательно, внутренняя функция замкнута по этой свободной переменной. Тот факт, что внутренняя функция создается, используется и отбрасывается всеми при выполнении findAllPeople
и не используется нигде вне findAllPeople
, не имеет никакого отношения к тому, что внутренняя функция является закрытием.
Ответ 8
Да, конечно, есть замыкания. Например, вы используете $page, $next
и т.д., Объявленные в области вне, например, init
(который их использует).
Ответ 9
Какой день не принести свой Javascript: книга "Хорошие детали" для работы...
Я считаю, что короткий ответ "Нет" из-за этой части определения: "даже после того, как эта родительская функция вернулась". Но я не уверен на 100%.
Это может помочь.
Клип YouTube Javascript: Хорошие детали
http://www.youtube.com/watch?v=hQVTIJBZook&feature=player_embedded
Ответ 10
очевидно, да...
когда u вложил функцию внутри другой функции, u имеет замыкание.
thats it
var func = (function(){
var txt = 'Hi There!'
return function(){
alert(txt);
}
})();
func(); //alert 'Hi There', eventhough the outter function has exit