Как работают замыкания в JavaScript?

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

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

Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
7 631
0
1 496 003
82

Ответы 82

TL; DR

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

Подробности

В терминологии спецификации ECMAScript можно сказать, что замыкание реализуется ссылкой [[Environment]] каждого объекта-функции, которая указывает на лексическая среда, в котором определена функция.

Когда функция вызывается через внутренний метод [[Call]], ссылка [[Environment]] на объект-функцию копируется в ссылка на внешнюю средузапись окружающей среды вновь созданного контекст исполнения (кадра стека).

В следующем примере функция f закрывает лексическое окружение глобального контекста выполнения:

function f() {}

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

function g() {
    function h() {}
}

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

В следующем примере функция j закрывается над лексической средой функции i, что означает, что переменная x видна изнутри функции j, спустя много времени после того, как функция i завершила выполнение:

function i() {
    var x = 'mochacchino'
    return function j() {
        console.log('Printing the value of x, from within function j: ', x)
    }
} 

const k = i()
setTimeout(k, 500) // invoke k (which is j) after 500ms

В замыкании доступны переменные внешнего лексического окружения самих себя, нет копирует.

function l() {
  var y = 'vanilla';

  return {
    setY: function(value) {
      y = value;
    },
    logY: function(value) {
      console.log('The value of y is: ', y);
    }
  }
}

const o = l()
o.logY() // The value of y is: vanilla
o.setY('chocolate')
o.logY() // The value of y is: chocolate

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

Обратите внимание, что в попытке улучшить ясность и точность этот ответ был существенно изменен по сравнению с оригиналом.

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

const makePlus = function(x) {
    return function(y) { return x + y; };
}

const plus5 = makePlus(5);
console.log(plus5(3));

Что бы здесь произошло, если бы JavaScript не сделал знал замыкания? Просто замените вызов в последней строке его телом метода (что в основном то, что делают вызовы функций), и вы получите:

console.log(x + 3);

А где определение x? Мы не определили его в текущем объеме. Единственное решение - оставить plus5нести свою область видимости (или, скорее, его родительскую область). Таким образом, x четко определен и привязан к значению 5.

«они используются для того, чтобы заставить работать какое-то поведение, которое все интуитивно ожидает в любом случае». Оцените этот комментарий, поскольку отчасти это было то, с чем я боролся. Мне казалось, что я чего-то упускаю, но оказалось, что это не так!

Shane 10.07.2020 21:45

Каждая функция в 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 и переменную tmp, которые существуют в лексической среде внешней функции foo.

Функция bar вместе с ее связью с лексическим окружением функции foo является закрытием.

Для создания замыкания функции не требуется возвращаться. Просто в силу своего объявления каждая функция закрывается над своим лексическим окружением, образуя замыкание.

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2);
bar(10); // 16
bar(10); // 17

Вышеупомянутая функция также будет записывать 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, создается новый контекст выполнения ec. Вместе с аргументами функции и целевым объектом этот контекст выполнения также получает ссылку на лексическую среду вызывающего контекста выполнения, что означает, что переменные, объявленные во внешней лексической среде (в приведенном выше примере, как a, так и b) доступны из ec.

Каждая функция создает замыкание, потому что каждая функция имеет ссылку на внешнее лексическое окружение.

Обратите внимание, что переменные самих себя видны изнутри замыкания, нет копирует.

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

Это попытка прояснить несколько (возможных) недоразумений относительно замыканий, которые появляются в некоторых других ответах.

  • Замыкание создается не только тогда, когда вы возвращаете внутреннюю функцию. Фактически, включающая функция вообще не нужно возвращаться для создания ее закрытия. Вместо этого вы можете назначить свою внутреннюю функцию переменной во внешней области или передать ее в качестве аргумента другой функции, где ее можно будет вызвать немедленно или в любое время позже. Следовательно, закрытие охватывающей функции, вероятно, создается как только вызывается включающая функция, поскольку любая внутренняя функция имеет доступ к этому закрытию всякий раз, когда вызывается внутренняя функция, до или после возврата закрывающей функции.
  • Замыкание не ссылается на копию старые ценности переменных в своей области. Сами переменные являются частью замыкания, поэтому значение, видимое при доступе к одной из этих переменных, является самым последним значением на момент обращения к ней. Вот почему внутренние функции, созданные внутри циклов, могут быть сложными, поскольку каждая из них имеет доступ к одним и тем же внешним переменным, а не захватывает копию переменных во время создания или вызова функции.
  • "Переменные" в замыкании включают любые именованные функции. объявлен внутри функции. Они также включают аргументы функции. Замыкание также имеет доступ к содержащим его переменным замыкания вплоть до глобальной области видимости.
  • Замыкания используют память, но не вызывают утечек памяти, поскольку JavaScript сам по себе очищает свои собственные циклические структуры, на которые нет ссылок. Утечки памяти Internet Explorer, связанные с замыканиями, создаются, когда ему не удается отключить значения атрибутов DOM, которые ссылаются на замыкания, тем самым сохраняя ссылки на возможно циклические структуры.

Пример первой точки от dlaliberte:

A closure is not only created when you return an inner function. In fact, the enclosing function does not need to return at all. 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 used immediately. Therefore, the closure of the enclosing function probably already exists at the time that enclosing function was called since any inner function has access to it as soon as it is called.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);

Можете ли вы объяснить закрытие 5-летнему ребенку? *

Я все еще думаю, что Объяснение Google работает очень хорошо и кратко:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Proof that this example creates a closure even if the inner function doesn't return

*A C# question

Некоторое время назад я написал сообщение в блоге, объясняющее закрытие. Вот что я сказал о закрытии с точки зрения Почему, которое вам нужно.

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.

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

Полная запись:

Так что же это за закрывающиеся штуки?

Функции JavaScript могут получить доступ к своим:

  1. Аргументы
  2. Локальные (то есть их локальные переменные и локальные функции)
  3. Окружающая среда, в которую входят:
    • глобальные объекты, включая DOM
    • что-нибудь во внешних функциях

Если функция обращается к своему окружению, то функция является закрытием.

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

Пример закрытия, использующего глобальную среду:

Представьте, что события кнопок «Голосование за переполнение стека» и «Голосование за отклонение» реализованы как замыкания, voteUp_click и voteDown_click, которые имеют доступ к внешним переменным isVotedUp и isVotedDown, которые определены глобально. (Для простоты я имею в виду кнопки голосования за вопрос в StackOverflow, а не набор кнопок ответа на голосование.)

Когда пользователь нажимает кнопку VoteUp, функция voteUp_click проверяет, является ли isVotedDown == true, чтобы определить, голосовать ли за или просто отменить голосование против. Функция voteUp_click является закрытием, потому что она обращается к своей среде.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

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

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

