Вопрос: Как работают блокировки JavaScript?


Как бы вы объяснили закрытие JavaScript для кого-то, у кого есть знания о концепциях, из которых они состоят (например, функции, переменные и т. П.), Но не понимают самих замыканий?

я видел пример схемы данный в Википедии, но, к сожалению, это не помогло.


7654


источник


Ответы:


Закрытие 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');

Пример закрытия

Два предложения по одному предложению:

  • Закрытие - один из способов поддержки первоклассные функции ; это выражение, которое может ссылаться на переменные в пределах своей области (когда оно было объявлено ранее), назначаться переменной, передаваться как аргумент функции или возвращаться как результат функции.

  • Или закрытие представляет собой стек стека, который выделяется, когда функция начинает свое выполнение, и не освобожден после возвращения функции (как если бы «стек стека» был выделен в куче, а не в стеке!).

Следующий код возвращает ссылку на функцию:

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); }объявляется внутри другая функция, 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 ссылка на функцию также имеет секретную ссылку на закрытие, в которой она была создана, - подобно тому, как делегаты являются указателем метода плюс секретная ссылка на объект.

Дополнительные примеры

По какой-то причине закрытие кажется очень трудно понять, когда вы читаете о них, но когда вы видите несколько примеров, становится ясно, как они работают (мне потребовалось некоторое время). Я рекомендую внимательно изучить примеры, пока вы не поймете, как они работают. Если вы начнете использовать закрытие без полного понимания того, как они работают, вы скоро создадите очень странные ошибки!

Пример 3.

Этот пример показывает, что локальные переменные не копируются - они хранятся по ссылке. Это похоже на сохранение фрейма стека в памяти при выходе из внешней функции!

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

Пример 4.

Все три глобальных функции имеют общую ссылку на одна и та же потому что все они объявлены в течение одного 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()снова, то создается новое закрытие (стек-кадр!). Старый gLogNumber, gIncreaseNumber, gSetNumberпеременные перезаписываются новый функции, которые имеют новое закрытие. (В JavaScript, когда вы объявляете функцию внутри другой функции, внутренняя функция (ы) снова воссоздается каждый когда вызывается внешняя функция.)

Пример 5.

Этот пример показывает, что замыкание содержит любые локальные переменные, которые были объявлены внутри внешней функции до ее выхода. Заметим, что переменная aliceфактически объявляется после анонимной функции. Анонимная функция объявляется первой; и когда эта функция называется, она может получить доступ к aliceпеременная, поскольку aliceнаходится в той же области (JavaScript делает переменный подъем ). Также 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"

Трудность: обратите внимание также, что sayпеременная также находится внутри замыкания, и к ней может обращаться любая другая функция, которая может быть объявлена ​​внутри sayAlice(), или он может быть рекурсивно доступен внутри внутренней функции.

Пример 6.

Это для всех людей настоящая магия, поэтому вам нужно это понять. Будьте очень осторожны, если вы определяете функцию внутри цикла: локальные переменные из замыкания могут не действовать так, как вы могли бы сначала подумать.

Вам нужно понять функцию «переменной подъема» в 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

Пример 7.

В этом последнем примере каждый вызов основной функции создает отдельное закрытие.

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(…)( Конструктор функций ) внутри функции, она не создает замыкания. (Новая функция не может ссылаться на локальные переменные внешней функции.)
  • Закрытие в JavaScript подобно хранению копии всех локальных переменных, как и при выходе из функции.
  • Вероятно, лучше всего подумать, что замыкание всегда создается как запись функции, а локальные переменные добавляются к этому закрытию.
  • Новый набор локальных переменных сохраняется каждый раз, когда вызывается функция с замыканием (учитывая, что функция содержит внутри нее декларацию функции, либо ссылка на эту внутреннюю функцию либо возвращена, либо внешняя ссылка хранится для нее каким-то образом ).
  • Две функции могут выглядеть так, как будто они имеют один и тот же исходный текст, но имеют совершенно другое поведение из-за их скрытого закрытия. Я не думаю, что JavaScript-код действительно может узнать, есть ли у функции ссылка закрытие или нет.
  • Если вы пытаетесь выполнить любые изменения динамического исходного кода (например: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), он не будет работать, если myFunctionявляется закрытием (конечно, вы даже не подумали бы о замене строки исходного кода во время выполнения, но ...).
  • Можно получить объявления функций внутри деклараций функций внутри функций - и вы можете получить закрытие на более чем одном уровне.
  • Я думаю, что обычно замыкание - это термин как для функции, так и для захваченных переменных. Обратите внимание, что я не использую это определение в этой статье!
  • Я подозреваю, что замыкания в JavaScript отличаются от тех, которые обычно встречаются на функциональных языках.

