В документации jQuery.fn.on
есть хорошее объяснение.
Короче:
Обработчики событий привязаны только к выбранные в данный момент элементы; они должны существовать на странице, когда ваш код делает вызов
blockquote>.on()
.Таким образом, в следующем примере
#dataTable tbody tr
должен существовать до генерации кода.$("#dataTable tbody tr").on("click", function(event){ console.log($(this).text()); });
Если на страницу вводится новый HTML-код, предпочтительнее использовать делегированные события для присоединения обработчика событий, как описано ниже.
Делегированные события имеют то преимущество, что они могут обрабатывать события от элементов потомков, которые будут добавлены в документ позже. Например, если таблица существует, но строки добавляются динамически с использованием кода, следующее будет обрабатывать ее:
$("#dataTable tbody").on("click", "tr", function(event){ console.log($(this).text()); });
В дополнение к их способности обрабатывать события на дочерних элементах, которые еще не созданы, другим преимуществом делегированных событий является их потенциал для гораздо более низких накладных расходов, когда необходимо контролировать многие элементы. В таблице данных с 1000 строками в
tbody
первый пример кода прикрепляет обработчик к 1000 элементам.Подход с делегированными событиями (второй пример кода) прикрепляет обработчик события только к одному элементу ,
tbody
, и событию нужно только выровнять один уровень (от щелчкаtr
доtbody
).Примечание. Делегированные события не работают для SVG .
ПРЕДИСЛОВИЕ: этот ответ был написан, когда вопрос был:
Как старый Альберт сказал: «Если вы не можете объяснить это шестилетнему ребенку, вы действительно не понимаете сами ». Ну, я попытался объяснить закрытие JS 27-летнему другу и полностью потерпел неудачу.
Кто-нибудь может подумать, что мне 6 и странно интересуется этим вопросом?
blockquote>Я уверен, что я был одним из тех людей, которые пытались взять исходный вопрос буквально. С тех пор этот вопрос несколько раз мутировал, поэтому мой ответ теперь может казаться невероятно глупым и неуместным. Надеюсь, общая идея этой истории остается для некоторых забавой.
Я очень люблю аналог и метафору при объяснении сложных понятий, поэтому позвольте мне попробовать свои силы в истории.
Когда-то:
Была принцесса ...
function princess() {
Она жила в прекрасном мире, полном приключений. Она познакомилась с ее принцем Обаятельным, ехала вокруг ее мира на единороге, сражались драконы, е ncountered говорящие животные и многие другие фантастические вещи.
var adventures = []; function princeCharming() { /* ... */ } var unicorn = { /* ... */ }, dragons = [ /* ... */ ], squirrel = "Hello!"; /* ... */
Но ей всегда приходилось возвращаться в свой скучный мир хлопот и взрослых.
return {
И она часто рассказывала им о своем последнем удивительном приключении в качестве принцессы.
story: function() { return adventures[adventures.length - 1]; } }; }
Но все, что они увидели бы, это маленькая девочка ...
var littleGirl = princess();
... рассказывая рассказы о магии и фантазии.
littleGirl.story();
И хотя взрослые знали о настоящих принцессах, они никогда не поверили бы в единорогов или драконов, потому что они никогда их не увидели. Взрослые говорили, что они существуют только внутри воображения маленькой девочки.
Но мы знаем истинную истину; что маленькая девочка с принцессой внутри ...
... на самом деле принцесса с маленькой девочкой внутри.
story()
, которая является единственным интерфейсом, который экземпляр littleGirl
выставляет в мир магии.
– Patrick M
28 February 2013 в 09:49
story
является замыканием, но если код был var story = function() {}; return story;
, тогда littleGirl
будет замыканием. По крайней мере, создается впечатление, что я получаю из использование MDN «частных» методов с закрытием : ». Эти три публичные функции - это блокировки, которые используют одну и ту же среду. & Quot; i>
– icc97
23 February 2016 в 01:58
story
является закрытием, ссылающимся на среду, предоставленную в рамках princess
. princess
также является еще одним закрытием подразумеваемого i>, то есть princess
и littleGirl
будут передавать любую ссылку на массив parents
, который будет существовать в среде / области, где littleGirl
существует и определяется princess
.
– Jacob Swartwood
1 March 2016 в 17:00
princess
больше операций, чем написано. К сожалению, эта история сейчас немного неуместна в этой теме. Первоначально вопрос заключался в том, чтобы «объяснять закрытие JavaScript для 5yr old»; мой ответ был единственным, кто даже пытался это сделать. Я не сомневаюсь, что это провалилось бы ужасно, но, по крайней мере, этот ответ мог бы иметь интерес к 5-летнему старому.
– Jacob Swartwood
11 September 2017 в 18:45
Взяв вопрос всерьез, мы должны выяснить, что типичный 6-летний человек способен когнитивно, хотя, по общему признанию, тот, кто интересуется JavaScript, не так типичен.
В Развитие детства: от 5 до 7 лет говорится:
Ваш ребенок сможет следовать двухэтапным направлениям. Например, если вы скажете своему ребенку: «Идите на кухню и достаньте мешок для мусора», они смогут запомнить это направление.
blockquote>Мы можем использовать этот пример, чтобы объяснить закрытие , следующим образом:
Кухня - это замыкание, которое имеет локальную переменную, называемую
blockquote>trashBags
. Существует функция внутри кухни под названиемgetTrashBag
, которая получает один мусорный мешок и возвращает его.Мы можем закодировать это в JavaScript следующим образом:
function makeKitchen () { var trashBags = ['A', 'B', 'C']; // only 3 at first return { getTrashBag: function() { return trashBags.pop(); } }; } var kitchen = makeKitchen(); kitchen.getTrashBag(); // returns trash bag C kitchen.getTrashBag(); // returns trash bag B kitchen.getTrashBag(); // returns trash bag A
Другие моменты, которые объясняют, почему замыкания интересны:
- Каждый раз, когда вызывается
makeKitchen()
, создается новое замыкание со своим отдельнымtrashBags
.trashBags
является локальной для каждой кухни и недоступна снаружи, но внутренняя функция в свойствеgetTrashBag
имеет к ней доступ.- Каждый вызов функции создает замыкание, но не нужно будет закрывать его, если внутренняя функция, которая имеет доступ к внутренней части замыкания, может быть вызвана из-за закрытия. Возвращение объекта с помощью функции
getTrashBag
делает это здесь.
Мне нужно знать, сколько раз нажата кнопка и что-то делать на каждом третьем клике ...
// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');
element.addEventListener("click", function() {
// Increment outside counter
counter++;
if (counter === 3) {
// Do something every third time
console.log("Third time's the charm!");
// Reset counter
counter = 0;
}
});
<button id="button">Click Me!</button>
Теперь это сработает, но оно вторгнется во внешнюю область, добавив переменную, единственной целью которой является отслеживание счета , В некоторых ситуациях это было бы предпочтительнее, так как вашему внешнему приложению может потребоваться доступ к этой информации. Но в этом случае мы изменяем только поведение каждого третьего клика, поэтому предпочтительно включать эту функциональность внутри обработчика событий.
var element = document.getElementById('button');
element.addEventListener("click", (function() {
// init the count to 0
var count = 0;
return function(e) { // <- This function becomes the click handler
count++; // and will retain access to the above `count`
if (count === 3) {
// Do something every third time
console.log("Third time's the charm!");
//Reset counter
count = 0;
}
};
})());
<button id="button">Click Me!</button>
Обратите внимание на несколько вещей.
В приведенном выше примере я использую поведение закрытия JavaScript. Такое поведение позволяет любой функции иметь доступ к области, в которой она была создана, на неопределенный срок. Чтобы практически применить это, я немедленно вызываю функцию, которая возвращает другую функцию, и потому что возвращаемая функция имеет доступ к внутренней переменной счетчика (из-за описанного выше поведения закрытия), это приводит к закрытой области для использования в результате функция ... Не так просто? Давайте разбавим его ...
Простое однострочное закрытие
// _______________________Immediately invoked______________________
// | |
// | Scope retained for use ___Returned as the____ |
// | only by returned function | value of func | |
// | | | | | |
// v v v v v v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();
Все переменные за пределами возвращаемой функции доступны для возвращаемой функции, но они не доступны непосредственно для возвращаемый объект функции ...
func(); // Alerts "val"
func.a; // Undefined
Получите его? Таким образом, в нашем основном примере переменная count содержится в закрытии и всегда доступна для обработчика событий, поэтому она сохраняет свое состояние от щелчка до щелчка.
Кроме того, это состояние частной переменной полностью доступно для оба чтения и присвоение его частным переменным области.
Там вы идете; вы теперь полностью инкапсулируете это поведение.
Полная запись в блоге (включая соображения jQuery)
Я не понимаю, почему ответы здесь настолько сложны.
Вот замыкание:
var a = 42;
function b() { return a; }
Да. Вы, вероятно, используете это много раз в день.
Нет причин полагать, что закрытие - это сложный хакер для решения конкретных проблем. Нет, закрытие - это просто использование переменной, которая исходит из более высокой области с точки зрения того, где была объявлена функция (не запускается).
Теперь, что она позволяет вам делать может быть более впечатляющим, см. другие ответы.
blockquote>
a
. На мой взгляд, удивление заключается в том, что объект области JS эффективно обеспечивает a
как свойство, а не константу. И вы заметите только это важное поведение, если вы измените его, как в return a++;
– Jon Coombs
15 May 2015 в 01:34
Возможно, немного выше всех, кроме самых ранних шестилетних, но несколько примеров, которые помогли мне сделать концепцию закрытия в JavaScript.
Закрытие - это функция, которая имеет доступ к области другой функции (ее переменные и функции). Самый простой способ создать замыкание - это функция внутри функции; причина в том, что в JavaScript функция всегда имеет доступ к области своей содержащей функции.
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
alert(outerVar);
}
innerFunction();
}
outerFunction();
ALERT: обезьяна
В приведенном выше примере вызывается внешняя функция, которая в свою очередь вызывает innerFunction. Обратите внимание, что внешний VAR доступен для innerFunction, о чем свидетельствует его правильное предупреждение о значении externalVar.
Теперь рассмотрим следующее:
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
return outerVar;
}
return innerFunction;
}
var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());
ALERT: обезьяна
referenceToInnerFunction устанавливается в функцию externalFunction (), которая просто возвращает ссылку на innerFunction. Когда вызывается referenceToInnerFunction, он возвращает outerVar. Опять же, как и выше, это демонстрирует, что innerFunction имеет доступ к externalVar, переменной внешней функции. Кроме того, интересно отметить, что он сохраняет этот доступ даже после завершения функции externalFunction.
И здесь все становится действительно интересным. Если бы мы избавились от externalFunction, скажем, установим его в null, вы можете подумать, что referenceToInnerFunction потеряет свой доступ к значению externalVar. Но это не так.
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
return outerVar;
}
return innerFunction;
}
var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());
outerFunction = null;
alert(referenceToInnerFunction());
ALERT: обезьяна ALERT: обезьяна
Но как это так? Как может referenceToInnerFunction все еще знать значение outerVar, теперь, когда внешняя функция была установлена в нуль?
Причина, по которой referenceToInnerFunction все еще может получить доступ к значению externalVar, заключается в том, что когда закрытие было сначала создано путем помещения внутренней функции внутри внешней функции , innerFunction добавила ссылку на область внешнего интерфейса (ее переменные и функции) в свою цепочку видимости. Это означает, что innerFunction имеет указатель или ссылку на все переменные externalFunction, включая outerVar. Таким образом, даже когда функция externalFunction завершила выполнение, или даже если она была удалена или установлена в нуль, переменные в своей области, такие как outerVar, остаются в памяти из-за выдающейся ссылки на них со стороны внутренней функции, которая была возвращена referenceToInnerFunction. Чтобы по-настоящему освободить externalVar и остальные переменные externalFunction из памяти, вам придется избавиться от этой выдающейся ссылки на них, скажем, установив для referenceToInnerFunction значение null.
///////// /
Еще две вещи о замыканиях, которые следует учитывать. Во-первых, у закрытия всегда будет доступ к последним значениям его содержащей функции.
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
alert(outerVar);
}
outerVar = "gorilla";
innerFunction();
}
outerFunction();
ALERT: gorilla
Во-вторых, когда создается замыкание, он сохраняет ссылку на все свои функции и функции своей вмещающей функции; он не может выбрать и выбрать. И все же, закрытие должно использоваться экономно или, по крайней мере, тщательно, поскольку они могут быть интенсивными в памяти; многие переменные могут храниться в памяти многократно после завершения исполняющей функции.
Я просто укажу их на страницу Mozilla Closures . Это лучшее, кратчайшее и простое объяснение основ закрытия и практического использования, которые я нашел. Настоятельно рекомендуется всем, кто изучает JavaScript.
И да, я бы даже рекомендовал его 6-летнему - если 6-летний учитель узнает о закрытии, то логично, что они готовый понять краткое и простое объяснение , приведенное в статье.
Я знаю, что уже есть много решений, но я предполагаю, что этот небольшой и простой скрипт может быть полезен для демонстрации концепции:
// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
Как я объясню это шестилетнему ребенку:
Вы знаете, как взрослые могут владеть домом, и они называют его домом? Когда у мамы есть ребенок, ребенок действительно ничего не владеет, верно? Но его родители владеют домом, поэтому всякий раз, когда кто-то спрашивает ребенка «Где твой дом?», Он может ответить «на этот дом!» И указать на дом своих родителей. «Закрытие» - это способность ребенка всегда (даже если за границей) быть в состоянии сказать, что у него есть дом, хотя это действительно родители, которые владеют этим домом.
Пример для первой точки dlaliberte:
Закрытие создается не только при возврате внутренней функции. Фактически, закрывающая функция вообще не нуждается в возврате. Вместо этого вы можете назначить свою внутреннюю функцию переменной во внешней области или передать ее в качестве аргумента другой функции, где ее можно будет использовать немедленно. Поэтому закрытие закрывающей функции, вероятно, уже существует в момент вызова функции-приложения, поскольку любая внутренняя функция имеет доступ к ней, как только она вызывается.
blockquote>var i; function foo(x) { var tmp = 3; i = function (y) { console.log(x + y + (++tmp)); } } foo(2); i(3);
Затруднения трудно объяснить, потому что они используются, чтобы сделать какую-то работу по поведению, которую все интуитивно ожидают, чтобы работать в любом случае. Я нахожу лучший способ объяснить их (и то, как I узнал, что они делают) представляет собой ситуацию без них:
var bind = function(x) {
return function(y) { return x + y; };
}
var plus5 = bind(5);
console.log(plus5(3));
Что произойдет, если JavaScript не знает закрытие? Просто замените вызов в последней строке телом метода (который в основном выполняет вызовы функции), и вы получите:
console.log(x + 3);
Теперь, где определение x
? Мы не определяли его в текущей области. Единственное решение состоит в том, чтобы позволить plus5
переносить свою область (или, точнее, область ее родителя) вокруг. Таким образом, x
четко определен и привязан к значению 5.
alert(x+3, where x = 5)
. where x = 5
является замыканием. Я прав?
– Jus12
22 December 2010 в 10:52
Я собрал интерактивный учебник по JavaScript, чтобы объяснить, как работают замыкания. Что такое закрытие?
Вот один из примеров:
var create = function (x) {
var f = function () {
return x; // We can refer to x here!
};
return f;
};
// 'create' takes one argument, creates a function
var g = create(42);
// g is a function that takes no arguments now
var y = g();
// y is 42 here
Дети всегда будут помнить секреты, которые они поделили с родителями, даже после того, как их родители ушли. Это то, что закрывает для функций.
blockquote>Секреты функций JavaScript - это частные переменные
var parent = function() { var name = "Mary"; // secret }
Каждый раз, когда вы вызываете его, локальная переменная «name» создал и получил имя «Мэри». И каждый раз, когда функция выходит из переменной, теряется и имя забывается.
Как вы можете догадаться, поскольку переменные повторно создаются каждый раз, когда функция вызывается, и никто их не узнает, должно быть секретным местом, где они хранятся. Его можно было бы назвать Тайной Палатой или стек или местный охват, но это не имеет большого значения. Мы знаем, что они где-то скрыты в памяти.
Но в JavaScript есть особая вещь, что функции, созданные внутри других функций, также могут знать локальные переменные своих родителей и поддерживать если они живут.
var parent = function() { var name = "Mary"; var child = function(childName) { // I can also see that "name" is "Mary" } }
Итак, до тех пор, пока мы находимся в родительской -функции, он может создать одну или несколько дочерних функций, которые разделяют секретные переменные из секретного места.
Но печально, что если дочерний элемент также является частной переменной его родительской функции, он также погибнет, когда родитель закончится, и секреты будут умирать вместе с ними.
So чтобы жить, ребенок должен уйти, пока не стало слишком поздно
var parent = function() { var name = "Mary"; var child = function(childName) { return "My name is " + childName +", child of " + name; } return child; // child leaves the parent -> } var child = parent(); // < - and here it is outside
И теперь, хотя Мэри «больше не работает», память о ней не потеряна, и ее ребенок всегда будет помнить ее имя и другие секреты, которые они делили в течение их времени вместе.
Итак, если вы позвоните ребенку «Алиса», она ответит
child("Alice") => "My name is Alice, child of Mary"
. Это все, что нужно сказать.
Всякий раз, когда вы видите ключевое слово function в другой функции, внутренняя функция имеет доступ к переменным во внешней функции.
function foo(x) {
var tmp = 3;
function bar(y) {
console.log(x + y + (++tmp)); // will log 16
}
bar(10);
}
foo(2);
Это всегда будет записывать 16, потому что bar
может получить доступ к x
, который был определен как аргумент foo
, а также tmp
из foo
. Он также может получить доступ к tmp
.
Что является замыканием. Функция не должна возвращать , чтобы называться замыканием. Простое обращение к переменным за пределами вашей непосредственной лексической области создает закрытие .
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + (++tmp)); // will also log 16
}
}
var bar = foo(2); // bar is now a closure.
bar(10);
Вышеуказанная функция также будет записываться в журнал 16, поскольку bar
все еще может ссылаться на x
и tmp
, даже если он больше не находится внутри области.
Однако, поскольку tmp
все еще висит внутри bar
, он также увеличивается. Он будет увеличиваться каждый раз при вызове bar
.
Простейшим примером замыкания является следующее:
var a = 10;
function test() {
console.log(a); // will output 10
console.log(b); // will output 6
}
var b = 6;
test();
При вызове функции JavaScript новый контекст выполнения создано. Вместе с аргументами функции и родительским объектом этот контекст выполнения также принимает все переменные, объявленные вне него (в приведенном выше примере «a» и «b»).
Можно создать более одной функции закрытия, либо путем возврата их списка, либо путем установки их на глобальные переменные. Все они будут ссылаться на те же x
и те же tmp
, что они не делают своих собственных копий.
Здесь число x
- это буквальное число. Как и в других литералах в JavaScript, когда вызывается foo
, число x
копируется в foo
как его аргумент x
.
С другой стороны, JavaScript всегда использует ссылки при работе с объектами. Если, скажем, вы вызвали foo
с объектом, то его замыкание ссылается на этот исходный объект!
function foo(x) {
var tmp = 3;
return function (y) {
console.log(x + y + tmp);
x.memb = x.memb ? x.memb + 1 : 1;
console.log(x.memb);
}
}
var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);
Как и ожидалось, каждый вызов bar(10)
будет увеличиваться x.memb
. Нельзя ожидать, что x
просто ссылается на тот же объект, что и переменная age
! После пары звонков в bar
, age.memb
будет 2! Эта ссылка является основой для утечек памяти с объектами HTML.
delete
терпит неудачу. Тем не менее, лексическая среда, которую функция будет выполнять как [[Область]] (и в конечном итоге использовать в качестве основы для своей собственной лексической среды при вызове), определяется, когда выполняется оператор, определяющий функцию. Это означает, что функция - это i>, закрывающая содержимое ENTIRE области выполнения, независимо от того, на какие значения она действительно ссылается и выходит ли она из области видимости. Пожалуйста, посмотрите разделы 13.2 и 10 в spec
– Asad Saeeduddin
20 August 2013 в 18:51
Функции JavaScript могут обращаться к их:
Если функция обращается к своей среде, то функция является закрытием.
Обратите внимание, что внешние функции не требуются, хотя они предлагают льготы, которые я здесь не обсуждаю. Получая доступ к данным в своей среде, закрытие сохраняет эти данные. В подслучае внешних / внутренних функций внешняя функция может создавать локальные данные и, в конечном счете, выходить, и все же, если какая-либо внутренняя функция (ы) выживает после выхода внешней функции, тогда внутренняя функция (ы) сохраняет локальные данные внешней функции живой.
Пример закрытия, использующего глобальную среду:
Представьте, что события кнопки «Переполнение стека» и «Голос-вниз» реализованы как закрытие, voteUp_click и voteDown_click, что имеют доступ к внешним переменным isVotedUp и isVotedDown, которые определены глобально. (Для простоты я имею в виду кнопки «Голос голоса» StackOverflow, а не массив кнопок «Ответ на голосование».)
Когда пользователь нажимает кнопку VoteUp, функция voteUp_click проверяет, является ли isVotedDown == true, чтобы определить, голосовать или просто отменять пониженное голосование. Функция voteUp_click является закрытием, потому что она обращается к своей среде.
var isVotedUp = false;
var isVotedDown = false;
function voteUp_click() {
if (isVotedUp)
return;
else if (isVotedDown)
SetDownVote(false);
else
SetUpVote(true);
}
function voteDown_click() {
if (isVotedDown)
return;
else if (isVotedUp)
SetUpVote(false);
else
SetDownVote(true);
}
function SetUpVote(status) {
isVotedUp = status;
// Do some CSS stuff to Vote-Up button
}
function SetDownVote(status) {
isVotedDown = status;
// Do some CSS stuff to Vote-Down button
}
Все четыре из этих функций являются закрытием, поскольку все они обращаются к своей среде.
Я стараюсь лучше учиться на тестах GOOD / BAD. Мне нравится видеть рабочий код, за которым следует нерабочий код, с которым кто-то может столкнуться. Я собрал a jsFiddle , который делает сравнение, и пытается свести различия к простейшим объяснениям, которые я мог бы придумать.
console.log('CLOSURES DONE RIGHT');
var arr = [];
function createClosure(n) {
return function () {
return 'n = ' + n;
}
}
for (var index = 0; index < 10; index++) {
arr[index] = createClosure(index);
}
for (var index in arr) {
console.log(arr[index]());
}
createClosure(n)
вызывается на каждой итерации цикла. Обратите внимание, что я назвал переменную n
, чтобы выделить, что это новая переменная, созданная в новой области функций, и не является той же переменной, что и index
, которая привязана к внешней области. n
привязаны к этой области; это означает, что у нас есть 10 отдельных областей, по одному для каждой итерации. createClosure(n)
возвращает функцию, которая возвращает n в пределах этой области. n
независимо от того, какое значение оно имело, когда createClosure(n)
был вызван, поэтому вложенная функция, которая возвращается, всегда будет возвращать значение n
, которое оно имело при вызове createClosure(n)
. console.log('CLOSURES DONE WRONG');
function createClosureArray() {
var badArr = [];
for (var index = 0; index < 10; index++) {
badArr[index] = function () {
return 'n = ' + index;
};
}
return badArr;
}
var badArr = createClosureArray();
for (var index in badArr) {
console.log(badArr[index]());
}
createClosureArray()
, и теперь функция только возвращает завершенный массив, который на первый взгляд кажется более интуитивным. createClosureArray()
вызывается только после создания только одной области для этой функции вместо одной для каждой итерации цикла. index
. Цикл запускается и добавляет функции массиву, возвращающему index
. Обратите внимание, что index
определяется внутри функции createClosureArray
, которая только когда-либо вызывается один раз. createClosureArray()
имеется только одна область, index
привязана только к значению в пределах этого объема. Другими словами, каждый раз, когда цикл меняет значение index
, он меняет его на все, что ссылается на него в пределах этой области. index
из родительской области, где она была определена вместо 10 различных из 10 различных областей, таких как первый пример. Конечным результатом является то, что все 10 функций возвращают одну и ту же переменную из той же области. index
было изменено конечное значение 10, поэтому каждая функция, добавленная в массив, возвращается значение единственной переменной index
, которая теперь установлена в 10. ЗАКРЫТЬ СДЕЛАНО ПРАВО n = 0 n = 1 n = 2 n = 3 n = 4 n = 5 n = 6 n = 7 n = 8 n = 9
ЗАКРЫВАЕТСЯ СОВЕРШЕННО n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10
blockquote>
n
, созданные в новом закрытии. Мы просто возвращаем функцию, чтобы сохранить ее в массиве и вызывать ее позже.
– Chev
29 December 2017 в 19:52
Закрытие очень похоже на объект. Он создается при каждом вызове функции.
Объем закрытия в JavaScript лексический, что означает, что все, что содержится в функции, к которой принадлежит замыкание , имеет доступ к любому переменная, которая в ней.
Переменная содержится в закрытии , если вы
var foo=1;
или var foo;
Если внутренняя функция (функция, содержащаяся внутри другой функции) обращается к такой переменной, не определяя ее в своей собственной области с помощью var, она изменяет содержимое переменная во внешнем замыкании .
Закрытие переживает время выполнения функции, которая породила его. Если другие функции выходят из закрытия / scope , в котором они определены (например, как возвращаемые значения), они будут продолжать ссылаться на это закрытие .
function example(closure) {
// define somevariable to live in the closure of example
var somevariable = 'unchanged';
return {
change_to: function(value) {
somevariable = value;
},
log: function(value) {
console.log('somevariable of closure %s is: %s',
closure, somevariable);
}
}
}
closure_one = example('one');
closure_two = example('two');
closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();
somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
console.log
. Если кого-то интересует, есть еще: developer.mozilla.org/en-US/docs/DOM/…
– Flash
4 January 2013 в 04:59
var
).
– Thomas Eding
18 March 2015 в 04:38
Функция в JavaScript - это не просто ссылка на набор инструкций (как на языке C), но также включает скрытую структуру данных, которая состоит из ссылок на все нелокальные переменные, которые она использует (захваченные переменные). Такие функции из двух частей называются замыканиями. Каждая функция в JavaScript может считаться замыканием.
Закрытие - это функции с состоянием. Это несколько похоже на «это» в том смысле, что «это» также предоставляет состояние для функции, но функция, а «это» - отдельные объекты («это» - просто причудливый параметр и единственный способ привязать его к функция - создать закрытие). Хотя «это» и функция всегда живут отдельно, функция не может быть отделена от ее закрытия, а язык не предоставляет никаких средств для доступа к захваченным переменным.
Поскольку все эти внешние переменные, на которые ссылается лексически вложенная функция, фактически являются локальными переменные в цепочке его лексически охватывающих функций (глобальные переменные можно считать локальными переменными некоторой корневой функции), и каждое отдельное выполнение функции создает новые экземпляры ее локальных переменных, из этого следует, что каждое выполнение функции, возвращающей ( или иным способом передать его, например, зарегистрировать его как обратный вызов), вложенная функция создает новое закрытие (с его собственным потенциально уникальным набором ссылочных нелокальных переменных, которые представляют его контекст выполнения).
Кроме того, он должен что локальные переменные в JavaScript создаются не в фрейме стека, а в куче и уничтожаются только тогда, когда никто не ссылается на них. Когда функция возвращается, ссылки на ее локальные переменные уменьшаются, но они все равно могут быть не равными нулю, если во время текущего выполнения они стали частью замыкания и по-прежнему ссылаются на его лексически вложенные функции (что может произойти, только если ссылки на эти вложенные функции были возвращены или иным образом переданы на некоторый внешний код).
Пример:
function foo (initValue) {
//This variable is not destroyed when the foo function exits.
//It is 'captured' by the two nested functions returned below.
var value = initValue;
//Note that the two returned functions are created right now.
//If the foo function is called again, it will return
//new functions referencing a different 'value' variable.
return {
getValue: function () { return value; },
setValue: function (newValue) { value = newValue; }
}
}
function bar () {
//foo sets its local variable 'value' to 5 and returns an object with
//two functions still referencing that local variable
var obj = foo(5);
//Extracting functions just to show that no 'this' is involved here
var getValue = obj.getValue;
var setValue = obj.setValue;
alert(getValue()); //Displays 5
setValue(10);
alert(getValue()); //Displays 10
//At this point getValue and setValue functions are destroyed
//(in reality they are destroyed at the next iteration of the garbage collector).
//The local variable 'value' in the foo is no longer referenced by
//anything and is destroyed too.
}
bar();
В информатике закрытие является функцией вместе со средой ссылок для нелокальных имен (свободных переменных) этой функции.
blockquote>Технически, в JavaScript каждая функция является замыканием. Он всегда имеет доступ к переменным, определенным в области окружения.
Поскольку конструкция, определяющая область видимости в JavaScript, является функцией, а не кодовым блоком, как на многих других языках, что мы обычно подразумеваем под замыканием в JavaScript - это функция, работающая с нелокальными переменными, определенная в уже выполненной окружающей функции.
Закрытия часто используются для создания функций с некоторыми скрытыми личными данными (но это не всегда так).
var db = (function() { // Create a hidden object, which will hold the data // it's inaccessible from the outside. var data = {}; // Make a function, which will provide some access to the data. return function(key, val) { if (val === undefined) { return data[key] } // Get else { return data[key] = val } // Set } // We are calling the anonymous surrounding function, // returning the above inner function, which is a closure. })(); db('x') // -> undefined db('x', 1) // Set x to 1 db('x') // -> 1 // It's impossible to access the data object itself. // We are able to get or set individual it.
ems
В приведенном выше примере используется анонимная функция, которая была выполнена один раз. Но этого не должно быть. Его можно назвать (например,
mkdb
) и выполнить позже, генерируя функцию базы данных каждый раз при ее вызове. Каждая сгенерированная функция будет иметь свой собственный скрытый объект базы данных. Другим примером использования замыканий является то, что мы не возвращаем функцию, а объект, содержащий несколько функций для разных целей, каждая из которых имеет доступ к тем же данным.
Автор Closures очень хорошо объяснил замыкания, объясняя причину, по которой они нам нужны, а также объясняет LexicalEnvironment, который необходим для понимания замыканий. Вот сводка:
Что делать, если к переменной обращаются, но она не является локальной? Как здесь:
В этом случае интерпретатор находит переменную во внешнем LexicalEnvironment
объекте.
Процесс состоит из двух шагов:
Когда функция создается, он получает скрытое свойство с именем [[Scope]], которое ссылается на текущую LexicalEnvironment.
Если переменная читается, но не может быть
Вложенные функции
Функции могут быть вложены друг в друга, образуя цепочку Лексических сред, которые также можно назвать цепочкой областей видимости.
Таким образом, функция g имеет доступ к g, a и f.
Замыкания
Вложенная функция может продолжаться
Разметка LexicalEnvironments:
g32]
Как мы видим, this.say
является свойством в объекте пользователя, поэтому он продолжает жить после завершения пользователем.
И если вы помните, когда this.say
создан, он ( как каждая функция) получает внутреннюю ссылку this.say.[[Scope]]
на текущий LexicalEnvironment. Итак, LexicalEnvironment текущего исполнения пользователя остается в памяти. Все переменные пользователя также являются его свойствами, поэтому они также тщательно хранятся, а не как обычно.
Все дело в том, чтобы убедиться, что если внутренняя функция хочет получить доступ к внешней переменной в будущем, .
Подводя итог:
Это называется замыканием.
first_calculator
является объектом (а не функцией), вы не должны использовать круглые скобки в second_calculator = first_calculator;
, поскольку это назначение, а не вызов функции. Чтобы ответить на ваш вопрос, тогда будет только один вызов make_calculator, поэтому будет сделан только один калькулятор, и переменные first_calculator и second_calculator будут ссылаться на один и тот же калькулятор, поэтому ответы будут 3, 403, 4433, 44330.
– Matt
10 October 2016 в 09:49
Я все еще думаю, что Объяснение Google работает очень хорошо и кратким:
/*
* When a function is defined in another function and it
* has access to the outer function's context even after
* the outer function returns.
*
* An important concept to learn in JavaScript.
*/
function outerFunction(someNum) {
var someString = 'Hey!';
var content = document.getElementById('content');
function innerFunction() {
content.innerHTML = someNum + ': ' + someString;
content = null; // Internet Explorer memory leak for DOM reference
}
innerFunction();
}
outerFunction(1);
[/g3]
* AC # вопрос
ОК, 6-летний вентилятор закрытия. Вы хотите услышать простейший пример закрытия?
Давайте представим следующую ситуацию: водитель сидит в машине. Этот автомобиль находится внутри самолета. Самолет находится в аэропорту. Способность водителя получать доступ к вещам вне его автомобиля, но внутри самолета, даже если этот самолет выходит из аэропорта, является закрытием. Вот и все. Когда вам исполнится 27, посмотрите на подробное объяснение или в приведенном ниже примере.
Вот как я могу преобразовать историю своего плана в код.
var plane = function (defaultAirport) {
var lastAirportLeft = defaultAirport;
var car = {
driver: {
startAccessPlaneInfo: function () {
setInterval(function () {
console.log("Last airport was " + lastAirportLeft);
}, 2000);
}
}
};
car.driver.startAccessPlaneInfo();
return {
leaveTheAirport: function (airPortName) {
lastAirportLeft = airPortName;
}
}
}("Boryspil International Airport");
plane.leaveTheAirport("John F. Kennedy");
Хорошо, разговаривая с 6-летним ребенком, я, возможно, буду использовать следующие ассоциации.
Представьте, вы играете со своими маленькими братьями и сестрами во всем доме, и вы двигаясь с вашими игрушками, и привел некоторых из них в комнату вашего старшего брата. Через некоторое время ваш брат вернулся из школы и пошел в свою комнату, и он заперся внутри, так что теперь вы не могли получить доступ к игрушкам, оставленным там прямо. Но вы могли бы постучать в дверь и спросить своего брата за игрушками. Это называется закрытием игрушек ; ваш брат сделал это для вас, и теперь он находится во внешнем области .
blockquote>Сравните с ситуацией, когда дверь была заперта сквозняком и никто внутри (общий выполнение функции), а затем происходит местный пожар и сжигание комнаты (сборщик мусора: D), а затем была построена новая комната, и теперь вы можете оставить там другие игрушки (новый экземпляр функции), но никогда не получите одинаковые игрушки, которые были оставлены в первом экземпляре комнаты.
Для продвинутого ребенка я бы поставил что-то вроде следующего. Это не идеально, но это заставляет вас почувствовать, что это такое:
function playingInBrothersRoom (withToys) { // We closure toys which we played in the brother's room. When he come back and lock the door // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him. var closureToys = withToys || [], returnToy, countIt, toy; // Just another closure helpers, for brother's inner use. var brotherGivesToyBack = function (toy) { // New request. There is not yet closureToys on brother's hand yet. Give him a time. returnToy = null; if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it. for ( countIt = closureToys.length; countIt; countIt--) { if (closureToys[countIt - 1] == toy) { returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!'; break; } } returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.'; } else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room. returnToy = 'Behold! ' + closureToys.join(', ') + '.'; closureToys = []; } else { returnToy = 'Hey, lil shrimp, I gave you everything!'; } console.log(returnToy); } return brotherGivesToyBack; } // You are playing in the house, including the brother's room. var toys = ['teddybear', 'car', 'jumpingrope'], askBrotherForClosuredToy = playingInBrothersRoom(toys); // The door is locked, and the brother came from the school. You could not cheat and take it out directly. console.log(askBrotherForClosuredToy.closureToys); // Undefined // But you could ask your brother politely, to give it back. askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear askBrotherForClosuredToy('ball'); // The brother would not be able to find it. askBrotherForClosuredToy(); // The brother gives you all the rest askBrotherForClosuredToy(); // Nothing left in there
Как вы можете видеть, игрушки, оставшиеся в комнате, все еще доступны через брата, и независимо от того, закрыта ли комната , Здесь jsbin , чтобы поиграть с ним.
Я написал сообщение в блоге некоторое время назад, объясняя закрытие. Вот что я сказал о закрытии с точки зрения того, зачем вам это нужно.
Закрытие - это способ, чтобы функция имела постоянные, частные переменные - то есть переменные, которые только одна функция знает о , где он может отслеживать информацию с предыдущих времен, когда она была запущена.
blockquote>В этом смысле они позволяют функции действовать как объект с частными атрибутами.
Full post:
devError = emailError("devinrhode2@googmail.com", errorString)
, а затем иметь собственную пользовательскую версию общей функции emailError?
– Devin G Rhode
31 July 2011 в 07:42
Закрытие - это когда внутренняя функция имеет доступ к переменным во внешней функции. Это, наверное, самое простое однострочное объяснение, которое вы можете получить для закрытия.
Ты спишь и приглашаешь Дэн. Вы говорите Дэну, чтобы он привел один контроллер XBox.
Дэн приглашает Павла. Дэн просит Павла взять одного контроллера. Сколько контроллеров было доставлено на вечеринку?
function sleepOver(howManyControllersToBring) {
var numberOfDansControllers = howManyControllersToBring;
return function danInvitedPaul(numberOfPaulsControllers) {
var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
return totalControllers;
}
}
var howManyControllersToBring = 1;
var inviteDan = sleepOver(howManyControllersToBring);
// The only reason Paul was invited is because Dan was invited.
// So we set Paul's invitation = Dan's invitation.
var danInvitedPaul = inviteDan(howManyControllersToBring);
alert("There were " + danInvitedPaul + " controllers brought to the party.");