Like the old Albert said : "If you can't explain it to a six-year old, you really don't understand it yourself.”. Well I tried to explain JS closures to a 27 years old friend and completely failed.

Can anybody consider that I am 6 and strangely interested in that subject ?

Я почти уверен, что был одним из немногих, кто попытался понять первоначальный вопрос буквально. С тех пор вопрос несколько раз видоизменялся, поэтому теперь мой ответ может показаться невероятно глупым и неуместным. Надеюсь, что общая идея истории кому-то понравится.


Я большой поклонник аналогий и метафор при объяснении сложных концепций, поэтому позвольте мне попробовать свои силы в истории.

Давным-давно:

Была принцесса ...

function princess() {

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

    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Но ей всегда приходилось возвращаться в свой унылый мир хлопот и взрослых.

    return {

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

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Но все, что они увидят, это маленькую девочку ...

var littleGirl = princess();

... рассказывать истории о волшебстве и фантазиях.

littleGirl.story();

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

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

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

Мне правда нравится это объяснение. Для тех, кто читал и не следил, аналогия такова: функция princess () - это сложная область видимости, содержащая частные данные. Вне функции личные данные не могут быть просмотрены или доступны. Принцесса хранит единорогов, драконов, приключения и т. д. В своем воображении (личные данные), и взрослые не могут их увидеть сами. НО воображение принцессы отражено в закрытии функции story(), которая является единственным интерфейсом, который экземпляр littleGirl предоставляет миру магии.

Patrick M 28.02.2013 11:49

Неопределенные значения затрудняют понимание. Вот настоящая история jsfiddle.net/rjdx34k0/3

Hugolpz 02.09.2020 22:13

О, хорошо, я был так близок к тому, чтобы внести правку, чтобы удалить то, что я считал лишним пространством в начале. Хорошая работа, +1

Tiago Martins Peres 李大仁 23.10.2020 10:34

И Прекрасный Принц может добавить к ее приключениям, может убить всех драконов, чтобы спасти ее от опасностей, как показано ниже: function princeCharming { adventures.push('Honeymoon Trip', 'Skydiving', 'Visiting Somalia'); const pickADragonToKill = dragons.pop(); }

Shivam 13.01.2021 08:15

Вы спите и приглашаете Дэна. Вы говорите Дэну принести один контроллер XBox.

Дэн приглашает Пола. Дэн просит Пола принести один контроллер. Сколько контролеров было привлечено на вечеринку?

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");

Я собрал интерактивное руководство по 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

Википедия о закрытии:

In computer science, a closure is a function together with a referencing environment for the nonlocal names (free variables) of that function.

Технически в JavaScript, каждая функция - это закрытие. Он всегда имеет доступ к переменным, определенным в окружающей области.

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

Замыкания часто используются для создания функций с некоторыми скрытыми личными данными (но это не всегда так).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

ems

В приведенном выше примере используется анонимная функция, которая была выполнена один раз. Но этого не должно быть. Ему можно присвоить имя (например, mkdb) и выполнить позже, генерируя функцию базы данных при каждом вызове. Каждая сгенерированная функция будет иметь свой собственный скрытый объект базы данных. Другой пример использования замыканий - это когда мы возвращаем не функцию, а объект, содержащий несколько функций для разных целей, каждая из которых имеет доступ к одним и тем же данным.

Серьезно относясь к вопросу, мы должны выяснить, на что способен типичный шестилетний ребенок в когнитивном отношении, хотя, надо признать, тот, кто интересуется JavaScript, не так уж типичен.

На Детское развитие: от 5 до 7 лет написано:

Your child will be able to follow two-step directions. For example, if you say to your child, "Go to the kitchen and get me a trash bag" they will be able to remember that direction.

Мы можем использовать этот пример для объяснения замыканий следующим образом:

The kitchen is a closure that has a local variable, called trashBags. There is a function inside the kitchen called getTrashBag that gets one trash bag and returns it.

Мы можем закодировать это в 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.

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

// 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

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

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

Из личного Сообщение блога:

По умолчанию 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);

присвоит d значение 3 + 4, то есть 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 ; }

На самом деле, так инициализируется a, поскольку последнее значение 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 при создании экземпляров функции.

Функция в JavaScript - это не просто ссылка на набор инструкций (как в языке C), но также включает в себя скрытую структуру данных, которая состоит из ссылок на все нелокальные переменные, которые она использует (захваченные переменные). Такие состоящие из двух частей функции называются замыканиями. Любую функцию в JavaScript можно считать закрытием.

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

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

Кроме того, следует понимать, что локальные переменные в 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();

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

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

Соломенный Человек

Мне нужно знать, сколько раз была нажата кнопка, и делать что-то при каждом третьем нажатии ...

Довольно очевидное решение

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

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

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

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

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

Рассмотрим этот вариант

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

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

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

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

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

Обратите внимание на несколько вещей.

В приведенном выше примере я использую закрывающее поведение JavaScript. Такое поведение позволяет любой функции иметь неограниченный доступ к области, в которой она была создана. Чтобы применить это на практике, я немедленно вызываю функцию, которая возвращает другую функцию, и поскольку функция, которую я возвращаю, имеет доступ к внутренней переменной count (из-за поведения закрытия, описанного выше), это приводит к частной области для использования Результирующая функция ... Не все так просто? Давайте разбавим это ...

Простое закрытие в одну линию

//          _______________________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)

Хорошо, разговаривая с 6-летним ребенком, я, возможно, использовал бы следующие ассоциации.

Imagine - you are playing with your little brothers and sisters in the entire house, and you are moving around with your toys and brought some of them into your older brother's room. After a while your brother returned from the school and went to his room, and he locked inside it, so now you could not access toys left there anymore in a direct way. But you could knock the door and ask your brother for that toys. This is called toy's closure; your brother made it up for you, and he is now into outer scope.

Сравните с ситуацией, когда дверь была заперта из-за сквозняка и никого не было внутри (выполнение общей функции), а затем возник локальный пожар и сгорел комнату (сборщик мусора: D), а затем была построена новая комната, и теперь вы можете уйти. там есть другие игрушки (новый экземпляр функции), но никогда не получить те игрушки, которые были оставлены в первом экземпляре комнаты.

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

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Как видите, игрушки, оставленные в комнате, по-прежнему доступны через брата, независимо от того, заперта ли комната. Вот jsbin, чтобы поиграть с ним.

Я бы просто указал им на Страница закрытия Mozilla. Это лучший, самый краткое и простое объяснение основ закрытия и практического использования, который я нашел. Настоятельно рекомендуется всем, кто изучает JavaScript.

И да, я бы даже порекомендовал его 6-летнему ребенку - если 6-летний ребенок изучает замыкания, то логично, что они готовы понять краткое и простое объяснение, представленный в статье.