связи

благодаря

Если у вас есть просто (здесь или где-либо еще!), тогда меня интересует любая обратная связь от вас о любых изменениях, которые вы могли бы предложить, чтобы сделать эту статью более ясной. Отправить сообщение для morrisjohns.com (morris_closure @). Обратите внимание, что я не гуру на JavaScript - ни на закрытии.


Оригинальный пост Морриса можно найти в Интернет-архив ,


6047



Всякий раз, когда вы видите ключевое слово 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.


3782



ПРЕДИСЛОВИЕ: этот ответ был написан, когда вопрос был:

Как и старый Альберт сказал: «Если вы не можете объяснить это шестилетнему ребенку, вы действительно этого не понимаете сами». Ну, я попытался объяснить закрытие 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();

И хотя взрослые знали о настоящих принцессах, они никогда не поверили бы в единорогов или драконов, потому что они никогда их не увидели. Взрослые говорили, что они существуют только внутри воображения маленькой девочки.

Но мы знаем истинную истину; что маленькая девочка с принцессой внутри ...

... на самом деле принцесса с маленькой девочкой внутри.


2230



Принимая этот вопрос всерьез, мы должны выяснить, что типичный 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();

kitchen.getTrashBag(); // returns trash bag C
kitchen.getTrashBag(); // returns trash bag B
kitchen.getTrashBag(); // returns trash bag A

Дальнейшие моменты, объясняющие, почему замыкания интересны:

  • Каждый раз makeKitchen()называется, новое закрытие создается с его собственным отдельным trashBags,
  • trashBagsпеременная локальна внутри каждой кухни и недоступна снаружи, но внутренняя функция на getTrashBagсвойство имеет доступ к нему.
  • Каждый вызов функции создает замыкание, но нет необходимости держать замыкание, если внутренняя функция, которая имеет доступ к внутренней части замыкания, может быть вызвана из-за закрытия. Возврат объекта с помощью getTrashBagфункция делает это здесь.

677



The Straw Man

I need to know how many times a button has been clicked and do something on every third click...

Fairly Obvious Solution

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Now this will work, but it does encroach into the outer scope by adding a variable, whose sole purpose is to keep track of the count. In some situations, this would be preferable as your outer application might need access to this information. But in this case, we are only changing every third click's behavior, so it is preferable to enclose this functionality inside the event handler.

Consider this option

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Notice a few things here.

In the above example, I am using the closure behavior of JavaScript. This behavior allows any function to have access to the scope in which it was created, indefinitely. To practically apply this, I immediately invoke a function that returns another function, and because the function I'm returning has access to the internal count variable (because of the closure behavior explained above) this results in a private scope for usage by the resulting function... Not so simple? Let's dilute it down...

A simple one-line closure

//          _______________________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); }; })();

All variables outside the returned function are available to the returned function, but they are not directly available to the returned function object...

func();  // Alerts "val"
func.a;  // Undefined

Get it? So in our primary example, the count variable is contained within the closure and always available to the event handler, so it retains its state from click to click.

Also, this private variable state is fully accessible, for both readings and assigning to its private scoped variables.

There you go; you're now fully encapsulating this behavior.

Full Blog Post (including jQuery considerations)


514



Closures are hard to explain because they are used to make some behaviour work that everybody intuitively expects to work anyway. I find the best way to explain them (and the way that I learned what they do) is to imagine the situation without them:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

What would happen here if JavaScript didn't know closures? Just replace the call in the last line by its method body (which is basically what function calls do) and you get:

console.log(x + 3);

