Как бы вы объяснили закрытие JavaScript для кого-то, у кого есть знания о концепциях, из которых они состоят (например, функции, переменные и т.п.), Но не понимают самих замыканий?
Я видел пример схемы, приведенный в Википедии, но, к сожалению, это не помогло.
Представлено Morris on Tue, 2006-02-21 10:19. Сообщество отредактировано с тех пор.
На этой странице объясняется закрытие, чтобы программист мог их понять - используя рабочий код JavaScript. Это не для гуру или функциональных программистов.
Закрытие не сложно понять, как только основное понятие будет зашито. Однако их невозможно понять, прочитав теоретические или академически обоснованные объяснения!
Эта статья предназначена для программистов с некоторым опытом программирования на основном языке и может читать следующую функцию JavaScript:
function sayHello(name) {
var text = 'Hello ' + name;
var say = function() { console.log(text); }
say();
}
sayHello('Joe');
Когда функция (foo) объявляет другие функции (bar и baz), семейство локальных переменных, созданных в foo, не разрушается, когда функция завершается. Переменные просто становятся невидимыми для внешнего мира. Таким образом, Foo может хитро возвращать панель функций и baz, и они могут продолжать читать, писать и общаться друг с другом через это закрытое семейство переменных ("закрытие"), с которыми никто не может вмешиваться, даже тот, кто называет foo снова в будущем.
Закрытие - один из способов поддержки первоклассных функций; это выражение, которое может ссылаться на переменные в пределах своей области (когда оно было объявлено ранее), назначаться переменной, передаваться как аргумент функции или возвращаться как результат функции.
Следующий код возвращает ссылку на функцию:
function sayHello2(name) {
var text = 'Hello ' + name; // Local variable
var say = function() { console.log(text); }
return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"
Большинство программистов из JavaScript поймут, как ссылка на функцию возвращается переменной (say2
) в приведенном выше коде. Если вы этого не сделаете, вам нужно взглянуть на это, прежде чем вы сможете узнать о закрытии. Программист, использующий C, будет думать о том, что функция возвращает указатель на функцию и что переменные say
и say2
являются указателями на функцию.
Существует критическая разница между C-указателем на функцию и ссылкой JavaScript на функцию. В JavaScript вы можете рассматривать переменную ссылочной функции как имеющую как указатель на функцию, так и скрытый указатель на закрытие.
Вышеприведенный код имеет закрытие, потому что анонимная функция function() { console.log(text); }
function() { console.log(text); }
объявляется внутри другой функции, sayHello2()
в этом примере. В JavaScript, если вы используете ключевое слово function
внутри другой функции, вы создаете закрытие.
В C и большинстве других общих языков, после возвращения функции, все локальные переменные больше недоступны, поскольку стек стека уничтожается.
В JavaScript, если вы объявляете функцию внутри другой функции, локальные переменные внешней функции могут оставаться доступными после возвращения из нее. Это показано выше, потому что мы вызываем функцию say2()
после того, как мы вернулись из sayHello2()
. Обратите внимание, что код, который мы называем, ссылается на переменный text
, который является локальной переменной функции sayHello2()
.
function() { console.log(text); } // Output of say2.toString();
Глядя на результат say2.toString()
, мы видим, что код относится к переменному text
. Анонимная функция может ссылаться на text
который содержит значение 'Hello Bob'
потому что локальные переменные sayHello2()
были тайно сохранены в закрытии.
Гениальность заключается в том, что в JavaScript ссылка на функцию также имеет секретную ссылку на закрытие, в которой она была создана, - подобно тому, как делегаты являются указателем метода плюс секретная ссылка на объект.
По какой-то причине закрытие кажется очень трудно понять, когда вы читаете о них, но когда вы видите некоторые примеры, становится ясно, как они работают (мне потребовалось некоторое время). Я рекомендую внимательно изучить примеры, пока вы не поймете, как они работают. Если вы начнете использовать закрытие без полного понимания того, как они работают, вы скоро создадите очень странные ошибки!
Этот пример показывает, что локальные переменные не копируются - они хранятся по ссылке. Это похоже на то, что стек кадров остается в памяти даже после того, как существует внешняя функция!
function say667() {
// Local variable that ends up within closure
var num = 42;
var say = function() { console.log(num); }
num++;
return say;
}
var sayNumber = say667();
sayNumber(); // logs 43
Все три глобальных функции имеют общую ссылку на одно и то же закрытие, потому что все они объявлены в течение одного вызова setupSomeGlobals()
.
var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 42;
// Store some references to functions as global variables
gLogNumber = function() { console.log(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5
var oldLog = gLogNumber;
setupSomeGlobals();
gLogNumber(); // 42
oldLog() // 5
Три функции имеют общий доступ к одному и тому же закрытию - локальные переменные setupSomeGlobals()
когда три функции определены.
Обратите внимание, что в приведенном выше примере, если вы снова вызываете setupSomeGlobals()
, создается новое замыкание (stack-frame!). Старые gLogNumber
, gIncreaseNumber
, gSetNumber
перезаписываются новыми функциями, которые имеют новое замыкание. (В JavaScript, всякий раз, когда вы объявляете функцию внутри другой функции, внутренние функции снова воссоздаются каждый раз, когда вызывается внешняя функция.)
Этот пример показывает, что замыкание содержит любые локальные переменные, которые были объявлены внутри внешней функции до ее выхода. Обратите внимание, что переменная alice
фактически объявляется после анонимной функции. Анонимная функция объявлена первая, и когда эта функция вызывается он может получить доступ к alice
переменному, поскольку alice
находится в той же области (JavaScript, делает переменные Подъемно). Также sayAlice()()
просто вызывает sayAlice()
функции, возвращаемый sayAlice()
- это точно так же, как и ранее, но без временной переменной.
function sayAlice() {
var say = function() { console.log(alice); }
// Local variable that ends up within closure
var alice = 'Hello Alice';
return say;
}
sayAlice()();// logs "Hello Alice"
Tricky: также обратите внимание, say
переменная say
также находится внутри замыкания, и к ней может быть sayAlice()
любая другая функция, которая может быть объявлена в sayAlice()
, или к ней можно было бы получить рекурсивно внутри внутренней функции.
Это для всех людей настоящая магия, поэтому вам нужно это понять. Будьте очень осторожны, если вы определяете функцию внутри цикла: локальные переменные из замыкания могут не действовать так, как вы могли бы сначала подумать.
Вам нужно понять функцию "переменной подъема" в Javascript, чтобы понять этот пример.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + i;
result.push( function() {console.log(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// Using j only to help prevent confusion -- could use i.
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
testList() //logs "item2 undefined" 3 times
Строка result.push( function() {console.log(item + ' ' + list[i])}
добавляет ссылку на анонимную функцию три раза в массив результатов. Если вы не знакомы с анонимными функциями, это нравится:
pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);
Обратите внимание, что при запуске примера "item2 undefined"
записывается три раза! Это связано с тем, что, как и в предыдущих примерах, существует только одно замыкание для локальных переменных для buildList
(которые являются result
, i
и item
). Когда анонимные функции вызывают в строке fnlist[j]()
; все они используют одно и то же единственное замыкание, и они используют текущее значение для i
и item
пределах одного замыкания (где i
имеет значение 3
потому что цикл завершен, а item
имеет значение 'item2'
). Обратите внимание, что мы индексируем из 0, поэтому item
имеет значение item2
. И i++ увеличит i
до значения 3
.
Это может быть полезно, чтобы увидеть, что происходит, когда декларация на уровне блоков переменной item
используется (через let
ключевое слово) вместо функции в области видимости объявления переменной через var
ключевое слово. Если это изменение сделано, то каждая анонимная функция в result
массива имеет свое закрытие; при выполнении примера вывод выглядит следующим образом:
item0 undefined
item1 undefined
item2 undefined
Если переменная i
также определена с использованием let
вместо var
, то выход:
item0 1
item1 2
item2 3
В этом последнем примере каждый вызов основной функции создает отдельное закрытие.
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
console.log('num: ' + num +
'; anArray: ' + anArray.toString() +
'; ref.someVar: ' + ref.someVar + ';');
}
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;
Если все кажется совершенно неясным, лучше всего поиграть с примерами. Чтение объяснений намного сложнее, чем понимание примеров. Мои объяснения закрытий и стековых фреймов и т.д. Не являются технически правильными - это грубые упрощения, призванные помочь понять. После того, как основная идея будет решена, вы можете получить детали позже.
function
внутри другой функции, используется закрытие.eval()
внутри функции, используется закрытие. Текст, который вы eval
может ссылаться на локальные переменные функции, и в eval
вы можете даже создавать новые локальные переменные с помощью eval('var foo = …')
new Function(…)
(конструктор функции) внутри функции, она не создает закрытие. (Новая функция не может ссылаться на локальные переменные внешней функции.)myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));
), он не будет работать, если myFunction
является закрытием ( конечно, вы даже не подумали бы о замене строк исходного кода во время выполнения, но...).Если вы только что узнали о закрытии (здесь или где-нибудь еще!), То меня интересует любая обратная связь от вас о любых изменениях, которые вы могли бы предложить, чтобы сделать эту статью более ясной. Отправить сообщение для morrisjohns.com(morris_closure @). Обратите внимание, что я не гуру на JavaScript - ни на закрытии.
Оригинальный пост Морриса можно найти в Интернет-архиве.
Всякий раз, когда вы видите ключевое слово 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
.
То есть замыкание. Функция не должна возвращаться, чтобы называться замыканием. Простое обращение к переменным за пределами вашей непосредственной лексической области создает закрытие.
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
не удается. Тем не менее, лексическая среда, которую функция будет переносить как [[Scope]] (и, в конечном счете, использовать в качестве основы для своей собственной лексической среды при вызове), определяется при выполнении оператора, определяющего функцию. Это означает , что функция закрывает за полное содержание исполняющей сферы, независимо от того, каких значений он на самом деле относится к и выходит ли объем. Пожалуйста, смотрите разделы 13.2 и 10 в спецификации
ПРЕДИСЛОВИЕ: этот ответ был написан, когда вопрос был:
Как и старый Альберт, он сказал: "Если вы не можете объяснить это шестилетнему ребенку, вы сами этого не понимаете". Хорошо, я попытался объяснить закрытие JS 27-летнему другу и полностью потерпел неудачу.
Может ли кто-нибудь подумать, что мне 6, и странно интересуется этим вопросом?
Я почти уверен, что я был одним из тех людей, которые пытались взять начальный вопрос буквально. С тех пор этот вопрос несколько раз мутировал, поэтому мой ответ теперь может казаться невероятно глупым и неуместным. Надеюсь, общая идея этой истории остается для некоторых интересной.
Я большой поклонник аналогии и метафоры при объяснении сложных понятий, поэтому позвольте мне попробовать свои силы в истории.
Давным-давно:
Была принцесса...
function princess() {
Она жила в прекрасном мире, полном приключений. Она встретила своего принца Шарля, поехала вокруг своего мира на единорога, сражалась с драконами, встречалась с говорящими животными и многими другими фантастическими вещами.
var adventures = [];
function princeCharming() { /* ... */ }
var unicorn = { /* ... */ },
dragons = [ /* ... */ ],
squirrel = "Hello!";
/* ... */
Но ей всегда приходилось возвращаться в свой скучный мир хлопот и взрослых.
return {
И она часто рассказывала им о своем последнем удивительном приключении в качестве принцессы.
story: function() {
return adventures[adventures.length - 1];
}
};
}
Но все, что они увидели, это маленькая девочка...
var littleGirl = princess();
... рассказывая истории о магии и фантазии.
littleGirl.story();
И хотя взрослые знали о настоящих принцессах, они никогда не поверили бы в единорогов или драконов, потому что они никогда их не увидели. Взрослые сказали, что они существуют только внутри воображения маленькой девочки.
Но мы знаем истинную истину; что маленькая девочка с принцессой внутри...
... на самом деле принцесса с маленькой девочкой внутри.
story()
, которая является единственным интерфейсом, littleGirl
экземпляр littleGirl
предоставляет миру магии.
story
- это закрытие, но если бы код был var story = function() {}; return story;
тогда littleGirl
будет закрытием. По крайней мере, такое впечатление, которое я получаю от использования MDN «закрытых» методов с замыканиями : «Эти три публичные функции являются замыканиями, которые используют одну и ту же среду».
Принимая этот вопрос всерьез, мы должны выяснить, что типичный 6-летний человек способен когнитивно, хотя, по общему признанию, тот, кто интересуется JavaScript, не так типичен.
О развитии детства: от 5 до 7 лет говорится:
Ваш ребенок сможет следовать двухэтапным направлениям. Например, если вы скажете своему ребенку: "Идите на кухню и возьмите мешок для мусора", они смогут запомнить это направление.
Мы можем использовать этот пример для объяснения замыканий следующим образом:
Кухня - это закрытие, в котором есть локальная переменная, называемая
trashBags
. Внутри кухни есть функцияgetTrashBag
которая получает один мешок для мусора и возвращает его.
Мы можем кодировать это в JavaScript следующим образом:
function makeKitchen() {
var trashBags = ['A', 'B', 'C']; // only 3 at first
return {
getTrashBag: function() {
return trashBags.pop();
}
};
}
var kitchen = makeKitchen();
console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A
Дальнейшие моменты, объясняющие, почему замыкания интересны:
makeKitchen()
, создается новое замыкание со своими отдельными trashBags
.trashBags
является локальной для каждой кухни и недоступна снаружи, но внутренняя функция свойства getTrashBag
имеет к ней доступ.getTrashBag
функции getTrashBag
делает это здесь.Мне нужно знать, сколько раз нажата кнопка и что-то делать на каждом третьем клике...
// Declare counter outside event handler 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 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 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 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
- это замыкание. Я прав?
Это попытка прояснить несколько (возможных) недоразумений о замыканиях, которые появляются в некоторых других ответах.
ОК, 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");
Закрытие очень похоже на объект. Он создается при каждом вызове функции.
Объем закрытия в JavaScript является лексическим, что означает, что все, что содержится в функции, к которой принадлежит замыкание, имеет доступ к любой переменной, которая в ней.
Переменная содержится в закрытии, если вы
var foo=1;
или жеvar foo;
Если внутренняя функция (функция, содержащаяся внутри другой функции) обращается к такой переменной, не определяя ее в своей собственной области с помощью var, она изменяет содержимое переменной во внешнем закрытии.
Закрытие переживает время выполнения функции, которая породила его. Если другие функции выходят из замыкания/области действия, в которой они определены (например, как возвращаемые значения), они будут продолжать ссылаться на это закрытие.
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/…
var
).
Следующий простой пример охватывает все основные моменты закрытия JavaScript. *
Вот фабрика, которая производит калькуляторы, которые могут добавлять и умножать:
function make_calculator() {
var n = 0; // this calculator stores a single number n
return {
add: function(a) {
n += a;
return n;
},
multiply: function(a) {
n *= a;
return n;
}
};
}
first_calculator = make_calculator();
second_calculator = make_calculator();
first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400
first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000
Ключевой момент: Каждый вызов make_calculator
создает новую локальную переменную n
, которая продолжает быть полезными этим калькулятором add
и multiply
функции долго после make_calculator
возвращается.
Если вы знакомы с фреймами стека, эти калькуляторы кажутся странными: как они могут продолжать доступ к n
после make_calculator
? Ответ заключается в том, чтобы предположить, что JavaScript не использует "фреймы стека", но вместо этого использует "кучевые кадры", которые могут сохраняться после вызова функции, который заставлял их возвращаться.
Внутренние функции, такие как add
и multiply
, которые передают переменные, объявленные во внешней функции ** называются замыканиями.
Это почти все, что нужно для закрытия.
* Например, он охватывает все пункты статьи "Закрытия для чайников", приведенные в другом ответе, за исключением примера 6, который просто показывает, что переменные могут использоваться до их объявления, что является хорошим фактом, но совершенно не связанным с закрытием.Он также охватывает все точки принятого ответа, за исключением точек (1), которые копируют свои аргументы в локальные переменные (именованные аргументы функции) и (2) что копирование чисел создает новый номер, но копирование ссылки на объект дает вам другую ссылку на тот же объект.Они также хорошо знают, но снова полностью не связаны с закрытием.Он также очень похож на пример в этом ответе, но немного короче и менее абстрактен.Он не охватывает точку этого ответа или этого комментария, а это значит, что JavaScript затрудняет подключить текущее значение переменной цикла к вашей внутренней функции: шаг "запирания" может выполняться только с помощью вспомогательной функции, которая заключает вашей внутренней функции и вызывается на каждой итерации цикла.(Строго говоря, внутренняя функция обращается к копии вспомогательной функции переменной, а не к чему-либо подключенному.) Опять же, очень полезно при создании закрытий, но не в части того, что такое закрытие или как оно работает.Существует дополнительная путаница из-за того, что замыкания работают по-разному в функциональных языках, таких как ML, где переменные привязаны к значениям, а не к пространству хранения, обеспечивая постоянный поток людей, которые понимают закрытие способом (а именно "подключаемым" способом), который просто неверный для JavaScript, где переменные всегда привязаны к пространству хранения и никогда не относятся к значениям.
** Любая внешняя функция, если несколько вложенных или даже в глобальном контексте, как ясно указывает этот ответ.
first_calculator
является объектом (а не функцией), вы не должны использовать скобки в second_calculator = first_calculator;
, поскольку это присваивание, а не вызов функции. Чтобы ответить на ваш вопрос, будет только один вызов make_calculator, поэтому будет создан только один калькулятор, а переменные first_calculator и second_calculator будут ссылаться на один и тот же калькулятор, поэтому ответы будут 3, 403, 4433, 44330.
Я написал сообщение в блоге некоторое время назад, объясняя закрытие. Вот что я сказал о закрытии с точки зрения того, зачем вам это нужно.
Закрытие - это способ, позволяющий функции иметь постоянные частные переменные - то есть переменные, о которых знает только одна функция, где она может отслеживать информацию с предыдущих времен, когда она была запущена.
В этом смысле они позволяют функции действовать как объект с частными атрибутами.
Полное сообщение:
devError = emailError("[email protected]", errorString)
а затем иметь свою собственную версию общей функции emailError?
Как я объясню это шестилетнему ребенку:
Вы знаете, как взрослые могут владеть домом, и они называют его домом? Когда у мамы есть ребенок, ребенок действительно ничего не владеет, верно? Но его родители владеют домом, поэтому всякий раз, когда кто-то спрашивает ребенка "Где твой дом?", Он может ответить "на этот дом!" И указать на дом своих родителей. "Закрытие" - это способность ребенка всегда (даже если за границей) быть в состоянии сказать, что у него есть дом, хотя он действительно является родителем, который владеет домом.
Я все еще думаю, что объяснение Google работает очень хорошо и кратким:
/*
* When a function is defined in another function and it
* has access to the outer function 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);
* AС# вопрос
В информатике закрытие является функцией вместе со средой ссылок для нелокальных имен (свободных переменных) этой функции.
Технически, в JavaScript каждая функция является закрытием. Он всегда имеет доступ к переменным, определенным в окружении.
Поскольку конструкция, определяющая область видимости в JavaScript, является функцией, а не кодовым блоком, как на многих других языках, то, что мы обычно подразумеваем при закрытии в JavaScript, является функцией, работающей с нелокальными переменными, определенными в уже выполненной окружающей функции.
Закрытие часто используется для создания функций с некоторыми скрытыми частными данными (но это не всегда так).
var db = (function() {
// Create a hidden object, which will hold the data
// it 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 impossible to access the data object itself.
// We are able to get or set individual it.
Эмс
В приведенном выше примере используется анонимная функция, которая была выполнена один раз. Но этого не должно быть. Его можно назвать (например, mkdb
) и выполнить позже, генерируя функцию базы данных каждый раз при ее вызове. Каждая сгенерированная функция будет иметь свой собственный скрытый объект базы данных. Другой пример использования замыканий - это когда мы не возвращаем функцию, а объект, содержащий несколько функций для разных целей, каждая из которых имеет доступ к тем же данным.
Я стараюсь лучше учиться на тестах GOOD/BAD. Мне нравится видеть рабочий код, за которым следует нерабочий код, с которым кто-то может столкнуться. Я собрал 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
переменную SAME из родительской области, где она была определена вместо 10 разных из 10 различных областей, таких как первый пример. Конечным результатом является то, что все 10 функций возвращают одну и ту же переменную из той же области.
После того, как цикл завершен, и index
был изменен, конечное значение равно 10, поэтому каждая функция, добавленная в массив, возвращает значение единственной index
переменной, которая теперь установлена в 10.
CLOSURES DONE RIGHT
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9CLOSURES DONE WRONG
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
let
для var
исправляет разницу.
Я собрал интерактивный учебник по 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
Дети всегда будут помнить секреты, которые они поделили с родителями, даже после того, как их родители ушли. Это то, что закрывает для функций.
Секреты функций JavaScript - это частные переменные
var parent = function() {
var name = "Mary"; // secret
}
Каждый раз, когда вы вызываете его, создается локальная переменная "name" и имя "Mary". И каждый раз, когда функция выходит из переменной, теряется и имя забывается.
Как вы можете догадаться, поскольку переменные заново создаются каждый раз при вызове функции, и никто не узнает их, должно быть секретное место, где они хранятся. Его можно было бы назвать Тайной Палатой или стек или локальный охват, но это не имеет большого значения. Мы знаем, что они где-то скрыты в памяти.
Но в JavaScript есть особая вещь, что функции, созданные внутри других функций, также могут знать локальные переменные своих родителей и сохранять их до тех пор, пока они живут.
var parent = function() {
var name = "Mary";
var child = function(childName) {
// I can also see that "name" is "Mary"
}
}
Итак, пока мы находимся в родительском -function, он может создавать одну или несколько дочерних функций, которые разделяют секретные переменные из секретного места.
Но печально то, что если ребенок также является частной переменной своей родительской функции, он также погибнет, когда родитель закончится, и секреты умрут вместе с ними.
Чтобы жить, ребенок должен уйти до того, что он слишком поздно
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"
Это все, что нужно сказать.
Я не понимаю, почему ответы здесь настолько сложны.
Вот закрытие:
var a = 42;
function b() { return a; }
Да. Вы, вероятно, используете это много раз в день.
Нет никаких оснований полагать, что закрытие - это сложный дизайн для решения конкретных проблем. Нет, закрытие - это просто использование переменной, которая исходит из более высокой области видимости с точки зрения того, где была объявлена функция (не запускается).
Теперь то, что он позволяет вам сделать, может быть более впечатляющим, см. Другие ответы.
a
. На мой взгляд, удивительно то , что объект сфера JS эффективно обеспечивает как свойство , а не постоянной. a
И вы заметите это важное поведение, только если измените его, как return a++;
Пример для первой точки dlaliberte:
Закрытие создается не только при возврате внутренней функции. Фактически, закрывающая функция вообще не нуждается в возврате. Вместо этого вы можете назначить свою внутреннюю функцию переменной во внешней области или передать ее в качестве аргумента другой функции, где ее можно будет использовать немедленно. Поэтому закрытие закрывающей функции, вероятно, уже существует в момент вызова функции-приложения, поскольку любая внутренняя функция имеет доступ к ней, как только она вызывается.
var i;
function foo(x) {
var tmp = 3;
i = function (y) {
console.log(x + y + (++tmp));
}
}
foo(2);
i(3);
Я знаю, что уже есть много решений, но я думаю, что этот небольшой и простой скрипт может быть полезен для демонстрации концепции:
// 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
Вы спите, и вы приглашаете Дэн. Вы говорите Дэну, чтобы он привел один контроллер 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 invitation = Dan invitation.
var danInvitedPaul = inviteDan(howManyControllersToBring);
alert("There were " + danInvitedPaul + " controllers brought to the party.");
Закрытие - это то, где внутренняя функция имеет доступ к переменным в своей внешней функции. Это, вероятно, самое простое однострочное объяснение, которое вы можете получить для закрытия.
Автор Closures очень хорошо объяснил закрытие, объясняя причину, по которой мы нуждаемся в них, а также объясняем LexicalEnvironment, которая необходима для понимания закрытий.
Вот резюме:
Что делать, если переменная доступна, но она не является локальной? Как здесь:
В этом случае интерпретатор находит переменную во внешнем объекте LexicalEnvironment
.
Процесс состоит из двух этапов:
Когда функция создается, она получает скрытое свойство с именем [[Scope]], которое ссылается на текущую LexicalEnvironment.
Если переменная читается, но ее нельзя найти нигде, генерируется ошибка.
Вложенные функции
Функции могут вставляться внутри друг друга, образуя цепочку Лексических сред, которые также можно назвать цепочкой областей видимости.
Таким образом, функция g имеет доступ к g, a и f.
Затворы
Вложенная функция может продолжать жить после завершения внешней функции:
Маркировка LexicalEnvironments:
Как мы видим, this.say
является свойством в пользовательском объекте, поэтому он продолжает жить после завершения пользователем.
И если вы помните, когда this.say
создается, он (как каждая функция) получает внутреннюю ссылку this.say.[[Scope]]
в текущую LexicalEnvironment. Итак, LexicalEnvironment текущего исполнения пользователя остается в памяти. Все переменные пользователя также являются его свойствами, поэтому их также тщательно хранят, а не как обычно.
Все дело в том, чтобы убедиться, что если внутренняя функция хочет получить доступ к внешней переменной в будущем, она сможет это сделать.
Обобщить:
Это называется замыканием.
Функции JavaScript могут получить доступ к следующим функциям:
Если функция обращается к своей среде, то функция является закрытием.
Обратите внимание, что внешние функции не требуются, хотя они предлагают преимущества, которые я здесь не обсуждаю. Получая доступ к данным в своей среде, закрытие сохраняет эти данные. В подслучае внешних/внутренних функций внешняя функция может создавать локальные данные и, в конечном счете, выходить, и, тем не менее, если какая-либо внутренняя функция выживает после выхода внешней функции, тогда внутренняя функция сохраняет внешние данные внешней функции в живых.
Пример закрытия, использующего глобальную среду:
Представьте, что события кнопки "Переполнение стека" и "Голос-вниз" реализованы как закрытие, voteUp_click и voteDown_click, которые имеют доступ к внешним переменным isVotedUp и isVotedDown, которые определены глобально. (Для простоты я имею в виду кнопки StateOverflow Question Vote, а не массив кнопок Answer Vote).
Когда пользователь нажимает кнопку 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
}
Все четыре из этих функций являются закрытием, поскольку все они получают доступ к своей среде.
Будучи отцом 6-летнего ребенка, который в настоящее время обучает маленьких детей (и относительного новичка для кодирования без формального образования, так что исправления будут необходимы), я думаю, что урок будет лучше всего играть в практической игре. Если 6-летний человек готов понять, что такое закрытие, тогда они уже достаточно взрослые, чтобы пройти сами. Я бы предложил вставить код в jsfiddle.net, немного объяснив, и оставив их в покое, чтобы придумать уникальную песню. Пояснительный текст ниже, вероятно, более подходит для 10-летнего возраста.
function sing(person) {
var firstPart = "There was " + person + " who swallowed ";
var fly = function() {
var creature = "a fly";
var result = "Perhaps she'll die";
alert(firstPart + creature + "\n" + result);
};
var spider = function() {
var creature = "a spider";
var result = "that wiggled and jiggled and tickled inside her";
alert(firstPart + creature + "\n" + result);
};
var bird = function() {
var creature = "a bird";
var result = "How absurd!";
alert(firstPart + creature + "\n" + result);
};
var cat = function() {
var creature = "a cat";
var result = "Imagine That!";
alert(firstPart + creature + "\n" + result);
};
fly();
spider();
bird();
cat();
}
var person="an old lady";
sing(person);
ИНСТРУКЦИИ
ДАННЫЕ: Данные представляют собой совокупность фактов. Это могут быть цифры, слова, измерения, наблюдения или даже просто описания вещей. Вы не можете прикоснуться к нему, почувствовать запах или попробовать его. Вы можете записать его, говорить и слышать. Вы можете использовать его для создания сенсорного запаха и вкуса с помощью компьютера. Это может быть полезно с помощью компьютера с использованием кода.
КОД: Все записи выше называются кодом. Он написан на JavaScript.
JAVASCRIPT: JavaScript - это язык. Как английский, французский или китайский языки. Существует множество языков, которые понимаются компьютерами и другими электронными процессорами. Для того чтобы JavaScript понимал компьютер, ему нужен интерпретатор. Представьте, что учитель, который говорит только по-русски, учит ваш класс в школе. Когда учитель говорит "все садятся", класс не поймет. Но, к счастью, у вас есть русский ученик в вашем классе, который говорит всем, что это означает, что "все садятся" - так вы все и делаете. Класс похож на компьютер, а русский ученик - переводчик. Для JavaScript наиболее распространенный интерпретатор называется браузером.
BROWSER: Когда вы подключаетесь к Интернету на компьютере, планшете или телефоне, чтобы посетить веб-сайт, вы используете браузер. Примеры, которые вы, возможно, знаете, - это Internet Explorer, Chrome, Firefox и Safari. Браузер может понять JavaScript и сообщить компьютеру, что ему нужно делать. Инструкции JavaScript называются функциями.
ФУНКЦИЯ: Функция в JavaScript похожа на фабрику. Это может быть небольшая фабрика с одной машиной внутри. Или он может содержать много других небольших фабрик, каждый из которых имеет множество машин, выполняющих разные рабочие места. На фабрике одежды реального времени у вас могут появиться полоски ткани и бобины нитки, а также футболки и джинсы. Наша фабрика JavaScript обрабатывает только данные, она не может шить, сверлить отверстие или расплавить металл. На нашем JavaScript заводские данные поступают и данные выводятся.
Все эти данные звучат немного скучно, но это действительно очень круто; у нас может быть функция, которая сообщает роботу, что делать на обед. Скажем, я приглашаю вас и вашего друга в мой дом. Вам больше нравятся куриные ножки, мне нравятся колбасы, ваш друг всегда хочет, чего вы хотите, а мой друг не ел мяса.
У меня нет времени ходить по магазинам, поэтому функция должна знать, что у нас есть в холодильнике, чтобы принимать решения. У каждого ингредиента есть другое время приготовления, и мы хотим, чтобы все было подано горячим роботом одновременно. Нам нужно предоставить функцию с данными о том, что нам нравится, функция может "разговаривать" с холодильником, а функция может управлять роботом.
Функция обычно имеет имя, круглые скобки и фигурные скобки. Как это:
function cookMeal() { /* STUFF INSIDE THE FUNCTION */ }
Обратите внимание, что /*...*/
и //
останавливают чтение кода браузером.
NAME: вы можете вызвать функцию практически любого слова, которое вы хотите. Пример "cookMeal" типичен для объединения двух слов, а второй - заглавной буквы в начале, но это необязательно. Он не может иметь места в нем, и он не может быть числом сам по себе.
PARENTHESES: "Круглые скобки" или ()
- это поле для письма на заводской двери функции JavaScript или почтовый ящик на улице для отправки пакетов информации на завод. Иногда почтовый ящик может быть отмечен, например, cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, и в этом случае вы знаете, какие данные вы должны предоставить.
BRACES: "Подтяжки", которые выглядят так {}
являются тонированными окнами нашего завода. Изнутри фабрики вы можете видеть, но снаружи вы не можете видеть.
ПРИМЕР ДОПОЛНИТЕЛЬНОГО КОДА ВЫШЕ
Наш код начинается со словарной функции, поэтому мы знаем, что это одно! Тогда имя функции sing - это мое собственное описание того, что представляет собой функция. Затем скобки(). Круглые скобки всегда существуют для функции. Иногда они пустые, и иногда у них есть что-то. У этого есть слово в: (person)
. После этого есть такая скобка {
. Это знаменует начало функции sing(). У этого есть партнер, который отмечает конец sing() как это }
function sing(person) { /* STUFF INSIDE THE FUNCTION */ }
Таким образом, эта функция может иметь какое-то отношение к пению и может потребоваться некоторые данные о человеке. У этого есть инструкции внутри, чтобы сделать что-то с этими данными.
Теперь, после функции sing(), ближе к концу кода находится строка
var person="an old lady";
VARIABLE: буквы var обозначают "переменная". Переменная подобна конверту. Снаружи в этом конверте отмечается "человек". С внутренней стороны он содержит клочок бумаги с информацией, которую нам нужна наша функция, некоторые буквы и пространства, соединенные вместе, как часть строки (она называется струной), которые делают фразу, читающую "старушку". Наш конверт может содержать другие типы вещей, такие как числа (называемые целыми числами), инструкции (называемые функциями), списки (называемые массивами). Поскольку эта переменная написана вне всех фигурных скобок {}
, и потому, что вы можете видеть сквозь тонированные окна, когда находитесь внутри фигурных скобок, эту переменную можно увидеть из любого места в коде. Мы называем это "глобальной переменной".
GLOBAL VARIABLE: человек - глобальная переменная, означающая, что если вы измените свою ценность от "старой леди" до "молодого человека", человек будет оставаться молодым человеком, пока вы не решите изменить его снова и что любая другая функция в код может видеть, что это молодой человек. Нажмите кнопку F12 или посмотрите настройки параметров, чтобы открыть консоль разработчика браузера и введите "человек", чтобы узнать, что это за значение. Тип person="a young man"
чтобы изменить его, а затем снова наберите "человек", чтобы увидеть, что он изменился.
После этого у нас есть линия
sing(person);
Эта строка вызывает функцию, как если бы она вызывала собаку
"Come on sing, Come and get person!"
Когда браузер загрузил код JavaScript и достиг этой строки, он запустит эту функцию. Я положил строку в конец, чтобы убедиться, что браузер имеет всю информацию, необходимую для ее запуска.
Функции определяют действия - основная функция - пение. Он содержит переменную, называемую firstPart, которая относится к пению о человеке, который относится к каждому из стихов песни: "Был" + человек + ", который проглотил". Если вы введете firstPart в консоль, вы не получите ответ, потому что переменная заблокирована в функции - браузер не может видеть внутри тонированных окон фигурных скобок.
ЗАКРЫТИЯ: Закрытие - это меньшие функции, которые находятся внутри большой функции sing(). Маленькие фабрики на большой фабрике. У каждого из них есть свои собственные фигурные скобки, которые означают, что переменные внутри них не видны снаружи. Поэтому имена переменных (существа и результат) могут повторяться в закрытии, но с разными значениями. Если вы введете эти имена переменных в окне консоли, вы не получите его значение, потому что оно скрыто двумя слоями тонированных окон.
Все замыкающие знают, что переменная функции sing() называется firstPart, потому что они могут видеть из своих тонированных окон.
После замыканий приходят линии
fly();
spider();
bird();
cat();
Функция sing() будет вызывать каждую из этих функций в том порядке, в котором они заданы. Затем будет работать функция sing().
Хорошо, разговаривая с 6-летним ребенком, я, возможно, буду использовать следующие ассоциации.
Представьте себе, что вы играете со своими маленькими братьями и сестрами во всем доме, и вы двигаетесь с вашими игрушками и приводите некоторых из них в свою комнату старшего брата. Через некоторое время ваш брат вернулся из школы и пошел в свою комнату, и он заперся внутри, так что теперь вы не могли получить доступ к игрушкам, оставленным там прямо. Но вы могли бы постучать в дверь и спросить своего брата за игрушками. Это называется закрытием игрушек; ваш брат сделал это для вас, и теперь он находится во внешнем пространстве.
Сравните с ситуацией, когда дверь была заблокирована сквозняком и никем внутри (выполнение общей функции), а затем произошел какой-то локальный пожар и сжег комнату (сборщик мусора: D), а затем была построена новая комната, и теперь вы можете уйти другие игрушки там (новый экземпляр функции), но никогда не получают те же игрушки, которые остались в первом экземпляре комнаты.
Для продвинутого ребенка я бы поставил что-то вроде следующего. Это не идеально, но это заставляет вас чувствовать, что это такое:
function playingInBrothersRoom (withToys) {
// We closure toys which we played in the brother 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 inner use.
var brotherGivesToyBack = function (toy) {
// New request. There is not yet closureToys on brother 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 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, чтобы поиграть с ним.
Ответ для шестилетнего ребенка (предполагая, что он знает, что такое функция и какая переменная, и какие данные):
Функции могут возвращать данные. Еще одна функция - один из видов данных, которые вы можете вернуть из функции. Когда эта новая функция возвращается, все переменные и аргументы, используемые в созданной ей функции, не исчезают. Вместо этого эта родительская функция "закрывается". Другими словами, ничто не может заглянуть внутрь него и увидеть используемые переменные, кроме функции, которую она вернула. Эта новая функция обладает особой способностью оглядываться внутри функции, которая ее создала, и видеть внутри нее данные.
function the_closure() {
var x = 4;
return function () {
return x; // Here, we look back inside the_closure for the value of x
}
}
var myFn = the_closure();
myFn(); //=> 4
Другой действительно простой способ объяснить это с точки зрения объема:
Каждый раз, когда вы создаете меньшую область видимости в более широкой области, меньшая область всегда сможет увидеть, что находится в более широкой области.
Функция в 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();
Возможно, немного выше всех, кроме самых ранних шестилетних, но несколько примеров, которые помогли мне сделать концепцию закрытия в 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, скажем, установим его в 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 теперь, когда для параметра externalFunction установлено значение null?
Причина, по которой referenceToInnerFunction все еще может получить доступ к значению externalVar, заключается в том, что когда закрытие было сначала создано путем помещения внутренней функции внутри внешней функции, функция innerFunction добавила ссылку на область внешних функций (ее переменные и функции) в свою цепочку областей видимости. Это означает, что innerFunction имеет указатель или ссылку на все переменные externalFunctions, включая outerVar. Таким образом, даже когда функция externalFunction завершила выполнение, или даже если она была удалена или установлена в нуль, переменные в своей области, такие как outerVar, остаются в памяти из-за выдающейся ссылки на них со стороны внутренней функции, которая была возвращена referenceToInnerFunction. Чтобы по-настоящему освободить externalVar и остальные переменные externalFunctions из памяти, вам придется избавиться от этой выдающейся ссылки на них, скажем, установив для referenceToInnerFunction значение null.
//////////
Еще две вещи о закрытии. Во-первых, у закрытия всегда будет доступ к последним значениям его содержащей функции.
function outerFunction() {
var outerVar = "monkey";
function innerFunction() {
alert(outerVar);
}
outerVar = "gorilla";
innerFunction();
}
outerFunction();
ALERT: горилла
Во-вторых, когда создается замыкание, он сохраняет ссылку на все свои функции и функции включения функций; он не получает, чтобы выбрать и выбрать. И все же, закрытие должно использоваться экономно или, по крайней мере, тщательно, поскольку они могут быть интенсивными в памяти; многие переменные могут храниться в памяти многократно после завершения выполнения содержащейся функции.
Я верю в более короткие объяснения, поэтому см. Ниже изображение.
function f1()
..> Light Red Box
function f2()
..> Red Small Box
Здесь мы имеем две функции: f1()
и f2()
. f2() является внутренним относительно f1(). f1() имеет переменную, var x = 10
.
При вызове функции f1()
функция f2()
может получить доступ к значению var x = 10
.
Вот код:
function f1() {
var x=10;
function f2() {
console.log(x)
}
return f2
}
f1()
f1()
вызывающий здесь:
Я просто укажу их на страницу Mozilla Closures. Это лучшее, кратчайшее и простое объяснение основ закрытия и практического использования, которые я нашел. Настоятельно рекомендуется всем, кто изучает JavaScript.
И да, я бы даже рекомендовал его 6-летнему - если 6-летний учитель узнает о закрытии, тогда логично, что они готовы понять краткое и простое объяснение, приведенное в статье.
Закрытие - это функция, имеющая доступ к родительской области, даже после закрытия родительской функции.
Таким образом, в основном замыкание является функцией другой функции. Мы можем сказать, как дочерняя функция.
Закрытие - это внутренняя функция, которая имеет доступ к внешней (охватывающей) функциональной цепочке переменных-цепей. Закрытие имеет три цепочки областей действия: он имеет доступ к своей собственной области (переменные, определенные между ее фигурными скобками), он имеет доступ к внешним переменным функций и имеет доступ к глобальным переменным.
Внутренняя функция имеет доступ не только к внешним функциям, но и к параметрам внешних функций. Обратите внимание, что внутренняя функция не может вызывать объект аргументов внешних функций, хотя он может напрямую вызвать параметры внешних функций.
Вы создаете закрытие, добавляя функцию внутри другой функции.
Кроме того, это очень полезный метод, который используется во многих известных средах, включая Angular
, Node.js
и jQuery
:
Закрытия широко используются в Node.js; они являются рабочими лошадками в асинхронной, неблокирующей архитектуре Node.js. Закрытие также часто используется в jQuery и почти каждый фрагмент кода JavaScript, который вы читаете.
Но как выглядят замыкания в реальной кодировке? Посмотрите на этот простой пример кода:
function showName(firstName, lastName) {
var nameIntro = "Your name is ";
// this inner function has access to the outer function variables, including the parameter
function makeFullName() {
return nameIntro + firstName + " " + lastName;
}
return makeFullName();
}
console.log(showName("Michael", "Jackson")); // Your name is Michael Jackson
Кроме того, это классический способ закрытия в jQuery, который использовал каждый разработчик javascript и jQuery:
$(function() {
var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable
selections.push(this.prop("name")); // update the selections variable in the outer function scope
});
});
Но почему мы используем закрытие? когда мы используем его в реальном программировании? каково практическое использование закрытий? ниже - хорошее объяснение и пример MDN:
Практические замыкания
Закрытие полезно, поскольку они позволяют связать некоторые данные (лексическую среду) с функцией, которая работает с этими данными. Это имеет очевидные параллели с объектно-ориентированным программированием, где объекты позволяют нам связать некоторые данные (свойства объекта) с одним или несколькими методами.
Следовательно, вы можете использовать закрытие в любом месте, где вы обычно можете использовать объект только с одним методом.
Ситуации, в которых вы, возможно, захотите сделать это, особенно распространены в Интернете. Большая часть кода, который мы пишем в интерфейсе JavaScript, основана на событиях - мы определяем какое-то поведение, а затем присоединяем его к событию, которое запускается пользователем (например, щелчком или нажатием). Наш код обычно прикрепляется как обратный вызов: одна функция, которая выполняется в ответ на событие.
Например, предположим, что мы хотим добавить несколько кнопок на страницу, которые изменяют размер текста. Один из способов сделать это - указать размер шрифта элемента body в пикселях, а затем установить размер других элементов на странице (например, заголовки) с помощью относительной единицы em:
Прочтите приведенный ниже код и запустите код, чтобы увидеть, как закрытие помогает нам здесь легко создавать отдельные функции для каждого раздела:
//javascript
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/*css*/
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
<!--html><!-->
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
Для дальнейшего изучения закрытий я рекомендую вам посетить эту страницу по MDN: https://developer.mozilla.org/en/docs/Web/JavaScript/Closures
Для шестилетнего?
Вы и ваша семья живут в мифическом городе Энн Вилле. У вас есть друг, который живет по соседству, поэтому вы позвоните им и попросите их выйти и поиграть. Вы набираете:
000001 (jamiesHouse)
Через месяц вы и ваша семья выезжаете из Энн-Вилле в следующий город, но вы и ваш друг все еще поддерживаете связь, поэтому теперь вам нужно набрать код города для города, в котором живет ваш друг, прежде чем набирать номер своего " правильный 'номер:
001 000001 (annVille.jamiesHouse)
Через год ваши родители переезжают в совершенно новую страну, но вы и ваш друг все еще поддерживаете связь, поэтому, прослушивая своих родителей, чтобы вы могли звонить по международным тарифам, вы набираете:
01 001 000001 (myOldCountry.annVille.jamiesHouse)
Странно, хотя, переехав в вашу новую страну, вы и ваша семья просто так переехали в новый город под названием Энн Вилле... и вам просто так удалось подружиться с каким-то новым человеком по имени Джейми... Вы даете им вызов...
000001 (jamiesHouse)
Ужасный...
На самом деле так жуткий, что вы рассказываете Джейми из своей старой страны об этом... У вас есть хороший смех. Поэтому однажды вы и ваша семья вернетесь в старую страну. Вы посетите свой старый город (Энн Вилль) и отправитесь посетить Джейми...
02 001 000001 (myNewCountry.annVille.jamiesHouse)
Мнения?
Что еще, у меня есть масса вопросов о терпении современного шестилетнего...
В закрытии JavaScript удивительны, где переменные или аргументы доступны для внутренних функций, и они будут живы даже после того, как внешняя функция вернется.
function getFullName(a, b) {
return a + b;
}
function makeFullName(fn) {
return function(firstName) {
return function(secondName) {
return fn(firstName, secondName);
}
}
}
makeFullName(getFullName)("stack")("overflow"); // Stackoverflow
Вот простой сценарий реального времени. Просто прочитайте это, и вы поймете, как мы здесь использовали закрытие (см., Как меняется число мест).
Все другие примеры, объясненные ранее, также очень хороши для понимания концепции.
function movieBooking(movieName) {
var bookedSeatCount = 0;
return function(name) {
++bookedSeatCount ;
alert( name + " - " + movieName + ", Seat - " + bookedSeatCount )
};
};
var MI1 = movieBooking("Mission Impossible 1 ");
var MI2 = movieBooking("Mission Impossible 2 ");
MI1("Mayur");
// alert
// Mayur - Mission Impossible 1, Seat - 1
MI1("Raju");
// alert
// Raju - Mission Impossible 1, Seat - 2
MI2("Priyanka");
// alert
// Raja - Mission Impossible 2, Seat - 1
Закрытие позволяет программистам JavaScript писать лучший код. Творческий, выразительный и лаконичный. Мы часто используем закрытие в JavaScript, и, независимо от нашего опыта работы с JavaScript, мы, несомненно, сталкиваемся с ними снова и снова. Закрытие может показаться сложным, но, надеюсь, после того, как вы прочтете это, закрытие будет гораздо легче понято и, следовательно, более привлекательно для ваших повседневных задач программирования JavaScript.
Вы должны быть знакомы с областью переменных JavaScript, прежде чем читать дальше, потому что для понимания замыканий вы должны понимать область переменных JavaScripts.
Закрытие - это внутренняя функция, которая имеет доступ к внешней (охватывающей) функциональной цепочке переменных-цепей. Закрытие имеет три цепочки областей действия: он имеет доступ к своей собственной области (переменные, определенные между ее фигурными скобками), он имеет доступ к внешним переменным функций и имеет доступ к глобальным переменным.
Внутренняя функция имеет доступ не только к внешним функциям, но и к параметрам внешних функций. Обратите внимание, что внутренняя функция не может вызывать объект аргументов внешних функций, хотя он может напрямую вызвать параметры внешних функций.
Вы создаете закрытие, добавляя функцию внутри другой функции.
Основной пример закрытий в JavaScript:
function showName (firstName, lastName) {
var nameIntro = "Your name is ";
// this inner function has access to the outer function variables, including the parameter
function makeFullName () {
return nameIntro + firstName + " " + lastName;
}
return makeFullName ();
}
showName ("Michael", "Jackson"); // Your name is Michael Jackson
Закрытия широко используются в Node.js; они являются рабочими лошадками в асинхронной, неблокирующей архитектуре Node.js. Закрытие также часто используется в jQuery и почти каждый фрагмент кода JavaScript, который вы читаете.
Классический пример jQuery закрытия:
$(function() {
var selections = [];
$(".niners").click(function() { // this closure has access to the selections variable
selections.push (this.prop("name")); // update the selections variable in the outer function scope
});
});
1. Закрытия имеют доступ к переменной внешних функций даже после возврата внешней функции:
Одной из наиболее важных и щекотливых функций с закрытием является то, что внутренняя функция все еще имеет доступ к внешним функциям, даже после того, как внешняя функция вернулась. Да, вы правильно это прочитали. Когда функции в JavaScript выполняются, они используют ту же цепочку областей действия, которая действовала, когда они были созданы. Это означает, что даже после того, как внешняя функция вернулась, внутренняя функция все еще имеет доступ к внешним функциям. Поэтому вы можете вызвать внутреннюю функцию позже в своей программе. Этот пример демонстрирует:
function celebrityName (firstName) {
var nameIntro = "This celebrity is ";
// this inner function has access to the outer function variables, including the parameter
function lastName (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return lastName;
}
var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.
// The closure (lastName) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function variables and parameter
mjName ("Jackson"); // This celebrity is Michael Jackson
2. Закрытие хранит ссылки на переменные внешних функций:
Они не сохраняют фактическое значение. Закрытие становится более интересным, когда значение переменной внешних функций изменяется до того, как будет вызвано замыкание. И эта мощная функция может быть использована в творческих целях, например, этот пример частных переменных, впервые продемонстрированный Дугласом Крокфордом:
function celebrityID () {
var celebrityID = 999;
// We are returning an object with some inner functions
// All the inner functions have access to the outer function variables
return {
getID: function () {
// This inner function will return the UPDATED celebrityID variable
// It will return the current value of celebrityID, even after the changeTheID function changes it
return celebrityID;
},
setID: function (theNewID) {
// This inner function will change the outer function variable anytime
celebrityID = theNewID;
}
}
}
var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.
mjID.getID(); // 999
mjID.setID(567); // Changes the outer function variable
mjID.getID(); // 567: It returns the updated celebrityId variable
3. Закрытие Gone Awry
Поскольку закрытие имеет доступ к обновленным значениям переменных внешних функций, они также могут приводить к ошибкам, когда переменная внешних функций изменяется с циклом for. Таким образом:
// This example is explained in detail below (just after this code box).
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id()); // 103
Здесь самый ответ Дзэн, который я могу дать:
Что бы вы ожидали от этого кода? Расскажите мне в комментарии, прежде чем запускать его. Мне любопытно!
function foo() {
var i = 1;
return function() {
console.log(i++);
}
}
var bar = foo();
bar();
bar();
bar();
var baz = foo();
baz();
baz();
baz();
Теперь откройте консоль в своем браузере (Ctrl + Shift + I или F12, надеюсь) и вставьте код и нажмите Enter.
Если этот код печатает то, что вы ожидаете (JavaScript newbies - игнорировать "undefined" в конце), то у вас уже есть бессловесное понимание. В словах переменная i
является частью закрытия экземпляра внутренней функции.
Я сказал так, потому что, как только я понял, что этот код помещает экземпляры внутренней функции foo()
в bar
и baz
а затем вызывает их через эти переменные, меня больше не удивляет.
Но если я ошибаюсь, и выход на консоль удивил вас, дайте мне знать!
1 2 3 1 2 3
. Ага. Но на более традиционном языке я бы ожидал синтаксических ошибок, иначе 1 1 1 1 1 1
.
1 1 1 1 1 1
в JS, вы можете поставить var j = i;
внутри внутренней функции, а затем используйте console.log(j++);
вместо. Бесполезно, но более знакомо некоторым из нас. Поэтому в этом сценарии я думаю, что замыкания более интуитивны для непосвященных, чем для многих обученных программистов.
function person(name, age){
var name = name;
var age = age;
function introduce(){
alert("My name is "+name+", and I'm "+age);
}
return introduce;
}
var a = person("Jack",12);
var b = person("Matt",14);
Everytime функции person
называются новая крышка создана. Хотя переменные a
и b
имеют a
и ту же функцию introduce
, она связана с различными замыканиями. И что закрытие будет существовать даже после того, как функция person
заканчивает выполнение.
a(); //My name is Jack, and I'm 12
b(); //My name is Matt, and I'm 14
Абстрактные замыкания могут быть представлены примерно так:
closure a = {
name: "Jack",
age: 12,
call: function introduce(){
alert("My name is "+name+", and I'm "+age);
}
}
closure b = {
name: "Matt",
age: 14,
call: function introduce(){
alert("My name is "+name+", and I'm "+age);
}
}
Предполагая, что вы знаете, как работает class
на другом языке, я сделаю аналогию.
Подумайте как
function
JavaScript как constructor
local variables
как instance properties
properties
являются частнымиinner functions
как instance methods
Каждый раз, когда вызывается function
object
содержащий все локальные переменные."properties"
объекта этого экземпляра.(Я не учитываю 6-летнюю вещь).
На языке, таком как JavaScript, где вы можете передавать функции как параметры другим функциям (языки, где функции являются гражданами первого класса), вы часто будете делать что-то вроде:
var name = 'Rafael';
var sayName = function() {
console.log(name);
};
Вы видите, sayName
не имеет определения для переменной name
, но использует значение name
которое было определено вне sayName
(в родительской области).
Скажем, вы передадите sayName
в качестве параметра другой функции, которая вызовет sayName
в качестве обратного вызова:
functionThatTakesACallback(sayName);
Обратите внимание, что:
sayName
будет называться внутри functionThatTakesACallback
(предположим, что, так как я не выполнил functionThatTakesACallback
в данном примере).sayName
вызывается, он регистрирует значение переменной name
.functionThatTakesACallback
не определяет переменную name
(ну, может, но это не имеет значения, так что предположим, что это не так). Таким образом, мы имеем sayName
называют внутри functionThatTakesACallback
и со ссылкой на name
переменной, которая не определена внутри functionThatTakesACallback
.
Что происходит тогда? Имя ReferenceError: name is not defined
?
Нет! Значение name
фиксируется внутри закрытия. Вы можете думать об этом закрытии как контексте, связанном с функцией, которая содержит значения, которые были доступны, когда эта функция была определена.
Итак: Даже если name
не в сфере, где функция sayName
будет называться (внутри functionThatTakesACallback
), sayName
может получить доступ к значению для name
, которое захваченным в крышке, связанную с sayName
.
-
Из книги "Красноречивый JavaScript":
Хорошей ментальной моделью является представление значений функций, содержащих как код в их теле, так и среду, в которой они созданы. При вызове тело функции видит свою исходную среду, а не среду, в которой выполняется вызов.
Чем больше я думаю о закрытии, тем больше я рассматриваю его как двухэтапный процесс: init - action
init: pass first what needed...
action: in order to achieve something for later execution.
К 6-летнему я хотел бы подчеркнуть практический аспект закрытия:
Daddy: Listen. Could you bring mum some milk (2).
Tom: No problem.
Daddy: Take a look at the map that Daddy has just made: mum is there and daddy is here.
Daddy: But get ready first. And bring the map with you (1), it may come in handy
Daddy: Then off you go (3). Ok?
Tom: A piece of cake!
Пример: Принесите немного молока маме (= действие). Сначала подготовьтесь и принесите карту (= init).
function getReady(map) {
var cleverBoy = 'I examine the ' + map;
return function(what, who) {
return 'I bring ' + what + ' to ' + who + 'because + ' cleverBoy; //I can access the map
}
}
var offYouGo = getReady('daddy-map');
offYouGo('milk', 'mum');
Потому что, если вы принесете с собой очень важную информацию (карту), вы достаточно осведомлены, чтобы выполнять другие подобные действия:
offYouGo('potatoes', 'great mum');
Разработчику я бы сделал параллель между закрытием и ООП. Фаза init аналогична передаче аргументов конструктору на традиционном языке OO; фаза действия - это, в конечном счете, метод, который вы призываете для достижения того, что вы хотите. И этот метод имеет доступ к этим аргументам init, используя механизм, называемый замыканием.
См. Мой другой ответ, иллюстрирующий параллелизм между ОО и закрытием:
Как "правильно" создать пользовательский объект в JavaScript?
Чтобы понять закрытие, вам нужно перейти к программе и буквально выполнить так, как если бы вы были временем выполнения. Давайте рассмотрим этот простой фрагмент кода:
JavaScript запускает код в два этапа:
Когда JavaScript проходит этап компиляции, он извлекает декларации переменных и функций. Это называется подъем. Функции, встречающиеся в этой фазе, сохраняются как текстовые капли в памяти, также известные как лямбда. После компиляции JavaScript вводит этап выполнения, где он присваивает все значения и запускает функцию. Для запуска функции он подготавливает контекст выполнения, назначая память из кучи и повторяя фазу компиляции и выполнения для этой функции. Эта область памяти называется областью действия функции. При запуске запуска существует глобальная область. Области являются ключом к пониманию замыканий.
В этом примере в первом a
определяется переменная a
а затем f
определяется на этапе компиляции. Все необъявленные переменные сохраняются в глобальной области. На этапе выполнения f
вызывается с аргументом. f
и фаза компиляции и выполнения повторяется для нее.
Аргументы также сохраняются в этой локальной области для f
. Всякий раз, когда создается локальный контекст или область выполнения, он содержит указатель ссылки на родительскую область. Весь переменный доступ следует этой лексической цепочке областей, чтобы найти ее ценность. Если переменная не найдена в локальной области, она следует цепочке и находит ее в своей родительской области. Именно поэтому локальная переменная переопределяет переменные в родительской области. Родительская область называется "Закрытие" для локальной области или функции.
Здесь, когда g
scope устанавливается, он получил лексический указатель на его родительский охват f
. Объем f
является замыканием для g
. В JavaScript, если есть некоторая ссылка на функции, объекты или области действия, если вы можете каким-то образом их достичь, он не получит сбор мусора. Поэтому, когда myG запущен, он имеет указатель на область действия f
которая является его закрытием. Эта область памяти не будет мусор даже f
вернулся. Это закрытие в отношении времени выполнения.
[[scope]]
ссылка.var data = "My Data!";
setTimeout(function() {
console.log(data); // Prints "My Data!"
}, 3000);
function makeAdder(n) {
var inc = n;
var sum = 0;
return function add() {
sum = sum + inc;
return sum;
};
}
var adder3 = makeAdder(3);
Очень интересный разговор о закрытии и многое другое - Arindam Paul - внутренние элементы JavaScript VM, EventLoop, Async и ScopeChains.
Познакомьтесь с иллюстрированным объяснением: как закрытие JavaScript работает за кулисами.
В статье объясняется, как объекты области (или LexicalEnvironment
) распределяются и используются интуитивно. Например, для этого простого скрипта:
"use strict";
var foo = 1;
var bar = 2;
function myFunc() {
//-- Define local-to-function variables
var a = 1;
var b = 2;
var foo = 3;
}
//-- And then, call it:
myFunc();
При выполнении кода верхнего уровня мы имеем следующее расположение объектов области видимости:
И когда myFunc()
, у нас есть следующая цепочка областей видимости:
Понимание того, как объекты области объектов создаются, используются и удаляются, является ключом к тому, чтобы иметь общую картину и понимать, как затворы работают под капотом.
См. Вышеупомянутую статью для всех деталей.
Закрытие - это функция внутри функции, которая имеет доступ к своим "родительским" функциональным переменным и параметрам.
Пример:
function showPostCard(Sender, Receiver) {
var PostCardMessage = " Happy Spring!!! Love, ";
function PreparePostCard() {
return "Dear " + Receiver + PostCardMessage + Sender;
}
return PreparePostCard();
}
showPostCard("Granny", "Olivia");
Несмотря на то, что в Интернете существует множество прекрасных определений закрытия JavaScript, я пытаюсь начать объяснять своего шестилетнего друга своими любимыми определениями закрытия, которые помогли мне лучше понять закрытие.
Что такое закрытие?
Закрытие - это внутренняя функция, которая имеет доступ к внешней (охватывающей) функциональной цепочке переменных-цепей. Закрытие имеет три цепочки областей действия: он имеет доступ к своей собственной области (переменные, определенные между ее фигурными скобками), он имеет доступ к внешним переменным функций и имеет доступ к глобальным переменным.
Закрытие - это локальные переменные для функции, которые сохраняются в живых после возвращения функции.
Закрытие - это функции, которые относятся к независимым (свободным) переменным. Другими словами, функция, определенная в замыкании, "запоминает" среду, в которой она была создана.
Закрытие - это расширение концепции сферы действия. С закрытием функции имеют доступ к переменным, которые были доступны в области, где была создана функция.
Закрытие представляет собой стек-фрейм, который не освобождается при возврате функции. (Как будто "стек-кадр" был malloc'ed вместо того, чтобы быть в стеке!)
Языки, такие как Java, предоставляют возможность объявлять методы private, что означает, что их можно вызывать только другими методами в одном классе. JavaScript не обеспечивает собственный способ сделать это, но можно эмулировать частные методы, используя закрытие.
"Закрытие" - это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (что "закрывает" выражение).
Закрытие - это механизм абстракции, который позволяет очень осторожно разделить проблемы.
Использование закрытий:
Закрытие полезно для скрытия реализации функциональных возможностей при одновременном выявлении интерфейса.
Вы можете эмулировать концепцию инкапсуляции в JavaScript с помощью закрытий.
Закрытия широко используются в jQuery и Node.js.
Хотя объектные литералы, безусловно, легко создавать и удобны для хранения данных, закрытие часто является лучшим выбором для создания статических одноэлементных пространств имен в большом веб-приложении.
Пример закрытий:
Предполагая, что мой 6-летний друг познакомился совсем недавно в своей начальной школе, я почувствовал, что этот пример добавления двух чисел будет самым простым и умным для шестилетнего ребенка, чтобы узнать о закрытии.
Пример 1: Закрытие достигается здесь, возвращая функцию.
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
Пример 2. Закрытие достигается путем возврата литерала объекта.
function makeAdder(x) {
return {
add: function(y){
return x + y;
}
}
}
var add5 = makeAdder(5);
console.log(add5.add(2));//7
var add10 = makeAdder(10);
console.log(add10.add(2));//12
Пример 3: Закрытие в jQuery
$(function(){
var name="Closure is easy";
$('div').click(function(){
$('p').text(name);
});
});
Полезные ссылки:
Благодаря вышеуказанным ссылкам, которые помогают мне лучше понять и объяснить закрытие.
Версия изображения для этого ответа: [Решено]
Просто забудьте о сфере все и помните: когда нужна какая-то переменная, javascript ее не уничтожит. Переменная всегда указывает на самое новое значение.
Пример 1:
Пример 2:
Пример 3:
Этот ответ представляет собой краткое изложение этого видеоролика YouTube. Javascript Closures. Так что полный кредит на это видео.
Закрытие - это ничего, кроме функций состояния, которые поддерживают состояния их частных переменных.
Обычно, когда вы вызываете функцию, как показано на рисунке ниже. Переменные создаются в стеке (используемая оперативная память), а затем деблокируются.
Но теперь есть ситуации, когда мы хотим поддерживать это состояние функции, в которой используется Javascript-закрытие. Закрытие - это функция внутри функции с обратным вызовом, как показано в приведенном ниже коде.
Таким образом, код замыкания для функции счетчика выше выглядит так, как показано ниже. Это функция внутри функции с оператором return.
function Counter() {
var counter = 0;
var Increment = function () {
counter++;
alert(counter);
}
return {
Increment
}
}
Итак, теперь, если вы делаете вызов, счетчик будет увеличиваться, другими словами, вызов функции поддерживает состояния.
var x = Counter(); // get the reference of the closure
x.Increment(); // Displays 1
x.Increment(); // Display 2 ( Maintains the private variables)
Но теперь самый большой вопрос в том, что использование такой функции состояния. Функции состояния являются строительными блоками для реализации концепции ООП, таких как абстракция, инкапсуляция и создание самостоятельных модулей.
Итак, что бы вы ни делали в инкапсулированном виде, вы можете считать его частным, а вещи, которые должны быть открыты для общественности, должны быть поставлены в обратную инструкцию. Также эти компоненты являются изолированными изолированными объектами, поэтому они не загрязняют глобальные переменные.
Объект, который следует принципам ООП, является самодостаточным, следует за абстракцией, следует за капсулированием и т.д. С закрытием в Javascript это сложно реализовать.
Закрытие - это то, что многие разработчики JavaScript используют все время, но мы принимаем это как должное. Как это работает, это не так сложно. Понимание того, как использовать его целенаправленно, является сложным.
В своем простейшем определении (как указывали другие ответы) замыкание - это в основном функция, определенная внутри другой функции. И эта внутренняя функция имеет доступ к переменным, определенным в области внешней функции. Наиболее распространенной практикой, которую вы увидите при использовании закрытий, является определение переменных и функций в глобальной области видимости и доступ к этим переменным в области функций этой функции.
var x = 1;
function myFN() {
alert(x); //1, as opposed to undefined.
}
// Or
function a() {
var x = 1;
function b() {
alert(x); //1, as opposed to undefined.
}
b();
}
И что?
Закрытие не является особенным для пользователя JavaScript, пока вы не подумаете о том, какая будет жизнь без них. В других языках переменные, используемые в функции, очищаются, когда эта функция возвращается. В приведенном выше случае x был бы "нулевым указателем", и вам нужно было бы установить getter и setter и начать передавать ссылки. Звучит не так, как JavaScript? Поблагодарите могущественное закрытие.
Почему это должно меня беспокоить?
Вы действительно не должны знать о закрытии, чтобы использовать их. Но, как отмечали другие, они могут быть использованы для создания искусственных частных переменных. Пока вам не понадобятся частные переменные, просто используйте их, как и всегда.
Следующий пример - простая иллюстрация закрытия JavaScript. Это функция закрытия, которая возвращает функцию с доступом к ее локальной переменной x,
function outer(x){
return function inner(y){
return x+y;
}
}
Вызовите функцию следующим образом:
var add10 = outer(10);
add10(20); // The result will be 30
add10(40); // The result will be 50
var add20 = outer(20);
add20(20); // The result will be 40
add20(40); // The result will be 60
Я нашел очень четкую главу 8 раздела 6 "Закрытие" JavaScript: окончательное руководство Дэвида Фланагана, 6-е издание, О'Рейли, 2011. Я попытаюсь перефразировать.
Когда функция вызывается, создается новый объект для хранения локальных переменных для этого вызова.
Область действия зависит от местоположения объявления, а не от места ее выполнения.
Теперь предположим, что внутренняя функция объявлена внутри внешней функции и ссылается на переменные этой внешней функции. Далее предположим, что внешняя функция возвращает внутреннюю функцию как функцию. Теперь есть внешняя ссылка на любые значения в области внутренней функции (которая по нашим предположениям включает значения из внешней функции).
JavaScript сохранит эти значения, поскольку они остались в рамках текущего исполнения благодаря тому, что они были удалены из завершенной внешней функции. Все функции - это замыкания, но заинтересованными замыканиями являются внутренние функции, которые в нашем предполагаемом сценарии сохраняют значения внешних функций в их "оболочке" (я надеюсь, что я правильно использую язык здесь), когда возвращаются (внутренние функции) от внешних функций. Я знаю, что это не соответствует требованиям шестилетнего возраста, но, надеюсь, это по-прежнему полезно.
Из личного сообщения в блоге:
По умолчанию JavaScript знает два типа областей: глобальный и локальный.
var a = 1;
function b(x) {
var c = 2;
return x * c;
}
В приведенном выше коде переменная a и функция b доступны из любого места в коде (то есть глобально). Переменная c
доступна только в пределах области b
функции (то есть локальной). Большинство разработчиков программного обеспечения не будут довольны этой недостаточной гибкостью, особенно в крупных программах.
Закрытие JavaScript помогает решить эту проблему, связывая функцию с контекстом:
function a(x) {
return function b(y) {
return x + y;
}
}
Здесь функция a
возвращает функцию b
. Поскольку b
определен внутри a
, он автоматически получает доступ к тому, что определено в a
, то есть x
в этом примере. Вот почему b
может вернуть x
+ y
без объявления x
.
var c = a(3);
Переменной c
присваивается результат вызова параметра с параметром 3. То есть экземпляр функции b
где x
= 3. Другими словами, c
теперь является функцией, эквивалентной:
var c = function b(y) {
return 3 + y;
}
Функция b
запоминает, что x
= 3 в ее контексте. Следовательно:
var d = c(4);
назначит значение 3 + 4 на d
, то есть 7.
Примечание. Если кто-то изменяет значение x
(скажем, x
= 22) после создания экземпляра функции b
, это также будет отражено в b
. Следовательно, более поздний вызов c
(4) вернет 22 + 4, то есть 26.
Закрытие также может использоваться для ограничения объема переменных и методов, объявленных глобально:
(function () {
var f = "Some message";
alert(f);
})();
Вышеприведенное является закрытием, в котором функция не имеет имени, нет аргумента и вызывается немедленно. Выделенный код, объявляющий глобальную переменную f
, ограничивает области f
закрытием.
Теперь есть общая предосторожность JavaScript, где закрытие может помочь:
var a = new Array();
for (var i=0; i<2; i++) {
a[i]= function(x) { return x + i ; }
}
Из вышесказанного, большинство предположило бы, что массив a
будет инициализирован следующим образом:
a[0] = function (x) { return x + 0 ; }
a[1] = function (x) { return x + 1 ; }
a[2] = function (x) { return x + 2 ; }
В действительности, это как инициализируется, так как последнее значение i
в контексте равно 2:
a[0] = function (x) { return x + 2 ; }
a[1] = function (x) { return x + 2 ; }
a[2] = function (x) { return x + 2 ; }
Решение:
var a = new Array();
for (var i=0; i<2; i++) {
a[i]= function(tmp) {
return function (x) { return x + tmp ; }
} (i);
}
Аргумент/переменная tmp
содержит локальную копию изменяющегося значения i
при создании экземпляров функции.
Я уверен, что Эйнштейн не сказал этого с прямым ожиданием, что мы заберем любую эзотерическую идею "мозгового штурма" и будем бегать за шестилетними детьми с тщетными попытками получить эти "сумасшедшие" (а что еще хуже для них - скучно ) вещи своим детским ушам :) Если бы мне было шесть лет, мне бы не хотелось иметь таких родителей или не дружить с такими скучными филантропами, извините :)
Во всяком случае, для младенцев закрытие просто обнимает, я думаю, как бы вы ни пытались объяснить :) И когда вы обнимаете своего друга, вы оба делитесь тем, что у вас есть на данный момент. Это обряд посвящения, как только вы обнимаете кого-то, вы показываете ей доверие и готовность позволить ей делать с вами много вещей, которые вы не позволяете, и прятаться от других. Это акт дружбы :).
Я действительно не знаю, как объяснить это 5-6-летним младенцам. Я не думаю, что они оценят любые фрагменты кода JavaScript, такие как:
function Baby(){
this.iTrustYou = true;
}
Baby.prototype.hug = function (baby) {
var smiles = 0;
if (baby.iTrustYou) {
return function() {
smiles++;
alert(smiles);
};
}
};
var
arman = new Baby("Arman"),
morgan = new Baby("Morgana");
var hug = arman.hug(morgan);
hug();
hug();
Только для детей:
Закрытие объятия
Ошибка в мухе
KISS - это смуще! :)
Закрытие Понимание начинается с Scope Chain
Чтобы понять, что такое закрытие, вам нужно понять, что такое целая цепочка функции. Это полностью помогло мне, когда я боролся с закрытием. Придерживайтесь готовности до конца, и я уверен, что вы справитесь с закрытием
Цепочка охвата:
Каждый фрагмент кода JavaScript (глобальный код или функции) имеет связанную с ним цепочку областей видимости. Эта цепочка областей видимости - это список или цепочка объектов, которые определяют переменные, которые являются "в области" для этого кода. Когда JavaScript нужно искать значение переменной x
(процесс, называемый переменным разрешением), он начинается с поиска первого объекта в цепочке. Если этот объект имеет свойство с именем x
, используется значение этого свойства. Если первый объект не имеет свойства с именем x
, JavaScript продолжает поиск со следующим объектом в цепочке. Если второй объект не имеет свойства с именем x
, поиск переходит к следующему объекту и так далее. Если x
не является свойством какого-либо из объектов в цепочке областей видимости, то x
не является видимым для этого кода, и возникает ошибка ReferenceError. В коде JavaScript верхнего уровня (т.е. Код, не содержащийся в каких-либо определениях функций) цепочка областей видимости состоит из одного объекта, глобального объекта. В не-вложенной функции цепочка областей видимости состоит из двух объектов. Первый - это объект, который определяет параметры функций и локальные переменные, а второй - глобальный объект. Во вложенной функции цепочка областей действия имеет три или более объектов. Важно понять, как создается эта цепочка объектов. Когда функция ОПРЕДЕЛЕНА, она сохраняет цепочку областей действия в действии. Когда эта функция INVOKED, она создает новый объект для хранения своих локальных переменных и добавляет этот новый объект в цепочку хранимых цепей для создания новой более длинной цепочки, которая представляет область действия для этой функции. Это становится более интересным для вложенных функций, потому что каждый раз, когда вызывается внешняя функция, внутренняя функция определяется снова. Поскольку цепочка областей действия различна для каждого вызова внешней функции, внутренняя функция будет тонко различной при каждом ее определении - код внутренней функции будет идентичным при каждом вызове внешней функции, но цепочка областей, связанная с этим код будет другим. Это понятие цепи видимости имеет решающее значение для понимания закрытий.
Закрытие:
Технически все функции JavaScript - это замыкания: они являются объектами, и у них есть связанная с ними цепочка областей видимости. Большинство функций вызывается с использованием той же цепочки областей действия, которая действовала, когда функция была определена, и не имеет большого значения, что есть закрытие. Замыкания становятся интересными, когда они вызываются в другой цепочке видимости, чем те, которые действовали, когда они были определены. Это происходит чаще всего, когда возвращенный объект функции возвращается из функции, в которой он был определен. Существует ряд мощных методов программирования, которые включают в себя такие закрытые блокировки функций, и их использование стало относительно распространенным в программировании JavaScript. Закрытие может показаться запутанным, когда вы впервые en- встретите их, но важно понимать их достаточно хорошо, чтобы удобно использовать их.
Пример:
var scope = "global scope"; // A global variable
function checkscope() {
var scope = "local scope"; // A local variable
function f() {
return scope;
} // Return the value in scope here
return f();
}
checkscope(); // => "local scope"
Функция checkscope() объявляет локальную переменную, а затем определяет и вызывает функцию, возвращающую значение этой переменной. Вам должно быть понятно, почему вызов checkscope() возвращает "локальную область". Теперь немного изменим код. Можете ли вы сказать, что этот код вернется?
В этом коде пара скобок переместилась изнутри checkscope()
в checkscope()
. Вместо того, чтобы ссылаться на вложенную функцию и возвращать ее результат, checkscope()
теперь просто возвращает сам вложенный объект функции. Что происходит, когда мы вызываем эту вложенную функцию (со второй парой скобок в последней строке кода) вне функции, в которой она была определена? Помните основное правило лексического охвата: функции JavaScript выполняются с использованием цепочки областей видимости, которая действовала, когда они были определены. Вложенная функция f()
была определена в цепочке областей видимости, в которой область переменной была привязана к значению "локальная область". Эта привязка по-прежнему действует, когда f выполняется, где бы она не выполнялась. Таким образом, последняя строка кода возвращает "локальная область", а не "глобальная область". Это, в двух словах, удивительная и мощная природа замыканий: они фиксируют привязки локальных переменных (и параметров) внешней функции, в пределах которой они определены.
Вот конкретный пример закрытия:
var uniqueInteger = (function() {//Определить и вызывать var counter = 0;//Частное состояние функции ниже return function() {return counter++;};})();
Чтобы понять этот код, вы должны внимательно его прочитать. На первый взгляд первая строка кода выглядит так, что она назначает функцию переменной uniqueInteger
. Фактически, код определяет и вызывает (как намекнул открытая скобка в первой строке) функцию, поэтому это возвращаемое значение функции, которая присваивается uniqueInteger
. Теперь, если мы изучим тело функции, мы видим, что его возвращаемое значение является другой функцией. Именно этому вложенному объекту функции присваивается uniqueInteger
. Вложенная функция имеет доступ к переменным в области видимости и может использовать переменную счетчика, определенную во внешней функции. Как только эта внешняя функция вернется, ни один другой код не сможет увидеть переменную счетчика: внутренняя функция имеет эксклюзивный доступ к ней.
Частные переменные, такие как счетчик, не обязательно должны быть эксклюзивными для одного закрытия: вполне возможно, что две или более вложенных функций будут определены в рамках одной и той же внешней функции и разделяют одну и ту же цепочку областей. Рассмотрим следующий код:
function counter() {var n = 0; return { count: function() { return n++; return {count: function() {return n++; }, reset: function() { n = 0; }, reset: function() {n = 0; } }; }}; } var c = counter(), d = counter(); //Создаем два счетчика c.count(); //=> 0 d.count(); //=> 0: они подсчитывают независимо c.reset(); // методы reset() и count() совместно используют состояние c.count(); //=> 0: потому что мы перезагружаем c d.count(); //=> 1: d не был сброшен
Функция counter()
возвращает объект "counter". Этот объект имеет два метода: count()
возвращает следующее целое число, а reset()
сбрасывает внутреннее состояние. Первое, что нужно понять, это то, что два метода совместно используют доступ к частной переменной n. Второе, что нужно понять, это то, что каждый вызов функции counter()
создает новую цепочку областей видимости и новую приватную переменную. Поэтому, если вы дважды вызываете counter()
, вы получаете два объекта-счетчика с разными частными переменными. Вызов count()
или reset()
для одного объекта счетчика не влияет на другой.
Закрытие легко понять, если вы просто принимаете лексическое правило определения: функции выполняются с использованием цепочки областей действия, которая действовала, когда они были определены. Однако некоторые программисты считают, что запутывание запутывает, потому что они попадают в подробности imple-. Разумеется, они думают, что локальные переменные, определенные во внешней функции, перестают существовать, когда внешняя функция возвращается, так как может вложенная функция выполнить с использованием цепочки областей, которая больше не существует? Если вы задаетесь вопросом об этом your-, то вы, вероятно, были подвержены низкоуровневым языкам программирования, таким как C и архитектурам на основе стека: если функции локальных переменных определены в стеке процессора, тогда они действительно прекратили бы есть, когда функция вернулась. Но помните наше определение сферы действия. Мы описали его как список объектов, а не стек привязок. Каждый раз, когда вызывается функция JavaScript, создается новый объект для хранения локальных переменных для этого вызова, и этот объект добавляется в цепочку областей видимости. Когда функция возвращается, этот объект привязки переменных удаляется из цепочки областей видимости. Если не было вложенных функций, ссылок на объект привязки больше нет, и он собирает мусор. Если были определены вложенные функции, то каждая из этих функций имеет ссылку на цепочку областей видимости, и эта цепочка областей относится к объекту привязки переменных. Однако, если эти объекты вложенных функций остаются в пределах их внешней функции, то они сами будут собирать мусор вместе с объектом привязки переменных, на который они ссылаются. Но если функция определяет вложенную функцию и возвращает ее или сохраняет ее в каком-либо свойстве, тогда будет внешняя ссылка на вложенную функцию. Это не будет сбор мусора, а объект привязки переменных, к которому он относится, не будет собираться с мусором.
Закрытие - это функция, имеющая доступ к родительской области, даже после закрытия родительской функции.
var add = (function() {
var counter = 0;
return function() {
return counter += 1;
}
})();
add();
add();
add();
// The counter is now 3
Пример:
add
присваивается возвращаемое значение самостоятельного вызова функции.Pinocchio: Закрытие в 1883 году (за столетие до JavaScript)
Я думаю, что это лучше всего объяснить 6-летнему с приятным приключением... Часть Приключений Пиноккио, где Пиноккио проглатывается крупной собакой...
var tellStoryOfPinocchio = function(original) {
// Prepare for exciting things to happen
var pinocchioFindsMisterGeppetto;
var happyEnding;
// The story starts where Pinocchio searches for his 'father'
var pinocchio = {
name: 'Pinocchio',
location: 'in the sea',
noseLength: 2
};
// Is it a dog... is it a fish...
// The dogfish appears, however there is no such concept as the belly
// of the monster, there is just a monster...
var terribleDogfish = {
swallowWhole: function(snack) {
// The swallowing of Pinocchio introduces a new environment (for the
// things happening inside it)...
// The BELLY closure... with all of its guts and attributes
var mysteriousLightLocation = 'at Gepetto\ ship';
// Yes: in my version of the story the monsters mouth is directly
// connected to its belly... This might explain the low ratings
// I had for biology...
var mouthLocation = 'in the monsters mouth and then outside';
var puppet = snack;
puppet.location = 'inside the belly';
alert(snack.name + ' is swallowed by the terrible dogfish...');
// Being inside the belly, Pinocchio can now experience new adventures inside it
pinocchioFindsMisterGeppetto = function() {
// The event of Pinocchio finding Mister Geppetto happens inside the
// belly and so it makes sence that it refers to the things inside
// the belly (closure) like the mysterious light and of course the
// hero Pinocchio himself!
alert(puppet.name + ' sees a mysterious light (also in the belly of the dogfish) in the distance and swims to it to find Mister Geppetto! He survived on ship supplies for two years after being swallowed himself. ');
puppet.location = mysteriousLightLocation;
alert(puppet.name + ' tells Mister Geppetto he missed him every single day! ');
puppet.noseLength++;
}
happyEnding = function() {
// The escape of Pinocchio and Mister Geppetto happens inside the belly:
// it refers to Pinocchio and the mouth of the beast.
alert('After finding Mister Gepetto, ' + puppet.name + ' and Mister Gepetto travel to the mouth of the monster.');
alert('The monster sleeps with its mouth open above the surface of the water. They escape through its mouth. ');
puppet.location = mouthLocation;
if (original) {
alert(puppet.name + ' is eventually hanged for his innumerable faults. ');
} else {
alert(puppet.name + ' is eventually turned into a real boy and they all lived happily ever after...');
}
}
}
}
alert('Once upon a time...');
alert('Fast forward to the moment that Pinocchio is searching for his \'father\'...');
alert('Pinocchio is ' + pinocchio.location + '.');
terribleDogfish.swallowWhole(pinocchio);
alert('Pinocchio is ' + pinocchio.location + '.');
pinocchioFindsMisterGeppetto();
alert('Pinocchio is ' + pinocchio.location + '.');
happyEnding();
alert('Pinocchio is ' + pinocchio.location + '.');
if (pinocchio.noseLength > 2)
console.log('Hmmm... apparently a little white lie was told. ');
}
tellStoryOfPinocchio(false);
Представьте, что в вашем городе есть очень большой парк, где вы видите волшебника по имени Мистер Кодер, запускающего бейсбольные игры в разных уголках парка, используя свою волшебную палочку, называемую JavaScript.
Естественно, каждая игра в бейсбол имеет одни и те же правила, и каждая игра имеет свой собственный счет.
Естественно, десятки одной бейсбольной игры полностью отделены от других игр.
Закрытие - это особый способ, которым Mr.Coder сохраняет за собой все его магические игры в бейсбол.
Если вы это хорошо понимаете, вы можете объяснить это просто. И самый простой способ - абстрагировать его от контекста. Код в сторону, даже программирование в сторону. Пример метафоры будет лучше.
Представьте себе, что функция - это комната, стены которой выполнены из стекла, но они являются специальным стеклом, как и в комнате для допросов. Снаружи они непрозрачны, изнутри они прозрачны. Это могут быть комнаты в других комнатах, и единственным способом общения является телефон.
Если вы звоните извне, вы не знаете, что в нем, но вы знаете, что люди внутри будут выполнять задание, если вы дадите им определенную информацию. Они могут видеть снаружи, поэтому они могут спросить вас о вещах, которые находятся снаружи, и внести изменения в этот материал, но вы не можете изменить то, что внутри внутри, вы даже не видите (знаете), что внутри. Люди в той комнате, которую вы звоните, видят, что она снаружи, но не то, что находится внутри комнат в этой комнате, поэтому они взаимодействуют с ними так, как вы делаете извне. Люди внутри самых внутренних комнат могут видеть много вещей, но люди из самой внешней комнаты даже не знают о существовании самой внутренней комнаты.
Для каждого вызова во внутреннюю комнату люди в этой комнате записывают информацию об этом конкретном вызове, и они настолько хорошо делают это, что никогда не ошибаются в разговорах с другими материалами вызова.
Номера - это функции, видимость - область видимости, люди делают задачу - это заявления, вещи - объекты, телефонные звонки - вызовы функций, информация о телефонных звонках - это аргументы, записи вызовов - экземпляры области, самая внешняя комната - глобальный объект.
Если вы хотите объяснить это шестилетнему ребенку, тогда вы должны найти что-то гораздо более простое и НЕТ кода.
Просто скажите ребенку, что он "открыт", в котором говорится, что он может иметь отношения с некоторыми другими, своими друзьями. В какой-то момент он определил друзей (мы можем знать имена его друзей), это закрытие. Если вы сфотографируете его и его друзей, то он "закрыт" относительно своих способностей к дружбе. Но в целом он "открыт". За всю свою жизнь у него будет много разных друзей. Одним из этих наборов является замыкание.
Возможно, вам стоит рассмотреть объектно-ориентированную структуру вместо внутренних функций. Например:
var calculate = {
number: 0,
init: function (num) {
this.number = num;
},
add: function (val) {
this.number += val;
},
rem: function (val) {
this.number -= val;
}
};
И прочитайте результат из переменной calculate.number, которая в любом случае нуждается в "возврате".
Закрытие представляет собой блок кода, который удовлетворяет трем критериям:
Он может передаваться как ценность и
исполняемый по требованию всеми, кто имеет эту ценность, в это время
он может ссылаться на переменные из контекста, в котором он был создан (т.е. закрыт по отношению к переменному доступу, в математическом смысле слова "закрыто").
(Слово "закрытие" на самом деле имеет неточное значение, и некоторые люди не считают, что критерий № 1 является частью определения. Я думаю, что это так.)
Закрытие является основой функциональных языков, но они присутствуют и во многих других языках (например, Java анонимные внутренние классы). Вы можете сделать классный материал с ними: они позволяют отложить исполнение и некоторые элегантные трюки стиля.
Автор: Пол Кантрелл, @http://innig.net/software/ruby/closures-in-ruby
закрытие
"Закрытие" - это выражение (обычно функция), которое может иметь свободные переменные вместе со средой, которая связывает эти переменные (что "закрывает" выражение).
Другими словами, замыкание является выражением, которое сохраняет доступ к объектам вне их области видимости.
Простая аналогия
Многие люди любят путешествовать и посещать разные места. Они никогда не забывают свою родную страну и возвращаются с множеством историй. Люди, которые встречаются с туристами, фактически не посещают все пункты назначения, а узнают о них через рассказы.
function tourist(home) {
var places = [];
return {
country: function() {
console.log("Country: " + home);
},
visit: function(destination) {
places.push(destination);
},
tell: function() {
for (var i = 0; i < places.length; i++) {
console.log(places[i]);
}
}
};
}
var x = tourist('India');
x.visit('Ireland');
x.country();
x.tell();
Другой пример
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
использование
Закрытие позволяет связать некоторые данные (среду) с функцией, которая работает с этими данными. Это похоже на объектно-ориентированное программирование, где объекты позволяют нам связывать некоторые данные (свойства объекта) с одним или несколькими методами.
Таким образом, вы можете использовать закрытие в любом месте, где вы обычно можете использовать объект только с одним методом, т.е. обратными вызовами, обработчиками событий и т.д.
Кроме того, JavaScript не поддерживает частные методы/переменные, которые можно имитировать с помощью закрытий.
Дополнительные ссылки
Закрытие - это средство, посредством которого внутренние функции могут ссылаться на переменные, присутствующие в их внешней закрывающей функции после того, как их родительские функции уже завершены.
// A function that generates a new function for adding numbers.
function addGenerator( num ) {
// Return a simple function for adding two numbers
// with the first number borrowed from the generator
return function( toAdd ) {
return num + toAdd
};
}
// addFive now contains a function that takes one argument,
// adds five to it, and returns the resulting number.
var addFive = addGenerator( 5 );
// We can see here that the result of the addFive function is 9,
// when passed an argument of 4.
alert( addFive( 4 ) == 9 );
Вероятно, вы не должны сообщать шестилетнему о закрытии, но если вы это сделаете, вы можете сказать, что закрытие дает возможность получить доступ к переменной, объявленной в какой-либо другой области функций.
function getA() {
var a = [];
// this action happens later,
// after the function returned
// the 'a' value
setTimeout(function() {
a.splice(0, 0, 1, 2, 3, 4, 5);
});
return a;
}
var a = getA();
out('What is 'a' length?');
out(''a' length is ' + a.length);
setTimeout(function() {
out('No wait...');
out(''a' length is ' + a.length);
out('OK :|')
});
<pre id="output"></pre>
<script>
function out(k) {
document.getElementById('output').innerHTML += '> ' + k + '\n';
}
</script>
Закрытие создается, когда внутренняя функция каким-то образом становится доступной для любой области вне внешней функции.
Пример:
var outer = function(params){ //Outer function defines a variable called params
var inner = function(){ // Inner function has access to the params variable of the outer function
return params;
}
return inner; //Return inner function exposing it to outer scope
},
myFunc = outer("myParams");
myFunc(); //Returns "myParams"
Закрытие - это несколько продвинутая и часто неправильно понятая особенность языка JavaScript. Проще говоря, закрытие - это объекты, которые содержат функцию и ссылку на среду, в которой была создана функция. Однако, чтобы полностью понять закрытие, есть еще две особенности языка JavaScript, которые должны быть сначала поняты - первоклассные функции и внутренние функции.
Функции первого класса
В языках программирования функции считаются первоклассными гражданами, если их можно манипулировать, как и любой другой тип данных. Например, функции первого класса могут быть созданы во время выполнения и назначены переменным. Они также могут быть переданы и возвращены другими функциями. Помимо выполнения ранее упомянутых критериев, функции JavaScript также имеют свои собственные свойства и методы. В следующем примере показаны некоторые возможности первоклассных функций. В этом примере две функции создаются и назначаются переменным "foo" и "bar". Функция, хранящаяся в "foo", отображает диалоговое окно, а "bar" просто возвращает любой аргумент. Последняя строка примера делает несколько вещей. Во-первых, функция, хранящаяся в "bar", вызывается с аргументом "foo". Затем "bar" возвращает ссылку на функцию "foo". Наконец, вызывается возвращаемая ссылка "foo", вызывая "Hello World!". для отображения.
var foo = function() {
alert("Hello World!");
};
var bar = function(arg) {
return arg;
};
bar(foo)();
Внутренние функции
Внутренние функции, также называемые вложенными функциями, являются функциями, которые определены внутри другой функции (называемой внешней функцией). Каждый раз, когда вызывается внешняя функция, создается экземпляр внутренней функции. В следующем примере показано, как используются внутренние функции. В этом случае add() является внешней функцией. Внутри add() определяется и вызывается внутренняя функция doAdd().
function add(value1, value2) {
function doAdd(operand1, operand2) {
return operand1 + operand2;
}
return doAdd(value1, value2);
}
var foo = add(1, 2);
// foo equals 3
Важной характеристикой внутренних функций является то, что они имеют неявный доступ к области внешних функций. Это означает, что внутренняя функция может использовать переменные, аргументы и т.д. Внешней функции. В предыдущем примере аргументы "value1" и "value2" для add() передавались в doAdd() как аргументы "operand1" и "operand2". Однако это необязательно, поскольку doAdd() имеет прямой доступ к значениям "value1" и "value2". Предыдущий пример был переписан ниже, чтобы показать, как doAdd() может использовать значения "value1" и "value2".
function add(value1, value2) {
function doAdd() {
return value1 + value2;
}
return doAdd();
}
var foo = add(1, 2);
// foo equals 3
Creating Closures
Закрытие создается, когда внутренняя функция становится доступной извне функции, которая ее создала. Обычно это происходит, когда внешняя функция возвращает внутреннюю функцию. Когда это происходит, внутренняя функция поддерживает ссылку на среду, в которой она была создана. Это означает, что он запоминает все переменные (и их значения), которые в то время были в области. В следующем примере показано, как создается и используется закрытие.
function add(value1) {
return function doAdd(value2) {
return value1 + value2;
};
}
var increment = add(1);
var foo = increment(2);
// foo equals 3
В этом примере можно отметить несколько вещей.
Функция add() возвращает свою внутреннюю функцию doAdd(). Возвращая ссылку на внутреннюю функцию, создается замыкание. "value1" - это локальная переменная add() и нелокальная переменная doAdd(). Нелокальные переменные относятся к переменным, которые не относятся ни к локальной, ни к глобальной области. "value2" - это локальная переменная doAdd(). Когда вызывается add (1), замыкание создается и сохраняется в "increment". В среде ссылок закрытия ссылок значение "1" привязано к значению 1. Перечисленные переменные также считаются закрытыми. Здесь происходит закрытие имени. Когда вызывается приращение (2), вводится замыкание. Это означает, что doAdd() вызывается, а переменная value1 имеет значение 1. Закрытие по существу можно рассматривать как создание следующей функции.
function increment(value2) {
return 1 + value2;
}
When to Use Closures
Закрытие может использоваться для достижения многих целей. Они очень полезны для таких вещей, как настройка функций обратного вызова с параметрами. В этом разделе рассматриваются два сценария, где закрытие может сделать вашу жизнь как разработчика намного проще.
Работа с таймерами
Замыкания полезны при использовании в сочетании с функциями setTimeout() и setInterval(). Чтобы быть более конкретным, замыкания позволяют передавать аргументы функции обратного вызова setTimeout() и setInterval(). Например, следующий код печатает строку "некоторое сообщение" один раз в секунду, вызывая showMessage().
<!DOCTYPE html>
<html lang="en">
<head>
<title>Closures</title>
<meta charset="UTF-8" />
<script>
window.addEventListener("load", function() {
window.setInterval(showMessage, 1000, "some message<br />");
});
function showMessage(message) {
document.getElementById("message").innerHTML += message;
}
</script>
</head>
<body>
<span id="message"></span>
</body>
</html>
К сожалению, Internet Explorer не поддерживает передачу аргументов обратного вызова через setInterval(). Вместо отображения "некоторого сообщения" Internet Explorer отображает "undefined" (поскольку на showMessage() фактически не передается значение). Чтобы обойти эту проблему, можно создать замыкание, которое связывает аргумент "сообщение" с желаемым значением. Затем замыкание можно использовать в качестве функции обратного вызова для setInterval(). Чтобы проиллюстрировать эту концепцию, код JavaScript из предыдущего примера был переписан ниже, чтобы использовать закрытие.
window.addEventListener("load", function() {
var showMessage = getClosure("some message<br />");
window.setInterval(showMessage, 1000);
});
function getClosure(message) {
function showMessage() {
document.getElementById("message").innerHTML += message;
}
return showMessage;
}
Эмулирование частных данных
Многие объектно-ориентированные языки поддерживают концепцию данных частного участника. Однако JavaScript не является чисто объектно-ориентированным языком и не поддерживает частные данные. Но можно эмулировать частные данные с помощью закрытий. Напомним, что замыкание содержит ссылку на среду, в которой оно было изначально создано, которое теперь выходит за рамки. Поскольку переменные в ссылочной среде доступны только из функции закрытия, они являются, по существу, частными данными.
В следующем примере показан конструктор для простого класса Person. Когда создается каждый человек, ему присваивается имя через аргумент "имя". Внутри пользователь сохраняет свое имя в переменной "_name". После хороших методов объектно-ориентированного программирования метод getName() также предоставляется для извлечения имени.
function Person(name) {
this._name = name;
this.getName = function() {
return this._name;
};
}
Есть еще одна серьезная проблема с классом Person. Поскольку JavaScript не поддерживает личные данные, ничто не мешает кому-то еще прийти и изменить имя. Например, следующий код создает Person с именем Colin, а затем изменяет свое имя на Tom.
var person = new Person("Colin");
person._name = "Tom";
// person.getName() now returns "Tom"
Лично мне это не понравилось бы, если бы кто-нибудь мог прийти и юридически изменить мое имя. Чтобы это не произошло, можно использовать закрытие для переменной "_name". Конструктор Person был переписан ниже, используя закрытие. Обратите внимание, что "_name" теперь является локальной переменной конструктора Person вместо свойства объекта. Закрытие формируется из-за того, что внешняя функция Person() предоставляет внутреннюю функцию путем создания общедоступного метода getName().
function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
}
Теперь, когда вызывается getName(), гарантировано вернуть значение, которое было первоначально передано конструктору. По-прежнему возможно, чтобы кто-то добавил к объекту новое свойство "_name", но внутренние действия объекта не будут затронуты, если они относятся к переменной, связанной закрытием. Следующий код показывает, что переменная "_name", действительно, является частной.
var person = new Person("Colin");
person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"
When Not to Use Closures
Важно понимать, как работают затворы и когда их использовать. Не менее важно понять, когда они не являются подходящим инструментом для работы. Избыточные закрытия могут привести к медленному выполнению сценариев и потреблению ненужной памяти. И поскольку замыкания настолько просты в создании, можно их неправильно использовать, даже не зная об этом. В этом разделе рассматриваются несколько сценариев, где закрытие следует использовать с осторожностью.
В циклах
Создание замыканий внутри циклов может привести к ошибочным результатам. Пример этого показан ниже. В этом примере создаются три кнопки. Когда нажимается кнопка "1", должно отображаться предупреждение, которое гласит "Щелкнуть кнопку 1". Аналогичные сообщения должны отображаться для "button2" и "button3". Однако, когда этот код запускается, на всех кнопках отображается "Щелкнутая кнопка 4". Это связано с тем, что к моменту нажатия одной из кнопок цикл завершил выполнение, а переменная цикла достигла конечного значения четыре.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Closures</title>
<meta charset="UTF-8" />
<script>
window.addEventListener("load", function() {
for (var i = 1; i < 4; i++) {
var button = document.getElementById("button" + i);
button.addEventListener("click", function() {
alert("Clicked button " + i);
});
}
});
</script>
</head>
<body>
<input type="button" id="button1" value="One" />
<input type="button" id="button2" value="Two" />
<input type="button" id="button3" value="Three" />
</body>
</html>
Чтобы решить эту проблему, замыкание должно быть отделено от фактической переменной цикла. Это можно сделать, вызвав новую функцию, которая, в свою очередь, создает новую среду ссылок. В следующем примере показано, как это делается. Переменная цикла передается функции getHandler(). getHandler() затем возвращает замыкание, которое не зависит от исходного цикла for.
function getHandler(i) {
return function handler() {
alert("Clicked button " + i);
};
}
window.addEventListener("load", function() {
for (var i = 1; i < 4; i++) {
var button = document.getElementById("button" + i);
button.addEventListener("click", getHandler(i));
}
});
Unnecessary Use in Constructors
Функции конструктора являются еще одним распространенным источником неправильного использования. Weve видел, как замыкания могут использоваться для эмуляции частных данных. Тем не менее, слишком сложно реализовать методы как закрытие, если они фактически не имеют доступа к личным данным. Следующий пример пересматривает класс Person, но на этот раз добавляет метод sayHello(), который не использует личные данные.
function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
this.sayHello = function() {
alert("Hello!");
};
}
Каждый раз, когда создается экземпляр Person, время тратится на создание метода sayHello(). Если создается много объектов Person, это становится пустой тратой времени. Лучшим подходом было бы добавить sayHello() прототипу Person. Добавляя к прототипу, все объекты Person могут использовать один и тот же метод. Это экономит время в конструкторе, не создавая закрытие для каждого экземпляра. Предыдущий пример переписан ниже с посторонним замыканием, перемещенным в прототип.
function Person(name) {
var _name = name;
this.getName = function() {
return _name;
};
}
Person.prototype.sayHello = function() {
alert("Hello!");
};
То, что нужно запомнить
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is free variable from the outer environment
}
После вызова функции она выходит за рамки. Если эта функция содержит что-то вроде функции обратного вызова, то эта функция обратного вызова все еще находится в области видимости. Если функция обратного вызова ссылается на некоторую локальную переменную в непосредственной среде родительской функции, то, естественно, вы ожидаете, что эта переменная будет недоступна для функции обратного вызова и не будет возвращена.
Закрытие гарантирует, что любое свойство, на которое ссылается функция обратного вызова, доступно для использования этой функцией, даже если его родительская функция может выйти из области видимости.
MDN объясняет это лучше всего, я думаю:
Закрытие - это функции, которые относятся к независимым (свободным) переменным. Другими словами, функция, определенная в замыкании, "запоминает" среду, в которой она была создана.
Закрытие всегда имеет внешнюю функцию и внутреннюю функцию. Внутренняя функция - это то, где происходит вся работа, а внешняя функция - это просто среда, которая сохраняет область, в которой была создана внутренняя функция. Таким образом, внутренняя функция замыкания "запоминает" среду/область, в которой она была создана. Самый классический пример - функция счетчика:
var closure = function() {
var count = 0;
return function() {
count++;
console.log(count);
};
};
var counter = closure();
counter() // returns 1
counter() // returns 2
counter() // returns 3
В приведенном выше коде count
сохраняется внешней функцией (функцией среды), так что каждый раз, когда вы вызываете counter()
, внутренняя функция (функция работы) может увеличивать ее.
Мне нравится Kyle Simpson определение закрытия:
Закрытие - это когда функция может запоминать и получать доступ к ее лексической области, даже если эта функция выполняется вне ее лексической области.
Лексическая область - это когда внутренний объем может получить доступ к его внешнему охвату.
Вот измененный пример, который он дает в своей серии книг "You Do not Know JS: Scopes & Closures".
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
function test() {
var bz = foo();
bz();
}
// prints 2. Here function bar referred by var bz is outside
// its lexical scope but it can still access it
test();
Рассмотрение вопроса состоит в том, чтобы объяснить это просто как если бы 6-летнему, я бы ответил:
"Когда вы объявляете функцию в JavaScript, она имеет навсегда доступ ко всем переменным и функциям, которые были доступны в строке до объявления этой функции. Функция и все внешние переменные и функции, к которым у нее имеется доступ, - это то, что мы называем закрытием. "
Вот как новичок обернул одну голову вокруг Closures, как функция, обернутая внутри тела функций, также известное как Closures.
Определение из книги Говорящий JavaScript "Закрытие - это функция плюс соединение с областью действия, в которой была создана функция" -Dr.Axel Rauschmayer
Так что же это могло быть? Вот пример
function newCounter() {
var counter = 0;
return function increment() {
counter += 1;
}
}
var counter1 = newCounter();
var counter2 = newCounter();
counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3
newCounter закрывается с увеличением, счетчик может ссылаться на инкремент и получать доступ.
counter1 и counter2 будут отслеживать их собственную ценность.
Простая, но, надеюсь, четкая перспектива того, что закрытие вокруг всех этих больших и расширенных ответов.
Для шестилетнего...
Вы знаете, какие объекты?
Объекты - это вещи, которые обладают свойствами и делают вещи.
Одна из самых важных вещей о закрытии - это то, что они позволяют создавать объекты в JavaScript. Объекты в JavaScript - это просто функции и блокировки, которые позволяют JavaScript сохранять значение свойства для объекта после его создания.
Объекты очень полезны и сохраняют все красивое и организованное. Различные объекты могут выполнять разные задания, а совместная работа объектов может делать сложные вещи.
Ему повезло, что у JavaScript есть закрытие для создания объектов, иначе все станет грязным кошмаром.
Закрытие - это когда функция закрывается таким образом, что она была определена в пространстве имен, которое неизменено к моменту вызова функции.
В JavaScript это происходит, когда вы:
// 'name' is resolved in the namespace created for one invocation of bindMessage
// the processor cannot enter this namespace by the time displayMessage is called
function bindMessage(name, div) {
function displayMessage() {
alert('This is ' + name);
}
$(div).click(displayMessage);
}
Закрытие нетрудно понять. Это зависит только от точки зрения.
Я лично люблю использовать их в повседневной жизни.
function createCar()
{
var rawMaterial = [/* lots of object */];
function transformation(rawMaterials)
{
/* lots of changement here */
return transformedMaterial;
}
var transformedMaterial = transformation(rawMaterial);
function assemblage(transformedMaterial)
{
/*Assemblage of parts*/
return car;
}
return assemblage(transformedMaterial);
}
В отдельных случаях нам нужно только пройти определенные шаги. Что касается преобразования материалов, то это полезно только тогда, когда у вас есть детали.
Там когда-то был пещерный человек
function caveman {
у которого был особый камень,
var rock = "diamond";
Вы не могли получить камень, потому что это было в пещерной частной пещере. Только пещерный человек знал, как найти и получить камень.
return {
getRock: function() {
return rock;
}
};
}
К счастью, он был дружелюбным пещерным человеком, и, если бы вы были готовы дождаться его возвращения, он с радостью воспримет это для вас.
var friend = caveman();
var rock = friend.getRock();
Довольно умный пещерный человек.
Лучший способ - объяснить эти понятия постепенно:
переменные
console.log(x);
// undefined
Здесь undefined
- это способ JavaScript: "Я не знаю, что означает x
".
Переменные - это теги.
Вы можете сказать, что теги x
указывают значение 42
:
var x = 42;
console.log(x);
// 42
Теперь JavaScript знает, что означает x
.
Вы также можете повторно назначить переменную.
Сделать тег x
указывает на другое значение:
x = 43;
console.log(x);
// 43
Теперь x
означает что-то еще.
Объем
Когда вы создаете функцию, функция имеет свой "ящик" для переменных.
function A() {
var x = 42;
}
console.log(x);
// undefined
Из-за пределов коробки вы не можете видеть, что внутри коробки.
Но изнутри коробки вы можете видеть, что вне этого поля:
var x = 42;
function A() {
console.log(x);
}
// 42
Внутри функции
A
вас есть "доступ к области доступа" кx
.
Теперь, если у вас есть две коробки бок о бок:
function A() {
var x = 42;
}
function B() {
console.log(x);
}
// undefined
Внутри функции
B
вас нет доступа к переменным внутри функцииA
Но если вы положили функцию функции B
внутри функции A
:
function A() {
var x = 42;
function B() {
console.log(x);
}
}
// 42
Теперь у вас есть "доступ к области доступа".
функции
В JavaScript вы запускаете функцию, вызывая ее:
function A() {
console.log(42);
}
Как это:
A();
// 42
Функции как значения
В JavaScript вы можете указать тег на функцию, точно так же, как указывать на число:
var a = function() {
console.log(42);
};
Переменная
a
теперь означает функцию, вы можете ее запустить.
a();
// 42
Вы также можете передать эту переменную:
setTimeout(a, 1000);
Через секунду (1000 миллисекунд) функция a
указывает на:
// 42
Закрытие
Теперь, когда вы определяете функции, эти функции имеют доступ к своим внешним областям.
Когда вы передаете функции как значения, было бы сложно, если бы этот доступ был потерян.
В JavaScript функции сохраняют свой доступ к внешним переменным области. Даже когда их передают, чтобы их можно было запустить где-то в другом месте.
var a = function() {
var text = 'Hello!'
var b = function() {
console.log(text);
// inside function 'b', you have access to 'text'
};
// but you want to run 'b' later, rather than right away
setTimeout(b, 1000);
}
Что происходит сейчас?
// 'Hello!'
Или рассмотрите это:
var c;
var a = function() {
var text = 'Hello!'
var b = function() {
console.log(text);
// inside function 'b', you have access to 'text'
};
c = b;
}
// now we are out side of function 'a'
// call 'a' so the code inside 'a' runs
a();
// now 'c' has a value that is a function
// because what happened when 'a' ran
// when you run 'c'
c();
// 'Hello!'
Вы по-прежнему можете получить доступ к переменным в области закрытия.
Несмотря на то, закончит работу, и теперь вы работаете a
c
вне. a
То, что здесь произошло, называется " закрытие " в JavaScript.
Давайте начнем здесь. Как определено в MDN: Closures - это функции, которые ссылаются на независимые (свободные) переменные (переменные, которые используются локально, но определенные в охватывающей области). Другими словами, эти функции "запоминают" среду, в которой они были созданы.
Лексический обзор
Рассмотрим следующее:
function init() {
var name = 'Mozilla'; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert(name); // use variable declared in the parent function
}
displayName();
}
init();
init() создает локальную переменную с именем name и функцию, называемую displayName(). Функция displayName() является внутренней функцией, которая определена внутри init() и доступна только в теле функции init(). Функция displayName() не имеет собственных локальных переменных. Однако, поскольку внутренние функции имеют доступ к переменным внешних функций, displayName() может получить доступ к имени переменной, объявленному в родительской функции, init().
function init() {
var name = "Mozilla"; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert (name); // displayName() uses variable declared in the parent function
}
displayName();
}
init();
Запустите код и обратите внимание, что оператор alert() в функции displayName() успешно отображает значение переменной name, которое объявляется в его родительской функции. Это пример лексического охвата, который описывает, как парсер разрешает имена переменных, когда функции вложены. Слово "лексическое" относится к тому факту, что в лексическом охвате используется место, где переменная объявляется в исходном коде, чтобы определить, где эта переменная доступна. Вложенные функции имеют доступ к переменным, объявленным в их внешней области.
закрытие
Теперь рассмотрим следующий пример:
function makeFunc() {
var name = 'Mozilla';
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
Выполнение этого кода имеет тот же эффект, что и предыдущий пример функции init(): на этот раз строка "Mozilla" будет отображаться в окне предупреждения JavaScript. Что другое - и интересно - то, что внутренняя функция displayName() возвращается из внешней функции перед выполнением.
На первый взгляд может показаться неинтуитивным, что этот код все еще работает. В некоторых языках программирования локальные переменные внутри функции существуют только для продолжительности выполнения этой функции. Как только makeFunc() завершит выполнение, вы можете ожидать, что переменная name больше не будет доступна. Однако, поскольку код по-прежнему работает так, как ожидалось, в JavaScript это явно не так.
Причина в том, что функции в JavaScript закрываются. Закрытие представляет собой комбинацию функции и лексической среды, в которой была объявлена эта функция. Эта среда состоит из любых локальных переменных, которые были в области видимости в момент создания закрытия. В этом случае myFunc является ссылкой на экземпляр функции displayName, созданной при запуске makeFunc. Экземпляр displayName поддерживает ссылку на его лексическую среду, в которой существует имя переменной. По этой причине, когда myFunc вызывается, имя переменной остается доступным для использования, а "Mozilla" передается в alert.
Вот несколько интересный пример - функция makeAdder:
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
В этом примере мы определили функцию makeAdder (x), которая принимает единственный аргумент x и возвращает новую функцию. Возвращаемая функция принимает один аргумент y и возвращает сумму x и y.
По сути, makeAdder является фабрикой функций - он создает функции, которые могут добавить определенное значение к их аргументу. В приведенном выше примере мы используем нашу фабрику функций, чтобы создать две новые функции - одну, которая добавляет 5 к ее аргументу, и добавляет 10.
add5 и add10 - оба закрытия. Они используют одно и то же определение тела, но сохраняют разные лексические среды. В лексической среде add5 x равно 5, тогда как в лексической среде для add10 x равно 10.
Практические замыкания
Закрытие полезно, поскольку они позволяют связать некоторые данные (лексическую среду) с функцией, которая работает с этими данными. Это имеет очевидные параллели с объектно-ориентированным программированием, где объекты позволяют нам связать некоторые данные (свойства объекта) с одним или несколькими методами.
Следовательно, вы можете использовать закрытие в любом месте, где вы обычно можете использовать объект только с одним методом.
Ситуации, в которых вы, возможно, захотите сделать это, особенно распространены в Интернете. Большая часть кода, который мы пишем в интерфейсе JavaScript, основана на событиях - мы определяем какое-то поведение, а затем присоединяем его к событию, которое запускается пользователем (например, щелчком или нажатием). Наш код обычно прикрепляется как обратный вызов: одна функция, которая выполняется в ответ на событие.
Например, предположим, что мы хотим добавить несколько кнопок на страницу, которые изменяют размер текста. Один из способов сделать это - указать размер шрифта элемента body в пикселях, а затем установить размер других элементов на странице (например, заголовки) с помощью относительной единицы em:
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
Кнопки интерактивного текстового размера могут изменять свойство font-size элемента body, а настройки будут отображены другими элементами на странице благодаря относительным единицам. Здесь JavaScript:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
size12, size14 и size16 теперь являются функциями, которые будут изменять размер текста тела до 12, 14 и 16 пикселей соответственно. Мы можем прикрепить их к кнопкам (в данном случае ссылкам) следующим образом:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
для получения дополнительной информации о закрытии посетите ссылку на MDN
Самый простой пример использования, который я могу придумать, чтобы объяснить закрытие JavaScript, - это шаблон модуля. В шаблоне модуля вы определяете функцию и сразу вызываете ее в так называемом выражении с выраженной немедленной функцией (IIFE). Все, что вы пишете внутри этой функции, имеет личную область видимости, потому что она определена внутри закрытия, что позволяет вам "имитировать" конфиденциальность в JavaScript. Вот так:
var Closure = (function () {
// This is a closure
// Any methods, variables and properties you define here are "private"
// and can't be accessed from outside the function.
//This is a private variable
var foo = "";
//This is a private method
var method = function(){
}
})();
Если, с другой стороны, вы хотите сделать одну или несколько переменных или методов видимыми вне закрытия, вы можете вернуть их внутри литерала объекта. Вот так:
var Closure = (function () {
// This is a closure
// Any methods, variables and properties you define here are "private"
// and can't be accessed from outside the function.
//This is a private variable
var foo = "";
//This is a private method
var method = function(){
}
//The method will be accessible from outside the closure
return {
method: method
}
})();
Closure.method();
Надеюсь, поможет. С Уважением,
Я думаю, что было бы полезно сделать шаг назад и рассмотреть более общее понятие "закрытия" - так называемого "оператора объединения".
В математике оператор "join" представляет собой функцию на частично упорядоченном множестве, которая возвращает наименьший объект, который больше или равен его аргументам. В символах присоединяем [a, b] = d такие, что d> = a и d> = b, но не существует e такое, что d> e> = a или d> e> = b.
Таким образом, соединение дает вам самую маленькую вещь "больше", чем части.
Теперь обратите внимание, что области JavaScript являются частично упорядоченной структурой. Итак, есть разумное понятие о соединении. В частности, объединение областей является наименьшим масштабом, большим, чем исходные области. Эта область называется закрытием.
Таким образом, замыкание переменных a, b, c является наименьшей областью (в решетке областей вашей программы!), Которая приносит a, b и c в объем.
Я читал все это раньше, и все они очень информативны. Некоторые очень близки к тому, чтобы получить простое объяснение, а затем получить сложный или остаться абстрактным, превзойдя цель и не продемонстрировать очень простое использование в реальном мире.
Хотя вы прочесываете все примеры и объяснения, вы получаете представление о закрытии и отсутствии комментариев и кода, я все еще был недоволен очень простой иллюстрацией, которая помогла мне получить полезность закрытия, не становясь настолько сложной. Моя жена хочет научиться кодированию, и я подумал, что мне нужно показать здесь не только то, что, но почему и как.
Я не уверен, что шестилетний получит это, но я думаю, что это может быть немного ближе к демонстрации простого случая в реальном мире, который может быть полезным, и это легко понять.
Один из лучших (или наиболее близких к простейшему) - это пересказ Морриса "Закрытие для чайников".
Взяв концепцию "SayHi2Bob" всего на один шаг, мы продемонстрируем две основные вещи, которые вы можете получить от чтения всех ответов:
Доказав это и продемонстрировав это, я сделал небольшую скрипку:
function sayHello(name) {
var text = 'Hello ' + name; // Local variable
console.log(text);
var sayAlert = function () {
alert(text);
}
return sayAlert;
}
sayHello();
/* This will write 'Hello undefined' to the console (in Chrome anyway),
but will not alert though since it returns a function handle to nothing).
Since no handle or reference is created, I imagine a good js engine would
destroy/dispose of the internal sayAlert function once it completes. */
// Create a handle/reference/instance of sayHello() using the name 'Bob'
sayHelloBob = sayHello('Bob');
sayHelloBob();
// Create another handle or reference to sayHello with a different name
sayHelloGerry = sayHello('Gerry');
sayHelloGerry();
/* Now calling them again demonstrates that each handle or reference contains its own
unique local variable memory space. They remain in memory 'forever'
(or until your computer/browser explode) */
sayHelloBob();
sayHelloGerry();
Это демонстрирует обе основные концепции, которые вы должны получить о закрытии.
Простыми словами, чтобы объяснить, почему это полезно, у меня есть базовая функция, к которой я могу делать ссылки или дескрипторы, содержащие уникальные данные, которые сохраняются в этой ссылке на память. Мне не нужно переписывать функцию каждый раз, когда я хочу сказать кому-то имя. Я инкапсулировал эту рутину и сделал ее многоразовой.
Для меня это приводит, по крайней мере, к базовым понятиям конструкторов, oop-практик, синглетов и экземпляров с их собственными данными и т.д. И т.д.
Если вы начинаете с неофитом, тогда вы можете перейти к более сложным свойствам объекта/членам, и, надеюсь, эти понятия несут.
Закрытие слова просто означает возможность доступа к объектам (шестилетним: вещи), которые закрыты (шестилетние: частные) в рамках функции (шестилетняя: коробка). Даже если функция (шестилетняя: коробка) выходит за рамки (шестилетний: отправлен далеко).
Закрытие в основном создает две вещи: - функция - частная область, к которой может обращаться только эта функция
Это похоже на то, что некоторые функции покрывают.
Таким образом, для 6-летнего, это можно объяснить, давая аналогию. Скажем, я строю робота. Этот робот может многое сделать. Среди этих вещей я запрограммировал его, чтобы подсчитать количество птиц, которых он видит в небе. Каждый раз, когда он видел 25 птиц, он должен был сказать мне, сколько птиц он видел с самого начала.
Я не знаю, сколько птиц он видел, если он не сказал мне. Только он знает. Это личное пространство. Это в основном память робота. Скажем, я дал ему 4 ГБ.
Рассказывая мне, сколько птиц он видел, это возвращаемая функция. Я также создал это.
Эта аналогия немного сосательна, но кто-то может ее улучшить, я думаю.
Моя перспектива закрытия:
Закрытие можно сравнить с книгой с закладкой на книжной полке.
Предположим, вы прочитали книгу, и вам нравится какая-то страница в книге. Вы поместите закладку на эту страницу, чтобы ее отслеживать.
Теперь, когда вы закончите читать книгу, вам больше не нужна книга, за исключением того, что вы хотите иметь доступ к этой странице. Вы могли бы просто вырезать страницу, но тогда вы потеряете контекст в истории. Таким образом, вы положили книгу на свою книжную полку с закладкой.
Это похоже на замыкание. Книга представляет собой внешнюю функцию, а страница - это ваша внутренняя функция, которая возвращается из внешней функции. Закладка - это ссылка на вашу страницу, а контекст истории - лексический охват, который вам нужно сохранить. Книжная полка представляет собой стек функций, который не может быть очищен от старых книг, пока вы не удерживаетесь на странице.
Пример кода:
function book() {
var pages = [....]; //array of pages in your book
var bookMarkedPage = 20; //bookmarked page number
function getPage(){
return pages[bookMarkedPage];
}
return getPage;
}
var myBook = book(),
myPage = myBook.getPage();
Когда вы запускаете функцию book()
, вы выделяете память в стек для функции, в которой она запускается. Но поскольку она возвращает функцию, память не может быть выпущена, так как внутренняя функция имеет доступ к переменным из контекста вне ее, в этом случае "страницы" и "bookMarkedPage".
Таким образом, функция call book()
возвращает ссылку на закрытие, то есть не только функцию, но и ссылку на книгу и ее контекст, то есть ссылку на функцию getPage, состояние страниц и переменные bookMarkedPage.
Некоторые моменты, которые следует учитывать:
Пункт 1: Книжная полка, как и стек функций, имеет ограниченное пространство, поэтому используйте ее с умом.
Пункт 2: Подумайте о том, нужно ли держать всю книгу, когда вы просто хотите отслеживать одну страницу. Вы можете освободить часть памяти, не сохраняя все страницы в книге при возврате закрытия.
Это моя перспектива закрытия. Надеюсь, что это поможет, и если кто-нибудь подумает, что это не правильно, пожалуйста, дайте мне знать, поскольку мне очень интересно понять еще больше о областях и закрытиях!
Также... Возможно, мы должны немного сократить ваш 27-летний друг, потому что вся концепция "закрытия" на самом деле (!)... вуду!
Под этим я подразумеваю: (а) вы не интуитивно ожидаете этого... И... (б) когда кто-то тратит время, чтобы объяснить это вам, вы, конечно же, не ожидаете, что это сработает!
Интуиция говорит вам, что "это должно быть глупость... конечно, это должно привести к какой-то синтаксической ошибке или чему-то еще!" Как на самом деле (!) Вы могли бы, по сути, "вытащить функцию из" середины ", где бы она ни была", чтобы вы могли [все еще] иметь доступ на чтение/запись к контексту "везде, это-было-у?!"
Когда вы, наконец, поймете, что такое возможно, тогда... уверен... любая реакция после-факта будет: "whoa-aaa (!)... kew-el-lll... (!! !)"
Но сначала будет преодолено "большое противоинтуитивное препятствие". Интуиция дает вам множество совершенно правдоподобных ожиданий, что такая вещь будет "конечно, абсолютно бессмысленной и, следовательно, совершенно невозможной".
Как я сказал: "Это вуду".
Закрытие представляет собой блок кода, где каждая строка может ссылаться на тот же набор переменных с одинаковыми именами переменных.
Если "это" означает нечто иное, чем в другом месте, то вы знаете, что это два разных закрытия.
Закрытие происходит просто, когда функция имеет доступ к своей внешней области действия даже после завершения выполнения функции области видимости. Пример:
function multiplier(n) {
function multiply(x) {
return n*x;
}
return mutliply;
}
var 10xmultiplier = multiplier(10);
var x = 10xmultiplier(5); // x= 50
мы видим, что даже после того, как множитель завершил выполнение, внутренняя функция multiply все еще получает доступ к значению x, которое в этом примере равно 10.
Очень распространенное использование закрытий - это currying (тот же пример выше), где мы спешим нашу функцию постепенно с параметрами вместо того, чтобы поставлять все аргументы за один раз.
Мы можем добиться этого, потому что Javascript (в дополнение к прототипному ООП) позволяет программировать функционально, когда функции более высокого порядка могут принимать другие функции в качестве аргументов (функции класса fisrt). функциональное программирование в Википедии
Я настоятельно рекомендую вам прочитать эту книгу Кайла Симпсона: 2 одна часть серии книг посвящена закрытию, и она называется областью и закрытием. вы не знаете js: бесплатное чтение на github
Закрытие может представлять собой частные и общедоступные переменные или функции.
var ClusureDemo = function() {
//privare variables
var localVa1, localVa2;
//private functions
var setVaOne = function(newVa) {
localVa1 = newVa;
},
setVaTwo = function(newVa) {
localVa2 = newVa;
},
getVaOne = function() {
return localVa1;
},
getVaTwo = function() {
return localVa2;
};
return {
//public variables and functions
outVaOne : localVa1,
outVaTwo : localVa2,
setVaOne : setVaOne,
setVaTwo : setVaTwo,
getVaOne : getVaOne,
getVaTwo : getVaTwo
};
};
//Test Demo
var app = new ClusureDemo();
app.outVaOne = 'Hello Variable One';
app.outVaTwo = 'Hello Variable Two';
app.setVaOne(app.outVaOne);
app.setVaTwo(app.outVaTwo);
alert(app.getVaOne());
alert(app.getVaTwo());
Независимо от того, какой объект мы используем для переноса внутренней функции вне ее лексической области, он будет поддерживать ссылку на область, где она была первоначально объявлена, и где бы мы ее не выполняли, это закрытие будет осуществляться.
Закрытие - это функция, которая имеет доступ к информации из среды, в которой она была определена.
Для некоторых информация является значением в среде на момент создания. Для других информация является переменными в окружающей среде на момент создания.
Если лексическая среда, к которой относится замыкание, принадлежит к функции, которая вышла, то (в случае замыкания, относящегося к переменным в среде) эти лексические переменные будут продолжать существовать для ссылки замыканием.
Закрытие можно рассматривать как частный случай глобальных переменных - с частной копией, созданной только для функции.
Или это можно рассматривать как метод, в котором среда является конкретным экземпляром объекта, свойства которого являются переменными в среде.
Первый (замыкание как среда), аналогичный последнему, где копия окружения является переменной контекста, передаваемой каждой функции в первом, а переменные экземпляра образуют контекстную переменную в последнем.
Таким образом, замыкание является способом вызова функции без необходимости явно указывать контекст как параметр или как объект в вызове метода.
var closure = createclosure(varForClosure);
closure(param1); // closure has access to whatever createclosure gave it access to,
// including the parameter storing varForClosure.
против
var contextvar = varForClosure; // use a struct for storing more than one..
contextclosure(contextvar, param1);
против
var contextobj = new contextclass(varForClosure);
contextobj->objclosure(param1);
Для поддерживаемого кода я рекомендую объектно-ориентированный подход. Однако для быстрого и легкого набора задач (например, для создания обратного вызова) закрытие может стать естественным и более понятным, особенно в контексте функций lamda или анонимных функций.
Пример вызывающей функции.
var clicked = false;
for(var i=0;i<temp.length;i++){
(function(index){
if(clicked) return false;
$(temp[index]).click(function(){
if($(temp[index]).text()=="" && !$(".cell1").val()){
$(this).text(player1Val);
$(".cell1").val(true);
console.log("first player clicked ");
clicked = true;
return false;
}
});
})(i);
}