Если вы хотите объяснить это шестилетнему ребенку, вы должны найти что-то гораздо более простое и БЕЗ кода.

Просто скажите ребенку, что он «открытый», что говорит о том, что он может иметь отношения с другими людьми, со своими друзьями. В какой-то момент он определил друзей (мы можем узнать имена его друзей), и это закрытие. Если вы сфотографируете его и его друзей, то он «закрыт» относительно своих способностей к дружбе. Но в целом он «открытый». На протяжении всей своей жизни у него будет много разных друзей. Один из таких наборов - закрытие.

Я нашел очень четкий раздел 6 главы 8 «Замыкания» JavaScript: полное руководство Дэвида Фланагана, 6-е издание, O'Reilly, 2011. Я попытаюсь перефразировать.

  1. Когда функция вызывается, создается новый объект для хранения локальных переменных для этого вызова.

  2. Область действия функции зависит от места ее объявления, а не от места выполнения.

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

JavaScript сохранит эти значения, поскольку они остались в рамках текущего выполнения благодаря передаче из завершенной внешней функции. Все функции являются замыканиями, но интересующие нас замыкания - это внутренние функции, которые, в нашем предполагаемом сценарии, сохраняют значения внешних функций в своем "корпусе" (я надеюсь, что я правильно использую язык здесь), когда они (внутренние функции) возвращаются. из внешних функций. Я знаю, что это не соответствует требованиям шестилетней давности, но надеюсь, что это все еще полезно.

Функция выполняется в рамках объекта / функции, в которой она определена. Указанная функция может получить доступ к переменным, определенным в объекте / функции, где она была определена во время выполнения.

И просто воспринимайте это буквально .... как написан код: P

Для шестилетнего ребенка?

Вы и ваша семья живете в мифическом городке Анн Вилль. У вас есть друг, который живет по соседству, поэтому вы звоните ему и просите выйти и поиграть. Вы набираете:

000001 (jamiesHouse)

Через месяц вы и ваша семья переезжаете из Ann Ville в следующий город, но вы и ваш друг по-прежнему поддерживаете связь, поэтому теперь вам нужно набрать код города, в котором живет ваш друг, прежде чем набирать их ' правильный номер:

001 000001 (annVille.jamiesHouse)

Через год после этого ваши родители переезжают в совершенно новую страну, но вы и ваш друг все еще поддерживаете связь, поэтому после того, как вы уговорили родителей разрешить вам звонить по международным тарифам, вы набираете:

01 001 000001 (myOldCountry.annVille.jamiesHouse)

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

000001 (jamiesHouse)

Ужасный...

Настолько жутко, что ты рассказываешь об этом Джейми из своей старой страны ... Ты посмеялся над этим. Итак, однажды вы и ваша семья отправляетесь в отпуск в старую страну. Вы посетите свой старый город (Энн Вилль) и отправитесь навестить Джейми ...

  • «Правда? Еще один Джейми? В Энн Вилле? В твоей новой стране !!?»
  • "Ага ... Давай назовем их ..."

02 001 000001 (myNewCountry.annVille.jamiesHouse)

Мнения?

Более того, у меня куча вопросов о терпении современного шестилетнего ребенка ...

Ответ для шестилетнего ребенка (при условии, что он знает, что такое функция, что такое переменная и что такое данные):

Функции могут возвращать данные. Один вид данных, который вы можете вернуть из функции, - это другая функция. Когда эта новая функция возвращается, все переменные и аргументы, использованные в функции, которая ее создала, не исчезают. Вместо этого эта родительская функция «закрывается». Другими словами, ничто не может заглянуть внутрь и увидеть используемые переменные, кроме возвращаемой им функции. Эта новая функция обладает особой способностью заглядывать внутрь функции, которая ее создала, и видеть данные внутри нее.

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

Еще один очень простой способ объяснить это с точки зрения объема:

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

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

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

Я действительно не знаю, как объяснить это детям 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();

Только для детей:

Закрытие - это обнимать

Ошибка - это летать

ЦЕЛОВАТЬ это поцелуй! :)

Хорошо, 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");

Я склонен лучше учиться, сравнивая ХОРОШО / ПЛОХО. Мне нравится видеть рабочий код, за которым следует нерабочий код, с которым кто-то может столкнуться. Я собрал jsFiddle, который выполняет сравнение и пытается свести различия к простейшим объяснениям, которые я мог придумать.

Закрытия сделаны правильно:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • В приведенном выше коде createClosure(n) вызывается на каждой итерации цикла. Обратите внимание, что я назвал переменную n, чтобы подчеркнуть, что это переменная новый, созданная в новой области действия функции, а не та же переменная, что и index, привязанная к внешней области.

  • Это создает новую область видимости, и n привязан к этой области; это означает, что у нас есть 10 отдельных областей видимости, по одной для каждой итерации.

  • createClosure(n) возвращает функцию, которая возвращает n в пределах этой области.

  • В каждой области n привязан к тому значению, которое он имел при вызове createClosure(n), поэтому возвращаемая вложенная функция всегда будет возвращать значение n, которое она имела при вызове createClosure(n).

Закрытие сделано неправильно:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • В приведенном выше коде цикл был перемещен внутри функции createClosureArray(), и теперь функция просто возвращает завершенный массив, что на первый взгляд кажется более интуитивным.

  • Что может быть неочевидным, так это то, что, поскольку createClosureArray() вызывается только один раз, для этой функции создается только одна область видимости вместо одной для каждой итерации цикла.

  • В этой функции определяется переменная с именем index. Цикл запускается и добавляет в массив функции, возвращающие index. Обратите внимание, что index определен в функции createClosureArray, которая вызывается только один раз.

  • Поскольку в функции createClosureArray() была только одна область, index привязан только к значению в этой области. Другими словами, каждый раз, когда цикл изменяет значение index, он меняет его для всего, что ссылается на него в этой области.

  • Все функции, добавленные в массив, возвращают ТАКУЮ переменную index из родительской области, в которой она была определена, вместо 10 разных из 10 разных областей, как в первом примере. Конечным результатом является то, что все 10 функций возвращают одну и ту же переменную из одной и той же области.

  • После завершения цикла и завершения модификации index конечное значение было 10, поэтому каждая функция, добавленная в массив, возвращает значение единственной переменной index, которая теперь установлена ​​на 10.

Результат

CLOSURES DONE RIGHT
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

CLOSURES DONE WRONG
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10

Учитывая следующую функцию

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

Каждый раз, когда вызывается функция person, создается новое замыкание. Хотя переменные a и b имеют одну и ту же функцию introduce, они связаны с разными замыканиями. И это закрытие все еще будет существовать даже после того, как функция person завершит выполнение.

Enter image description here

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 на другом языке, я проведу аналогию.