Now, where's the definition of x? We didn't define it in the current scope. The only solution is to let plus5 carry its scope (or rather, its parent's scope) around. This way, x is well-defined and it is bound to the value 5.


433



This is an attempt to clear up several (possible) misunderstandings about closures that appear in some of the other answers.

  • A closure is not only created when you return an inner function. In fact, the enclosing function does not need to return at all in order for its closure to be created. You might instead assign your inner function to a variable in an outer scope, or pass it as an argument to another function where it could be called immediately or any time later. Therefore, the closure of the enclosing function is probably created as soon as the enclosing function is called since any inner function has access to that closure whenever the inner function is called, before or after the enclosing function returns.
  • A closure does not reference a copy of the old values of variables in its scope. The variables themselves are part of the closure, and so the value seen when accessing one of those variables is the latest value at the time it is accessed. This is why inner functions created inside of loops can be tricky, since each one has access to the same outer variables rather than grabbing a copy of the variables at the time the function is created or called.
  • The "variables" in a closure include any named functions declared within the function. They also include arguments of the function. A closure also has access to its containing closure's variables, all the way up to the global scope.
  • Closures use memory, but they don't cause memory leaks since JavaScript by itself cleans up its own circular structures that are not referenced. Internet Explorer memory leaks involving closures are created when it fails to disconnect DOM attribute values that reference closures, thus maintaining references to possibly circular structures.

331



OK, 6-year-old closures fan. Do you want to hear the simplest example of closure?

Let's imagine the next situation: a driver is sitting in a car. That car is inside a plane. Plane is in the airport. The ability of driver to access things outside his car, but inside the plane, even if that plane leaves an airport, is a closure. That's it. When you turn 27, look at the more detailed explanation or at the example below.

Here is how I can convert my plane story into the code.

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");

319



A closure is much like an object. It gets instantiated whenever you call a function.

The scope of a closure in JavaScript is lexical, which means that everything that is contained within the function the closure belongs to, has access to any variable that is in it.

A variable is contained in the closure if you

  1. assign it with var foo=1; or
  2. just write var foo;

If an inner function (a function contained inside another function) accesses such a variable without defining it in its own scope with var, it modifies the content of the variable in the outer closure.

A closure outlives the runtime of the function that spawned it. If other functions make it out of the closure/scope in which they are defined (for instance as return values), those will continue to reference that closure.

Example

 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();

Output

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

307



I wrote a blog post a while back explaining closures. Here's what I said about closures in terms of why you'd want one.

Closures are a way to let a function have persistent, private variables - that is, variables that only one function knows about, where it can keep track of info from previous times that it was run.

In that sense, they let a function act a bit like an object with private attributes.

Full post:

So what are these closure thingys?


207



Closures are simple:

The following simple example covers all the main points of JavaScript closures.*  

Here is a factory that produces calculators that can add and multiply:

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

The key point: Each call to make_calculator creates a new local variable n, which continues to be usable by that calculator's add and multiply functions long after make_calculator returns.

If you are familiar with stack frames, these calculators seem strange: How can they keep accessing n after make_calculator returns? The answer is to imagine that JavaScript doesn't use "stack frames", but instead uses "heap frames", which can persist after the function call that made them returns.

Inner functions like add and multiply, which access variables declared in an outer function**, are called closures.

That is pretty much all there is to closures.



* For example, it covers all the points in the "Closures for Dummies" article given in another answer, except example 6, which simply shows that variables can be used before they are declared, a nice fact to know but completely unrelated to closures. It also covers all the points in the accepted answer, except for the points (1) that functions copy their arguments into local variables (the named function arguments), and (2) that copying numbers creates a new number, but copying an object reference gives you another reference to the same object. These are also good to know but again completely unrelated to closures. It is also very similar to the example in this answer but a bit shorter and less abstract. It does not cover the point of this answer or this comment, which is that JavaScript makes it difficult to plug the current value of a loop variable into your inner function: The "plugging in" step can only be done with a helper function that encloses your inner function and is invoked on each loop iteration. (Strictly speaking, the inner function accesses the helper function's copy of the variable, rather than having anything plugged in.) Again, very useful when creating closures, but not part of what a closure is or how it works. There is additional confusion due to closures working differently in functional languages like ML, where variables are bound to values rather than to storage space, providing a constant stream of people who understand closures in a way (namely the "plugging in" way) that is simply incorrect for JavaScript, where variables are always bound to storage space, and never to values.

** Any outer function, if several are nested, or even in the global context, as this answer points out clearly.


191