Думай как

  • JavaScript function как constructor
  • local variables как instance properties
  • эти properties являются частными
  • inner functions как instance methods

Каждый раз при вызове function

  • Будет создан новый object, содержащий все локальные переменные.
  • Методы этого объекта имеют доступ к "properties" этого экземпляра объекта.

Учитывая, что вопрос заключается в том, чтобы объяснить это просто, как если бы это было 6-летний, я бы ответил:

«Когда вы объявляете функцию в JavaScript, она имеет постоянный доступ ко всем переменным и функциям, которые были доступны в строке до объявления этой функции. Функция и все внешние переменные и функции, к которым у нее есть доступ, - это то, что мы называем закрытием. "

Замыкания просты:

Следующий простой пример охватывает все основные моменты закрытия 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, которые обращаются к переменным, объявленным во внешней функции *, называются закрытие.

Это почти все, что нужно для закрытия.



**

* 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.

Закрытие в основном создает две вещи: - функция - частная область, к которой может получить доступ только эта функция

Это похоже на нанесение покрытия на функцию.

Итак, шестилетнему ребенку это можно объяснить с помощью аналогии. Допустим, я строю робота. Этот робот может многое. Среди прочего, я запрограммировал его на подсчет количества птиц, которых он видит в небе. Каждый раз, когда он видел 25 птиц, он должен сказать мне, сколько птиц он видел с самого начала.

Я не знаю, сколько птиц он видел, если он не сказал мне. Только он знает. Это частная сфера. Это в основном память робота. Допустим, я дал ему 4 GB.

Возвращенная функция сообщает мне, сколько птиц он видел. Я тоже это создал.

Эта аналогия немного отстойна, но, я думаю, кто-то может ее улучшить.

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

Чем больше я думаю о закрытии, тем больше я рассматриваю его как двухэтапный процесс: init - действие

init: pass first what's 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');

Для разработчика я бы провел параллель между замыканиями и ООП. этап инициализации аналогичен передаче аргументов конструктору в традиционном объектно-ориентированном языке; фаза действия - это, в конечном счете, метод, который вы вызываете для достижения желаемого. И метод имеет доступ к этим аргументам инициализации с помощью механизма, называемого закрытие.

См. Еще один мой ответ, иллюстрирующий параллелизм между объектно-ориентированным взаимодействием и замыканиями:

Как «правильно» создать кастомный объект в JavaScript?

Как бы я объяснил это шестилетнему ребенку:

Вы знаете, как взрослые могут владеть домом, и они называют его своим домом? Когда у мамы рождается ребенок, ребенку вообще ничего не принадлежит, верно? Но его родители владеют домом, поэтому всякий раз, когда кто-то спрашивает ребенка: «Где твой дом?», Он / она может ответить «этот дом!» И указать на дом его родителей. «Закрытие» - это способность ребенка всегда (даже если он находится за границей) сказать, что у него есть дом, даже если на самом деле дом принадлежит родителю.

Самый простой, самый короткий и самый легкий для понимания ответ:

Замыкание - это блок кода, в котором каждая строка может ссылаться на один и тот же набор переменных с одинаковыми именами переменных.

Если «это» означает нечто иное, чем где-либо еще, то вы знаете, что это два разных закрытия.

Если вы это хорошо понимаете, вы можете объяснить это просто. И самый простой способ - абстрагироваться от контекста. Не считая кода, даже программирования. Пример метафоры сделает это лучше.

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

Если вы звоните извне, вы не знаете, что в нем, но вы знаете, что люди внутри сделают задание, если вы дадите им определенную информацию. Они могут видеть снаружи, поэтому они могут спрашивать вас о вещах, которые находятся снаружи, и вносить изменения в эти вещи, но вы не можете изменить то, что находится внутри, снаружи, вы даже не видите (не знаете), что это внутри. Люди внутри той комнаты, которую вы вызываете, видят, что это снаружи, но не то, что внутри комнат в этой комнате, поэтому они взаимодействуют с ними так же, как вы делаете снаружи. Люди внутри самых внутренних комнат могут многое видеть, но люди самой внешней комнаты даже не знают о существовании самых внутренних комнат.

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

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

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

Что такое закрытие?

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

Замыкание - это локальные переменные функции, которые сохраняются после возврата из функции.

Замыкания - это функции, которые обращаются к независимым (свободным) переменным. Другими словами, функция, определенная в замыкании, «запоминает» среду, в которой она была создана.

Замыкания - это расширение концепции области видимости. С замыканиями функции имеют доступ к переменным, которые были доступны в области, в которой была создана функция.

Замыкание - это стековый фрейм, который не освобождается при возврате функции. (Как если бы 'стек-фрейм' был неправильно привязан к стеку, а не был в стеке!)

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

Полезные ссылки:

Благодаря приведенным выше ссылкам, которые помогают мне лучше понять и объяснить закрытие.

Я читал все это раньше, и все они очень информативны. Некоторые очень близки к тому, чтобы получить простое объяснение, а затем усложняются или остаются абстрактными, нарушая цель и не демонстрируя очень простого использования в реальном мире.

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

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

Один из лучших (или самый простой) - это пересказ примера Морриса «Замыкания для чайников».

Если сделать еще один шаг к концепции SayHi2Bob, это демонстрирует две основные вещи, которые вы можете почерпнуть, прочитав все ответы:

  1. Замыкания имеют доступ к переменным содержащейся функции.
  2. Замыкания сохраняются в собственном пространстве памяти (и, таким образом, полезны для всех видов создания экземпляров oop-y)

Доказывая и демонстрируя это себе, я сделал небольшую скрипку:

http://jsfiddle.net/9ZMyr/2/

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, синглтонов и экземпляров с их собственными данными и т. д. И т. Д.

Если вы начинаете с этого новичка, вы можете перейти к более сложным вызовам на основе свойств / элементов объекта, и, надеюсь, концепции сохранятся.

Замыкание создается, когда внутренняя функция каким-то образом становится доступной для любой области вне внешней функции.

Пример:

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 это происходит, когда вы:

  • Определите одну функцию внутри другой функции
  • Внутренняя функция вызывается после того, как внешняя функция вернула
// '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);
}

Я считаю полезным сделать шаг назад и изучить более общее понятие «замыкания» - так называемый «оператор соединения».

В математике оператор «соединения» - это функция на частично упорядоченном множестве, которая возвращает наименьший объект, больший или равный его аргументам. В символах соедините [a, b] = d такое, что d> = a и d> = b, но не существует e такого, что d> e> = a или d> e> = b.

Таким образом, объединение дает вам наименьшее «больше», чем части.

Обратите внимание, что области видимости JavaScript представляют собой частично упорядоченную структуру. Так что есть разумное понятие соединения. В частности, объединение областей видимости - это наименьшая область действия, превышающая исходные. Эта область называется закрытие.

Таким образом, замыкание для переменных a, b, c - это наименьшая область видимости (в решетке областей видимости вашей программы!), Которая вводит a, b и c в область видимости.

Замыкание - это блок кода, который соответствует трем критериям:

  • Его можно передавать как значение и

  • выполняется по требованию любым лицом, имеющим эту ценность, и в этот момент

  • он может ссылаться на переменные из контекста, в котором он был создан (то есть он закрыт относительно доступа к переменным, в математический смысл слова «закрытый»).

(Слово «закрытие» на самом деле имеет неточное значение, и некоторые люди не думают, что критерий № 1 является частью определения. Я думаю, что это так.)

Замыкания являются основой функциональных языков, но они присутствуют и во многих других языках (например, в анонимных внутренних классах Java). С ними можно делать крутые штуки: они допускают отложенное выполнение и некоторые изящные уловки стиля.

Автор: Пол Кантрелл, @ http://innig.net/software/ruby/closures-in-ruby

Представьте, что в вашем городе есть очень большой парк, где вы видите фокусника по имени мистер Кодер, который запускает бейсбольные игры в разных уголках парка, используя свою волшебную палочку, называемую JavaScript.

Естественно, каждая игра в бейсбол имеет одни и те же правила, и каждая игра имеет свое собственное табло.

Естественно, результаты одного бейсбольного матча полностью отделены от результатов других игр.

Закрытие - это особый способ, которым мистер Кодер разделяет подсчет очков во всех своих волшебных бейсбольных играх.

Вот простой сценарий в реальном времени. Просто прочтите его, и вы поймете, как мы здесь использовали закрытие (посмотрите, как меняется количество мест).

Все другие примеры, описанные ранее, также очень хорошо подходят для понимания концепции.

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 замыкания удивительны и уникальны, когда переменные или аргументы доступны для внутренних функций, и они будут живы даже после возврата внешней функции. Замыкания используются в большинстве шаблонов проектирования в JS.

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

Maybe you should consider an object-oriented structure instead of inner functions. For example:

    var calculate = {
        number: 0,
        init: function (num) {
            this.number = num;
        },
        add: function (val) {
            this.number += val;
        },
        rem: function (val) {
            this.number -= val;
        }
    };

And read the result from the calculate.number variable, who needs "return" anyway.


//Addition
First think about scope which defines what variable you have to access to (In Javascript);

//there are two kinds of scope
Global Scope which include variable declared outside function or curly brace

let globalVariable = "foo";

one thing to keep in mind is once yo've declared a globalVariable you can use it anywhere in your code even in function;

Local Scope which include variable that are usable only in a specific part of your code

//lets break it 

function scope is when you declare a variable in a function  you can access the variable only within the function

function User(){
    let name = "foo";
    alert(name);
}
    alert(name);//error

//Block scope is when you declare a variable within a block then you can  access that variable only within a block 
{
let user = "foo";
alert(user);
}
alert(user);
//Uncaught ReferenceError: user is not defined at.....

//A Closure

function User(fname){
return function(lname){
 return fname + " " lname;
}
}
let names = User("foo");
alert(names("bar"));

//When you create a function within a function you've created a closure, in our example above since the outer function is returned the inner function got access to outer function's scope

Я не понимаю, почему здесь такие сложные ответы.

Вот закрытие:

var a = 42;

function b() { return a; }

Да. Вы, вероятно, используете это много раз в день.


There is no reason to believe closures are a complex design hack to address specific problems. No, closures are just about using a variable that comes from a higher scope from the perspective of where the function was declared (not run).

Now what it allows you to do can be more spectacular, see other answers.

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

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

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

ПРЕДУПРЕЖДЕНИЕ: обезьяна

В приведенном выше примере вызывается externalFunction, который, в свою очередь, вызывает innerFunction. Обратите внимание на то, как externalVar доступен для innerFunction, о чем свидетельствует его правильное предупреждение значения outerVar.

Теперь рассмотрим следующее:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

ПРЕДУПРЕЖДЕНИЕ: обезьяна

Для referenceToInnerFunction установлено значение outerFunction (), которое просто возвращает ссылку на innerFunction. Когда вызывается referenceToInnerFunction, она возвращает externalVar. Опять же, как и выше, это демонстрирует, что innerFunction имеет доступ к outerVar, переменной outerFunction. Кроме того, интересно отметить, что он сохраняет этот доступ даже после завершения выполнения externalFunction.

И вот здесь все становится по-настоящему интересным. Если бы мы избавились от externalFunction, скажем, установили для него значение null, вы могли бы подумать, что referenceToInnerFunction потеряет доступ к значению externalVar. Но это не так.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

ПРЕДУПРЕЖДЕНИЕ: обезьяна ПРЕДУПРЕЖДЕНИЕ: обезьяна

Но как это так? Как может referenceToInnerFunction узнать значение outerVar теперь, когда для outerFunction установлено значение null?

Причина, по которой referenceToInnerFunction все еще может получить доступ к значению externalVar, заключается в том, что, когда закрытие было впервые создано путем помещения innerFunction внутри outerFunction, innerFunction добавила ссылку на область outerFunction (ее переменные и функции) в свою цепочку областей видимости. Это означает, что innerFunction имеет указатель или ссылку на все переменные outerFunction, включая externalVar. Таким образом, даже когда externalFunction завершил выполнение или даже если он удален или установлен в значение null, переменные в его области, такие как externalVar, остаются в памяти из-за выдающейся ссылки на них со стороны внутренней функции, которая была возвращена в referenceToInnerFunction. Чтобы действительно освободить externalVar и остальные переменные outerFunction из памяти, вам нужно будет избавиться от этой выдающейся ссылки на них, например, установив для referenceToInnerFunction значение null.

//////////

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

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

ПРЕДУПРЕЖДЕНИЕ: горилла

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

(Я не беру в расчет 6-летнюю давность.)

На таком языке, как JavaScript, где вы можете передавать функции в качестве параметров другим функциям (языки, где функции - граждане первого класса), вы часто будете делать что-то вроде:

var name = 'Rafael';

var sayName = function() {
  console.log(name);
};

Видите ли, sayName не имеет определения переменной name, но использует значение name, которое было определено вне sayName (в родительской области).

Допустим, вы передаете sayName в качестве параметра другой функции, которая будет вызывать sayName в качестве обратного вызова:

functionThatTakesACallback(sayName);

Обратите внимание, что:

  1. sayName будет вызываться изнутри functionThatTakesACallback (предположим, что, поскольку я не реализовал functionThatTakesACallback в этом примере).
  2. Когда вызывается sayName, он регистрирует значение переменной name.
  3. functionThatTakesACallback не определяет переменную name (ну, может, но это не имеет значения, поэтому предположим, что это не так).

Итак, у нас есть sayName, который вызывается внутри functionThatTakesACallback и ссылается на переменную name, которая не определена внутри functionThatTakesACallback.

Что тогда происходит? ReferenceError: name is not defined?

Нет! Значение name фиксируется в закрытие. Вы можете думать об этом закрытии как о контекст, связанный с функцией, которое содержит значения, которые были доступны там, где эта функция была определена.

Итак: несмотря на то, что name не входит в область действия, в которой будет вызываться функция sayName (внутри functionThatTakesACallback), sayName может получить доступ к значению name, которое зафиксировано в замыкании, связанном с sayName.

-

Из книги Красноречивый JavaScript:

A good mental model is to think of function values as containing both the code in their body and the environment in which they are created. When called, the function body sees its original environment, not the environment in which the call is made.

Вот самый дзен-ответ, который я могу дать:

Что вы ожидаете от этого кода? Расскажите мне в комментарии, прежде чем запускать его. Мне любопытно!

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 - игнорируйте «undefined» в конце), значит, у вас уже есть бессловесное понимание. Прописью, переменная i является частью внутренней функции закрытия экземпляра.

Я сформулировал это так, потому что, когда я понял, что этот код помещает экземпляры внутренней функции foo() в bar и baz, а затем вызывает их через эти переменные, меня больше ничего не удивило.

Но если я ошибаюсь и вывод на консоль вас удивил, дайте мне знать!

Я верю в более короткие объяснения, поэтому см. Изображение ниже.

Enter image description here

function f1() ..> Светло-красный ящик

function f2() ..> Красная коробка

Здесь у нас есть две функции: 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() вызывает здесь:

Enter image description here

Самый простой вариант использования Замыкания 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();

Надеюсь, это поможет. С уважением,

Также... Возможно, нам стоит урезать вашего 27-летнего друга немного слабина, потому что вся концепция «замыканий» на самом деле является(!) ... вуду!

Под этим я подразумеваю: а) интуитивно вы этого не ожидаете ... И ... (б) когда кто-то тратит время, чтобы объяснить это вам, вы, конечно, не ожидаете, что это работай!

Интуиция подсказывает вам, что «это, должно быть, чушь ... конечно, это должно привести к какой-то синтаксической ошибке или еще чему-то!» Как на Земле(!) не могли бы вы, по сути, «вытащить функцию из« середины », где бы она ни находилась», чтобы вы могли [все еще!] Иметь доступ для чтения / записи к контексту «wherever-it-был-at» ?! "

Когда вы наконец поймете, что это возможный,, тогда ... конечно ... чья-то реакция постфактум будет: "эй-а-а-а (!) ... кью-эль-л-л-л ... (!!!)"

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

Как я уже сказал: «это вуду».

Замыкание - это функция внутри функции, у которого есть доступ к переменным и параметрам своей «родительской» функции.

Пример:

function showPostCard(Sender, Receiver) {

    var PostCardMessage = " Happy Spring!!! Love, ";

    function PreparePostCard() {
        return "Dear " + Receiver + PostCardMessage + Sender;
    }

    return PreparePostCard();
}
showPostCard("Granny", "Olivia");

The children will always remember the secrets they have shared with their parents, even after their parents are gone. This is what closures are for functions.

Секреты функций 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"
  }
}

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

Но печально то, что если дочерний элемент также является частной переменной своей родительской функции, он также умрет, когда родительский элемент завершится, и секреты умрут вместе с ними.

Итак, чтобы жить, ребенок должен уйти, пока не стало слишком поздно

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"

Это все, что можно сказать.

Замыкание - это то, что многие разработчики 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 был бы «нулевым указателем», и вам нужно было бы установить геттер и сеттер и начать передачу ссылок. Не похоже на JavaScript, правда? Спасибо за мощное закрытие.

Почему это должно меня волновать?

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

Мне нравится определение замыкания, данное Кайлом Симпсоном:

Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.

Лексическая область видимости - это когда внутренняя область видимости может получить доступ к своей внешней области видимости.

Вот модифицированный пример, который он приводит в своей серии книг «You Don't 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(); 

Следующий пример представляет собой простую иллюстрацию закрытия 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

Автор Закрытие довольно хорошо объяснил замыкания, объяснив причину, по которой они нам нужны, а также объяснил LexicalEnvironment, который необходим для понимания замыканий.
Вот краткое изложение:

Что делать, если к переменной обращаются, но она не является локальной? Как здесь:

Enter image description here

В этом случае интерпретатор находит переменную в внешний объект LexicalEnvironment.

Процесс состоит из двух этапов:

  1. Во-первых, когда создается функция f, она не создается в пустом Космос. Есть текущий объект LexicalEnvironment. В случае выше, это окно (a не определено во время функции создание).

Enter image description here

Когда функция создается, она получает скрытое свойство с именем [[Scope]], которое ссылается на текущую LexicalEnvironment.

Enter image description here

Если переменная читается, но нигде не может быть найдена, генерируется ошибка.

Вложенные функции

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

Enter image description here

Итак, функция g имеет доступ к g, a и f.

Закрытие

Вложенная функция может продолжать работать после завершения внешней функции:

Enter image description here

Разметка лексических сред:

Enter image description here

Как мы видим, this.say - это свойство в объекте пользователя, поэтому оно продолжает жить после завершения User.

И если вы помните, когда создается this.say, он (как и каждая функция) получает внутреннюю ссылку this.say.[[Scope]] на текущую LexicalEnvironment. Таким образом, LexicalEnvironment текущего выполнения User остается в памяти. Все переменные пользователя также являются его свойствами, поэтому они также тщательно хранятся, а не выбрасываются, как обычно.

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

Подвести итоги:

  1. Внутренняя функция сохраняет ссылку на внешнюю LexicalEnvironment.
  2. Внутренняя функция может получить доступ к переменным из нее в любое время, даже если внешняя функция завершена.
  3. Браузер хранит LexicalEnvironment и все его свойства (переменные) в памяти до тех пор, пока на него не будет ссылаться внутренняя функция.

Это называется закрытием.

Встречайте иллюстрированное объяснение: Как закрытие 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();

При выполнении кода верхнего уровня мы имеем следующее расположение объектов области видимости:

Enter image description here

И когда вызывается myFunc(), у нас есть следующая цепочка областей видимости:

Enter image description here

Понимание того, как создаются, используются и удаляются объекты области видимости, является ключом к общей картине и пониманию того, как закрытие работает под капотом.

Подробности см. В вышеупомянутой статье.

Лучше всего объяснять эти концепции постепенно:

Переменные

console.log(x);
// undefined

Здесь undefined - это способ JavaScript "Я понятия не имею, что означает x".

Variables are like tags.

Можно сказать, тег x указывает на значение 42:

var x = 42;
console.log(x);
// 42

Теперь JavaScript знает, что означает x.

You can also re-assign a variable.

Сделайте так, чтобы тег x указывал на другое значение:

x = 43;
console.log(x);
// 43

Теперь x означает другое.

Объем

When you make a function, the function has its own "box" for variables.

function A() {
  var x = 42;
}

console.log(x);

// undefined

Из-за пределов коробки вы не можете увидеть, что внутри коробки.

Но изнутри коробки вы можете увидеть, что находится за ее пределами:

var x = 42;

function A() {
  console.log(x);
}

// 42

Inside function A, you have "scope access" to x.

Теперь, если у вас есть два бокса бок о бок:

function A() {
  var x = 42;
}

function B() {
  console.log(x);
}

// undefined

Inside function B, you have no access to variables inside function 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);
};

Variable a now means a function, you can run it.

a();
// 42

Вы также можете передать эту переменную:

setTimeout(a, 1000);

Через секунду (1000 миллисекунд) вызывается функция, на которую указывает a:

// 42

Объем закрытия

Теперь, когда вы определяете функции, эти функции имеют доступ к своим внешним областям.

Когда вы передаете функции как значения, потеря доступа может вызвать затруднения.

In JavaScript, functions keep their access to outer scope variables. Even when they are passed around to be run somewhere else.

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!'

You can still access variables in the closure scope.

Несмотря на то, что a завершил работу, и теперь вы используете c вне a.

То, что здесь произошло, в JavaScript называется «закрытие».

Пиноккио: закрытие в 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\'s 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);

 

Замыкания просты

Вероятно, вам не следует рассказывать шестилетнему ребенку о закрытии, но если вы это сделаете, вы можете сказать, что закрытие дает возможность получить доступ к переменной, объявленной в некоторой другой области видимости функции.

enter image description here

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 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 или анонимных функций.

A closure is a function having access to the parent scope, even after the parent function has closed.

var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();

add();
add();
add();
// The counter is now 3

Объяснение примера:

  • Переменной add присваивается значение, возвращаемое функцией с автоматическим запуском.
  • Самозапускающаяся функция запускается только один раз. Он устанавливает счетчик на ноль (0) и возвращает выражение функции.
  • Таким образом, add становится функцией. «Замечательно» то, что он может получить доступ к счетчику в родительской области.
  • Это называется закрытием JavaScript. Это позволяет функции иметь "частные" переменные.
  • Счетчик защищен областью действия анонимной функции и может быть изменен только с помощью функции добавления.

Источник

Для шестилетнего ...

Вы знаете, что это за предметы?

Объекты - это вещи, которые имеют свойства и что-то делают.

Одна из наиболее важных особенностей замыканий заключается в том, что они позволяют создавать объекты на JavaScript. Объекты в JavaScript - это просто функции и замыкания, которые позволяют JavaScript сохранять значение свойства для объекта после его создания.

Объекты очень полезны и содержат все в порядке и порядке. Разные объекты могут выполнять разную работу, а совместная работа объектов может выполнять сложные задачи.

К счастью, в JavaScript есть закрытие для создания объектов, иначе все превратилось бы в кошмар.

Когда-то был пещерный человек

function caveman {

у кого был особенный рок,

var rock = "diamond";

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

return {
    getRock: function() {
        return rock;
    }
};
}

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

var friend = caveman();
var rock = friend.getRock();

Довольно умный пещерный человек.

Замыкание может быть закрытым и публичным переменными или функциями.

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

Демо

Чтобы понять замыкания, вы должны перейти к программе и буквально выполнить ее, как если бы вы были средой выполнения. Давайте посмотрим на этот простой фрагмент кода:

Enter image description here

JavaScript запускает код в два этапа:

  • Фаза компиляции // JavaScript - это не чистый интерпретируемый язык
  • Фаза исполнения

Когда JavaScript проходит фазу компиляции, он извлекает объявления переменных и функций. Это называется подъемом. Функции, встречающиеся на этом этапе, сохраняются в памяти как текстовые блоки, также известные как лямбда. После компиляции JavaScript переходит в фазу выполнения, где он присваивает все значения и запускает функцию. Для запуска функции он подготавливает контекст выполнения, выделяя память из кучи и повторяя этапы компиляции и выполнения для функции. Эта область памяти называется областью действия функции. Когда запускается, существует глобальная область видимости. Области видимости - ключ к пониманию замыканий.

В этом примере сначала определяется переменная a, а затем f определяется на этапе компиляции. Все необъявленные переменные сохраняются в глобальной области видимости. На этапе выполнения f вызывается с аргументом. Назначена область действия f, и для нее повторяется этап компиляции и выполнения.

Аргументы также сохраняются в этой локальной области для f. Всякий раз, когда создается локальный контекст выполнения или область видимости, он содержит указатель ссылки на свою родительскую область. Весь доступ к переменной следует этой цепочке лексической области видимости, чтобы найти ее значение. Если переменная не найдена в локальной области видимости, она следует по цепочке и находит ее в своей родительской области. По этой же причине локальная переменная переопределяет переменные в родительской области. Родительская область видимости называется «закрытием» для локальной области или функции.

Здесь, когда настраивается область видимости g, он получил лексический указатель на свою родительскую область видимости 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);

Очень интересный разговор о замыканиях и многом другом - это Ариндам Пол - внутреннее устройство виртуальной машины JavaScript, EventLoop, Async и ScopeChains.

Вот так новичок обернулся вокруг Closures, как функция, заключенная в тело функции, также известное как Закрытие.

Определение из книги Speaking JavaScript «Замыкание - это функция плюс соединение с областью видимости, в которой функция была создана» -Доктор Аксель Раушмайер

Так как это могло выглядеть? Вот пример

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 будут отслеживать свое собственное значение.

Простое, но, надеюсь, ясное представление о том, что такое завершение всех этих замечательных и продвинутых ответов.

Функции, не содержащие свободных переменных, называются чистыми функциями.

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

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
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

Я думаю, MDN объясняет это лучше всего:

Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.

У закрытия всегда есть внешняя функция и внутренняя функция. Внутренняя функция - это то место, где происходит вся работа, а внешняя функция - это просто среда, которая сохраняет область, в которой была создана внутренняя функция. Таким образом, внутренняя функция закрытия «запоминает» среду / область видимости, в которой она была создана. Самый классический пример - функция счетчика:

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(), внутренняя функция (рабочая функция) может увеличивать ее.

Замыкания позволяют программистам на JavaScript писать лучший код. Креативный, выразительный и лаконичный. Мы часто используем замыкания в JavaScript, и, независимо от нашего опыта работы с JavaScript, мы, несомненно, сталкиваемся с ними снова и снова. Замыкания могут показаться сложными, но, надеюсь, после того, как вы прочтете это, замыкания станут намного более понятными и, следовательно, более привлекательными для ваших повседневных задач программирования на JavaScript.

Вы должны быть знакомы с Область видимости переменной JavaScript, прежде чем читать дальше, потому что для понимания замыканий вы должны понимать область видимости переменных JavaScript.

Что такое закрытие?

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

Внутренняя функция имеет доступ не только к переменным внешней функции, но и к параметрам внешней функции. Обратите внимание, что внутренняя функция не может вызывать объект arguments внешней функции, однако, даже если она может вызывать параметры внешней функции напрямую.

Вы создаете замыкание, добавляя функцию внутри другой функции.

Базовый пример замыканий в JavaScript:

function showName (firstName, lastName) {

  var nameIntro = "Your name is ";
  // this inner function has access to the outer function's 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's scope​
  });
​});

Правила закрытия и побочные эффекты

1. Замыкания имеют доступ к переменной внешней функции даже после того, как внешняя функция вернет:

Одна из наиболее важных и щекотливых особенностей замыканий заключается в том, что внутренняя функция по-прежнему имеет доступ к переменным внешней функции даже после того, как внешняя функция вернулась. Да, вы правильно прочитали. Когда функции в JavaScript выполняются, они используют ту же цепочку областей видимости, которая действовала при их создании. Это означает, что даже после возврата внешней функции внутренняя функция по-прежнему имеет доступ к переменным внешней функции. Следовательно, вы можете вызвать внутреннюю функцию позже в своей программе. Этот пример демонстрирует:

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's 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's 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's 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's 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's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable


3. Неудачи

Поскольку замыкания имеют доступ к обновленным значениям переменных внешней функции, они также могут приводить к ошибкам, когда переменная внешней функции изменяется с помощью цикла 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


Больше можно найти здесь-

  1. http://javascript.info/tutorial/closures

  2. http://www.javascriptkit.com/javatutors/closures.shtml

Изображение версии для этого ответа: [Решено]

Просто забудьте обо всем и помните: когда переменная где-то нужна, javascript не уничтожит ее. Переменная всегда указывает на самое новое значение.

Пример 1:

enter image description here

Пример 2:

enter image description here

Пример 3:enter image description here

Замыкание понять нетрудно. Это зависит только от точки зрения.

Мне лично нравится использовать их в повседневной жизни.

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 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".

Таким образом, эффективный вызов book() возвращает ссылку на закрытие, то есть не только функцию, но и ссылку на книгу и ее контекст, то есть ссылку на функцию getPage, состояние переменных страницы и bookMarkedPage.

Некоторые моменты, которые следует учитывать:

Пункт 1: Книжная полка, как и стек функций, имеет ограниченное пространство, поэтому используйте ее с умом.

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

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

Замыкание - это просто, когда функция имеет доступ к своей внешней области видимости даже после того, как функция области видимости завершила выполнение. Пример:

function multiplier(n) {
    function multiply(x) {
          return n*x;
    }
    return mutliply;
}

var 10xmultiplier = multiplier(10);
var x = 10xmultiplier(5); // x= 50

мы можем видеть, что даже после того, как multiplier завершил выполнение, внутренняя функция multiply по-прежнему получает доступ к значению x, которое в этом примере равно 10.

Очень распространенное использование замыканий - это каррирование (тот же пример выше), когда мы постепенно добавляем в нашу функцию параметры, вместо того, чтобы предоставлять все аргументы сразу.

Мы можем добиться этого, потому что Javascript (в дополнение к прототипу ООП) позволяет программировать функционально, когда функции более высокого порядка могут принимать другие функции в качестве аргументов (функции первого класса). функциональное программирование в википедии

Я настоятельно рекомендую вам прочитать эту книгу Кайла Симпсона: 2 одна часть серии книг посвящена замыканиям и называется областью действия и замыканиями. вы не знаете js: бесплатное чтение на github

Этот ответ представляет собой краткое изложение этого видео на YouTube Замыкания Javascript. Итак, все кредиты этому видео.

Замыкания - это не что иное, как функции с отслеживанием состояния, которые поддерживают состояния своих частных переменных.

Обычно при вызове функции, как показано на рисунке ниже. Переменные создаются в используемом стеке (оперативной памяти), а затем перемещаются.

enter image description here

Но теперь бывают ситуации, когда мы хотим поддерживать это состояние функции, в которой используются замыкания Javascript. Замыкание - это функция внутри функции с обратным вызовом, как показано в приведенном ниже коде.

enter image description here

Таким образом, код закрытия для функции счетчика выше выглядит примерно так, как показано ниже: это функция внутри функции с оператором 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)

Но теперь самый большой вопрос, в чем польза такой функции с отслеживанием состояния. Функции с отслеживанием состояния - это строительные блоки для реализации концепции ООП, такой как абстракция, инкапсуляция и создание автономных модулей.

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

Объект, который следует принципам ООП, является самодостаточным, следует абстракции, следует инкапсуляции и т.д. Без закрытий в Javascript это сложно реализовать.

enter image description here

A closure is a function having access to the parent scope, even after the parent function has closed.

Таким образом, замыкание - это функция другой функции. Можно сказать как дочерняя функция.

A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. The closure has three scope chains: it has access to its own scope (variables defined between its curly brackets), it has access to the outer function’s variables, and it has access to the global variables.

The inner function has access not only to the outer function’s variables but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments object, however, even though it can call the outer function’s parameters directly.

You create a closure by adding a function inside another function.

Кроме того, это очень полезный метод, который используется во многих известных фреймворках, включая Angular, Node.js и jQuery:

Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.

Но как замыкания выглядят в реальном коде? Взгляните на этот простой пример кода:

function showName(firstName, lastName) {
      var nameIntro = "Your name is ";
      // this inner function has access to the outer function's 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's scope
    });
});

Но почему мы используем замыкания? когда мы используем его в реальном программировании? какова практическая польза укупорочных средств? Ниже приведено хорошее объяснение и пример от MDN:

Практические закрытия

Closures are useful because they let you associate some data (the lexical environment) with a function that operates on that data. This has obvious parallels to object oriented programming, where objects allow us to associate some data (the object's properties) with one or more methods.

Consequently, you can use a closure anywhere that you might normally use an object with only a single method.

Situations where you might want to do this are particularly common on the web. Much of the code we write in front-end JavaScript is event-based — we define some behavior, then attach it to an event that is triggered by the user (such as a click or a keypress). Our code is generally attached as a callback: a single function which is executed in response to the event.

For instance, suppose we wish to add some buttons to a page that adjust the text size. One way of doing this is to specify the font-size of the body element in pixels, then set the size of the other elements on the page (such as headers) using the relative em unit:

Прочтите приведенный ниже код и запустите его, чтобы увидеть, как замыкание помогает нам легко создавать отдельные функции для каждого раздела:

//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

Другие вопросы по теме