JavaScript: привязка к переменной [duplicate]

В документации jQuery.fn.on есть хорошее объяснение.

Короче:

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

blockquote>

Таким образом, в следующем примере #dataTable tbody tr должен существовать до генерации кода.

$("#dataTable tbody tr").on("click", function(event){
    console.log($(this).text());
});

Если на страницу вводится новый HTML-код, предпочтительнее использовать делегированные события для присоединения обработчика событий, как описано ниже.

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

$("#dataTable tbody").on("click", "tr", function(event){
    console.log($(this).text());
});

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

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

Примечание. Делегированные события не работают для SVG .

7654
задан 28 revs, 21 users 17% 9 April 2017 в 13:55
поделиться

30 ответов

6188
ответ дан 47 revs, 42 users 33% 18 August 2018 в 17:20
поделиться

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

Как старый Альберт сказал: «Если вы не можете объяснить это шестилетнему ребенку, вы действительно не понимаете сами ». Ну, я попытался объяснить закрытие JS 27-летнему другу и полностью потерпел неудачу.

Кто-нибудь может подумать, что мне 6 и странно интересуется этим вопросом?

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


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

Когда-то:

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

function princess() {

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

    var adventures = [];

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

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

    /* ... */

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

    return {

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

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

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

var littleGirl = princess();

... рассказывая рассказы о магии и фантазии.

littleGirl.story();

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

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

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

2255
ответ дан 11 revs, 5 users 58% 18 August 2018 в 17:20
поделиться
  • 1
    Я очень люблю это объяснение. Для тех, кто читает это и не следует, аналогия такова: функция princess () представляет собой сложную область, содержащую личные данные. Вне функции частные данные не могут быть просмотрены или доступны. Принцесса держит в своем воображении единорогов, драконов, приключений и т. Д. (Частные данные), а взрослые не могут видеть их сами. НО воображение принцессы захватывается в закрытии для функции story(), которая является единственным интерфейсом, который экземпляр littleGirl выставляет в мир магии. – Patrick M 28 February 2013 в 09:49
  • 2
    Итак, здесь story является замыканием, но если код был var story = function() {}; return story;, тогда littleGirl будет замыканием. По крайней мере, создается впечатление, что я получаю из использование MDN «частных» методов с закрытием : ». Эти три публичные функции - это блокировки, которые используют одну и ту же среду. & Quot; – icc97 23 February 2016 в 01:58
  • 3
    @ icc97, да, story является закрытием, ссылающимся на среду, предоставленную в рамках princess. princess также является еще одним закрытием подразумеваемого , то есть princess и littleGirl будут передавать любую ссылку на массив parents, который будет существовать в среде / области, где littleGirl существует и определяется princess. – Jacob Swartwood 1 March 2016 в 17:00
  • 4
    @BenjaminKrupp Я добавил явный кодовый комментарий, чтобы показать / подразумевать, что в теле princess больше операций, чем написано. К сожалению, эта история сейчас немного неуместна в этой теме. Первоначально вопрос заключался в том, чтобы «объяснять закрытие JavaScript для 5yr old»; мой ответ был единственным, кто даже пытался это сделать. Я не сомневаюсь, что это провалилось бы ужасно, но, по крайней мере, этот ответ мог бы иметь интерес к 5-летнему старому. – Jacob Swartwood 11 September 2017 в 18:45
  • 5
    Собственно, для меня это имело прекрасный смысл. И я должен признать, что, наконец, понимание закрытия JS, используя рассказы о принцессах и приключениях, заставляет меня чувствовать себя странно странным. – Crystallize 2 October 2017 в 10:03

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

В Развитие детства: от 5 до 7 лет говорится:

Ваш ребенок сможет следовать двухэтапным направлениям. Например, если вы скажете своему ребенку: «Идите на кухню и достаньте мешок для мусора», они смогут запомнить это направление.

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

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

Мы можем закодировать это в JavaScript следующим образом:

function makeKitchen () {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

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

Другие моменты, которые объясняют, почему замыкания интересны:

  • Каждый раз, когда вызывается makeKitchen(), создается новое замыкание со своим отдельным trashBags.
  • trashBags является локальной для каждой кухни и недоступна снаружи, но внутренняя функция в свойстве getTrashBag имеет к ней доступ.
  • Каждый вызов функции создает замыкание, но не нужно будет закрывать его, если внутренняя функция, которая имеет доступ к внутренней части замыкания, может быть вызвана из-за закрытия. Возвращение объекта с помощью функции getTrashBag делает это здесь.
693
ответ дан 17 revs, 4 users 91% 18 August 2018 в 17:20
поделиться
  • 1
    На самом деле, смутно, функция makeKitchen call - это фактическое закрытие, а не объект кухни, который он возвращает. – dlaliberte 27 June 2016 в 17:56
  • 2
    Пробираясь через другие, я нашел этот ответ самым простым способом объяснить, что и почему закрывает. – Chetabahana 12 August 2016 в 15:12
  • 3
    Слишком много меню и закусок, недостаточно мяса и картофеля. Вы можете улучшить этот ответ только одним коротким предложением, например: «Закрытие - это запечатанный контекст функции из-за отсутствия какого-либо механизма обзора, предоставляемого классами». – Jacob 13 May 2017 в 16:30

Straw Man

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

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

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

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

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

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

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

Рассмотрим эту опцию

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

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

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

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

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

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

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

Простое однострочное закрытие

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

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

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

Получите его? Таким образом, в нашем основном примере переменная count содержится в закрытии и всегда доступна для обработчика событий, поэтому она сохраняет свое состояние от щелчка до щелчка.

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

Там вы идете; вы теперь полностью инкапсулируете это поведение.

Полная запись в блоге (включая соображения jQuery)

527
ответ дан 17 revs, 8 users 64% 18 August 2018 в 17:20
поделиться
  • 1
    Я не согласен с вашим определением того, что такое закрытие. Нет причин, по которым это должно быть самоисключение. Это также немного упрощенно (и неточно), чтобы сказать, что оно должно быть «возвращено». (обсуждение этого вопроса в комментариях верхнего ответа на этот вопрос) – James Montagne 26 February 2013 в 21:51
  • 2
    @James, даже если вы не согласны, его пример (и весь пост) является одним из лучших, что я видел. Хотя вопрос не старый и решенный для меня, он полностью заслуживает +1. – e-satis 27 February 2013 в 13:20
  • 3
    «Мне нужно знать, сколько раз была нажата кнопка, и делать что-то на каждом третьем клике ...» . Это привлекло мое внимание. Вариант использования и решение, показывающее, как закрытие не является таинственной вещью, и что многие из нас писали их, но точно не знали официального названия. – Chris22 10 January 2014 в 15:49
  • 4
    Хороший пример, потому что он показывает, что & quot; count & quot; во втором примере сохраняется значение "count" и не сбрасываются до 0 каждый раз, когда "элемент" щелчок. Очень информативно! – Adam 21 July 2014 в 07:19
  • 5
    +1 для поведения замыкания . Можем ли мы ограничить поведение закрытия на функции в javascript или эта концепция также может быть применена к другим структурам языка? – Dziamid 8 March 2015 в 20:32
48
ответ дан 2 revs, 2 users 90% 18 August 2018 в 17:20
поделиться

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

Вот замыкание:

var a = 42;

function b() { return a; }

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

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

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

100
ответ дан 2 revs, 2 users 97% 18 August 2018 в 17:20
поделиться
  • 1
    Этот ответ, похоже, не поможет разблокировать людей. Грубым эквивалентом на традиционном языке программирования может быть создание b () как метода для объекта, в котором также имеет частную константу или свойство a. На мой взгляд, удивление заключается в том, что объект области JS эффективно обеспечивает a как свойство, а не константу. И вы заметите только это важное поведение, если вы измените его, как в return a++; – Jon Coombs 15 May 2015 в 01:34
  • 2
    Именно то, что сказал Джон. До того, как я окончательно закрыл закрытие, мне было трудно найти практические примеры. Да, флорибон создал закрытие, но для необразованного меня это не научило бы мне абсолютно ничего. – Chev 30 October 2015 в 00:15
  • 3
    Это не определяет, что такое закрытие - это просто пример, который использует его. И это не касается нюансов о том, что происходит, когда область действия заканчивается; Я не думаю, что у кого-то был вопрос о лексическом охвате, когда все области все еще существуют, и особенно в случае глобальной переменной. – Gerard ONeill 9 August 2016 в 15:51

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

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

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

outerFunction();

ALERT: обезьяна

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

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

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

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

ALERT: обезьяна

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

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

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

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

outerFunction = null;
alert(referenceToInnerFunction());

ALERT: обезьяна ALERT: обезьяна

Но как это так? Как может referenceToInnerFunction все еще знать значение outerVar, теперь, когда внешняя функция была установлена ​​в нуль?

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

///////// /

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

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

    innerFunction();
}

outerFunction();

ALERT: gorilla

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

46
ответ дан 2 revs, 2 users 99% 18 August 2018 в 17:20
поделиться

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

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

43
ответ дан 3 revs, 2 users 50% 18 August 2018 в 17:20
поделиться
  • 1
    Я согласен: указанная страница Mozilla особенно проста и лаконична. Удивительно то, что ваш пост не был так широко оценен, как другие. – Brice Coustillas 28 April 2018 в 08:21

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

// 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
81
ответ дан 3 revs, 2 users 90% 18 August 2018 в 17:20
поделиться

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

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

196
ответ дан 3 revs, 3 users 50% 18 August 2018 в 17:20
поделиться

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

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

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
87
ответ дан 3 revs, 3 users 64% 18 August 2018 в 17:20
поделиться
  • 1
    FYI: выполнение приведенных выше показывает = & gt; 9 – JJ Rohrer 19 May 2010 в 21:24
  • 2
    Небольшое разъяснение о возможной двусмысленности. Когда я сказал «Фактически, закрывающая функция вообще не нуждается в возврате». Я не имел в виду & quot; return no value & quot; но «все еще активна». Таким образом, пример не показывает этот аспект, хотя он показывает другой способ, которым внутренняя функция может быть передана во внешнюю область. Основной момент, который я пытался сделать, - это создание time создания замыкания (для закрывающей функции), поскольку некоторые люди, похоже, думают, что это происходит, когда возвращается закрывающая функция. Другой пример необходим, чтобы показать, что замыкание создается, когда функция называется . – dlaliberte 21 July 2011 в 15:03

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

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

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

console.log(x + 3);

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

444
ответ дан 3 revs, 3 users 65% 18 August 2018 в 17:20
поделиться
  • 1
    Согласен. Предоставление функций значимых имен вместо традиционного «foobar». мне тоже очень помогают. Семантика считается. – Ishmael 8 April 2010 в 15:16
  • 2
    поэтому в псевдоязыке это в основном подобно alert(x+3, where x = 5). where x = 5 является замыканием. Я прав? – Jus12 22 December 2010 в 10:52
  • 3
    @ Jus12: точно. За кулисами закрытие - это просто пространство, в котором хранятся текущие значения переменных («привязки»), как в вашем примере. – Konrad Rudolph 22 December 2010 в 12:28
  • 4
    Это как раз тот пример, который вводит в заблуждение многих людей, думая, что это значения values ​​, которые используются в возвращаемой функции, а не сама переменная. Если бы он был изменен на «return x + = y» или, еще лучше, и то, и другое, и функция «x * = y», тогда было бы ясно, что ничего не копируется. Для людей, использующих стек кадров, представьте, вместо этого используйте кадры heap, которые могут продолжать существовать после возвращения функции. – Matt 21 June 2013 в 13:36
  • 5
    @Matt Я не согласен. Например, not должен исчерпывающе документировать все свойства. Он должен быть редуктивным и иллюстрировать характерную особенность концепции. ОП попросил простое объяснение («для шестилетнего»). Возьмите принятый ответ: он полностью терпит неудачу при предоставлении краткого объяснения, именно потому, что он пытается быть исчерпывающим. (Я согласен с вами в том, что важным свойством JavaScript является привязка по ссылке, а не по значению ... но опять же, успешное объяснение - это то, что сводится к минимуму). – Konrad Rudolph 21 June 2013 в 15:30

Я собрал интерактивный учебник по 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
128
ответ дан 3 revs, 3 users 89% 18 August 2018 в 17:20
поделиться

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

Секреты функций JavaScript - это частные переменные

var parent = function() {
 var name = "Mary"; // secret
}

Каждый раз, когда вы вызываете его, локальная переменная «name» создал и получил имя «Мэри». И каждый раз, когда функция выходит из переменной, теряется и имя забывается.

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

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

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

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

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

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

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

И теперь, хотя Мэри «больше не работает», память о ней не потеряна, и ее ребенок всегда будет помнить ее имя и другие секреты, которые они делили в течение их времени вместе.

Итак, если вы позвоните ребенку «Алиса», она ответит

child("Alice") => "My name is Alice, child of Mary"

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

118
ответ дан 3 revs, 3 users 95% 18 August 2018 в 17:20
поделиться
  • 1
    Это объяснение, которое имеет для меня наибольший смысл, поскольку оно не предполагает значительного предварительного знания технических терминов. Высшее проголосоваемое объяснение здесь предполагает, что человек, который не понимает закрытий, имеет полное и полное понимание терминов, таких как «лексический охват» и «контекст исполнения», - хотя я понимаю это концептуально, я не думаю, что я как комфортно с деталями из них, как я должен быть, и объяснение без жаргона в нем вообще - то, что сделало закрытие, наконец, нажмите для меня, спасибо. В качестве бонуса, я думаю, это также объясняет, какая область очень лаконична. – Emma W 17 May 2015 в 20:30

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

function foo(x) {
  var tmp = 3;

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

  bar(10);
}

foo(2);

Это всегда будет записывать 16, потому что bar может получить доступ к x, который был определен как аргумент foo, а также tmp из foo. Он также может получить доступ к tmp.

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

function foo(x) {
  var tmp = 3;

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

var bar = foo(2); // bar is now a closure.
bar(10);

Вышеуказанная функция также будет записываться в журнал 16, поскольку bar все еще может ссылаться на x и tmp, даже если он больше не находится внутри области.

Однако, поскольку tmp все еще висит внутри bar , он также увеличивается. Он будет увеличиваться каждый раз при вызове bar.

Простейшим примером замыкания является следующее:

var a = 10;
function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

При вызове функции JavaScript новый контекст выполнения создано. Вместе с аргументами функции и родительским объектом этот контекст выполнения также принимает все переменные, объявленные вне него (в приведенном выше примере «a» и «b»).

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

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

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

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Как и ожидалось, каждый вызов bar(10) будет увеличиваться x.memb. Нельзя ожидать, что x просто ссылается на тот же объект, что и переменная age! После пары звонков в bar, age.memb будет 2! Эта ссылка является основой для утечек памяти с объектами HTML.

3810
ответ дан 37 revs, 27 users 37% 18 August 2018 в 17:20
поделиться
  • 1
    @feeela: Да, каждая функция JS создает закрытие. Переменные, на которые не ссылаются, скорее всего, будут иметь право на сбор мусора в современных JS-машинах, но это не меняет того факта, что при создании контекста выполнения этот контекст имеет ссылку на охватывающий контекст выполнения и его переменные и эта функция является объектом, который может быть перемещен в другую область переменной, сохраняя эту исходную ссылку. Это закрытие. – user 19 August 2013 в 02:31
  • 2
    @Ali Я только что обнаружил, что jsFiddle, который я предоставил, на самом деле ничего не доказывает, поскольку delete терпит неудачу. Тем не менее, лексическая среда, которую функция будет выполнять как [[Область]] (и в конечном итоге использовать в качестве основы для своей собственной лексической среды при вызове), определяется, когда выполняется оператор, определяющий функцию. Это означает, что функция - это , закрывающая содержимое ENTIRE области выполнения, независимо от того, на какие значения она действительно ссылается и выходит ли она из области видимости. Пожалуйста, посмотрите разделы 13.2 и 10 в spec – Asad Saeeduddin 20 August 2013 в 18:51
  • 3
    Это был хороший ответ, пока он не попытался объяснить примитивные типы и ссылки. Это становится совершенно неправильным и говорит о копировании литералов, что действительно не имеет ничего общего с чем-либо. – Ry-♦ 4 July 2014 в 15:53
  • 4
    Закрытие - это ответ JavaScript на объектно-ориентированную программирование на основе классов. JS не основан на классе, поэтому нужно было найти другой способ реализовать некоторые вещи, которые иначе не могли быть реализованы. – Barth Zalewski 18 September 2014 в 11:45
  • 5
    это должен быть принятый ответ. Магия никогда не происходит во внутренней функции. Это происходит при назначении внешней функции переменной. Это создает новый контекст выполнения для внутренней функции, поэтому «частная переменная» могут накапливаться. Конечно, это возможно, поскольку переменная, назначенная внешней функции, сохранила контекст. Первый ответ просто усложняет все это, не объясняя, что на самом деле происходит. – Albert Gao 18 August 2016 в 00:26
342
ответ дан 4 revs, 2 users 76% 18 August 2018 в 17:20
поделиться

Функции JavaScript могут обращаться к их:

  1. Аргументы
  2. Локали (то есть их локальные переменные и локальные функции)
  3. Среда, которая включает : globals, включая 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
}

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

73
ответ дан 4 revs, 3 users 81% 18 August 2018 в 17:20
поделиться
58
ответ дан 5 revs, 3 users 77% 18 August 2018 в 17:20
поделиться

Я стараюсь лучше учиться на тестах GOOD / BAD. Мне нравится видеть рабочий код, за которым следует нерабочий код, с которым кто-то может столкнуться. Я собрал a jsFiddle , который делает сравнение, и пытается свести различия к простейшим объяснениям, которые я мог бы придумать.

Завершения выполнены правильно:

console.log('CLOSURES DONE RIGHT');

var arr = [];

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

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

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

Завершение сделано неправильно :

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

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

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • В приведенном выше коде цикл был перемещен внутри функции createClosureArray(), и теперь функция только возвращает завершенный массив, который на первый взгляд кажется более интуитивным.
  • Что может быть не очевидным, так это то, что поскольку createClosureArray() вызывается только после создания только одной области для этой функции вместо одной для каждой итерации цикла.
  • В этой функции переменная с именем index. Цикл запускается и добавляет функции массиву, возвращающему index. Обратите внимание, что index определяется внутри функции createClosureArray, которая только когда-либо вызывается один раз.
  • Поскольку в функции createClosureArray() имеется только одна область, index привязана только к значению в пределах этого объема. Другими словами, каждый раз, когда цикл меняет значение index, он меняет его на все, что ссылается на него в пределах этой области.
  • Все функции, добавленные в массив, возвращают переменную SAME index из родительской области, где она была определена вместо 10 различных из 10 различных областей, таких как первый пример. Конечным результатом является то, что все 10 функций возвращают одну и ту же переменную из той же области.
  • После завершения цикла и index было изменено конечное значение 10, поэтому каждая функция, добавленная в массив, возвращается значение единственной переменной index, которая теперь установлена ​​в 10.

Результат

ЗАКРЫТЬ СДЕЛАНО ПРАВО n = 0 n = 1 n = 2 n = 3 n = 4 n = 5 n = 6 n = 7 n = 8 n = 9

ЗАКРЫВАЕТСЯ СОВЕРШЕННО n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10 n = 10

163
ответ дан 5 revs, 3 users 82% 18 August 2018 в 17:20
поделиться
  • 1
    Хорошее дополнение, спасибо. Чтобы сделать это более понятным, можно себе представить, как «плохо» массив создается в "плохой" цикл с каждой итерацией: 1-я итерация: [function () {return 'n =' + 0;}] 2-я итерация: [(function () {return 'n =' + 1;}), (function () {return ' n = '+ 1;})] 3-я итерация: [(function () {return' n = '+ 2;}), (function () {return' n = '+ 2;}), (function () { return 'n =' + 2;})] и т. д. Таким образом, каждый раз, когда изменяется значение индекса, оно отражается во всех функциях, уже добавленных в массив. – Alex Alexeev 8 April 2016 в 22:38
  • 2
    Лучшее рабочее определение здесь! – KK. 4 October 2017 в 02:31
  • 3
    Использование let для var фиксирует разницу. – Rupam Datta 16 October 2017 в 10:25
  • 4
    Не здесь «Закрытие сделано правильно». является примером «закрытия внутри закрытия»? – TechnicalSmile 29 December 2017 в 11:17
  • 5
    Я имею в виду, что каждая функция технически является закрытием, но важной частью является то, что функция определяет новую переменную внутри. Полученная функция возвращает только ссылки n, созданные в новом закрытии. Мы просто возвращаем функцию, чтобы сохранить ее в массиве и вызывать ее позже. – Chev 29 December 2017 в 19:52

Закрытие очень похоже на объект. Он создается при каждом вызове функции.

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

Переменная содержится в закрытии , если вы

  1. назначили ее с помощью var foo=1; или
  2. просто напишите var foo;

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

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

Пример

 function example(closure) {
   // define somevariable to live in the closure of example
   var somevariable = 'unchanged';

   return {
     change_to: function(value) {
       somevariable = value;
     },
     log: function(value) {
       console.log('somevariable of closure %s is: %s',
         closure, somevariable);
     }
   }
 }

 closure_one = example('one');
 closure_two = example('two');

 closure_one.log();
 closure_two.log();
 closure_one.change_to('some new value');
 closure_one.log();
 closure_two.log();

Выход

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
319
ответ дан 5 revs, 4 users 65% 18 August 2018 в 17:20
поделиться
  • 1
    Ничего себе, никогда не знал, что вы можете использовать замену строк в console.log. Если кого-то интересует, есть еще: developer.mozilla.org/en-US/docs/DOM/… – Flash 4 January 2013 в 04:59
  • 2
    Переменные, входящие в список параметров функции, также являются частью закрытия (например, не ограничиваются только var). – Thomas Eding 18 March 2015 в 04:38

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

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

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

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

Пример:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
47
ответ дан 6 revs, 2 users 77% 18 August 2018 в 17:20
поделиться

Wikipedia on closures :

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

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

153
ответ дан 6 revs, 3 users 78% 18 August 2018 в 17:20
поделиться
  • 1
    Это лучшее объяснение закрытия JavaScript. Должен быть выбранный ответ. Остальные достаточно интересны, но этот метод действительно полезен для реальных кодеров JavaScript. – geoidesic 4 February 2018 в 13:32

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

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

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

Процесс состоит из двух шагов:

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

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

Если переменная читается, но не может быть

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

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

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

Замыкания

Вложенная функция может продолжаться

Разметка LexicalEnvironments:

g32]

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

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

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

Подводя итог:

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

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

198
ответ дан 7 revs, 2 users 81% 18 August 2018 в 17:20
поделиться
  • 1
    Что произойдет, если вы позвонили: second_calculator = first_calculator (); вместо second_calculator = make_calculator (); ? Должно быть одно и то же, не так ли? – Ronen Festinger 7 October 2016 в 05:02
  • 2
    @Ronen: Поскольку first_calculator является объектом (а не функцией), вы не должны использовать круглые скобки в second_calculator = first_calculator;, поскольку это назначение, а не вызов функции. Чтобы ответить на ваш вопрос, тогда будет только один вызов make_calculator, поэтому будет сделан только один калькулятор, и переменные first_calculator и second_calculator будут ссылаться на один и тот же калькулятор, поэтому ответы будут 3, 403, 4433, 44330. – Matt 10 October 2016 в 09:49

Можете ли вы объяснить закрытие 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 [/g3]

* AC # вопрос

186
ответ дан 7 revs, 3 users 70% 18 August 2018 в 17:20
поделиться
  • 1
    Если вы прочитаете описание, вы увидите, что ваш пример неверен. Вызов функции innerFunction входит в объем внешней функции, а не, как описано в описании, после возврата внешней функции. Всякий раз, когда вы вызываете externalFunction, создается новая внутренняя функция, а затем используется в области видимости. – Moss 6 December 2010 в 17:11
  • 2
  • 3
    Увидев, что innerFunction не ссылается на внешний внешний интерфейс, является ли интерпретатор достаточно умным, чтобы убедиться, что он не нуждается в закрытии? – syockit 7 March 2011 в 06:49
  • 4
    Код является «правильным», в качестве примера замыкания, хотя он не касается части комментария об использовании замыкания после возвращения внешней функции. Так что это не отличный пример. Существует много других способов использования замыкания, которые не связаны с возвратом внутренней функции. например innerFunction может быть передан другой функции, где он вызывается немедленно или сохраняется и вызывается через некоторое время, и во всех случаях он имеет доступ к контексту externalFunction, который был создан при его вызове. – dlaliberte 4 August 2011 в 15:01
  • 5
    @syockit Нет, Мосс ошибается. , независимо от , создается ли функция когда-либо из области, в которой она определена, и безоговорочно созданная ссылка на лексическую среду родителя делает все переменные в родительской области доступными для всех функций независимо от независимо от того, вызывается ли они вне или внутри области, в которой они были созданы. – Asad Saeeduddin 21 August 2013 в 14:41

ОК, 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");
331
ответ дан 7 revs, 5 users 62% 18 August 2018 в 17:20
поделиться
  • 1
    Хорошо сыграл и отвечает на оригинальный плакат. Я думаю, что это лучший ответ. Я собирался использовать багаж таким же образом: представьте, что вы заходите в дом бабушки, и вы упаковываете свой случай nintendo DS с игровыми карточками внутри вашего футляра, но затем упакуйте корпус в свой рюкзак, а также поместите игровые карты в карманы рюкзака и Тогда вы положили все это в большой чемодан с большим количеством игровых карт в карманах чемодана. Когда вы попадаете в дом бабушки, вы можете играть в любую игру на своих DS, пока открыты все внешние случаи. Или что-то в этом роде. – slartibartfast 19 September 2013 в 01:37

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

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

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

52
ответ дан 9 revs, 2 users 88% 18 August 2018 в 17:20
поделиться

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

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

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

Full post:

Итак, каковы эти объекты закрытия?

216
ответ дан Nathan Long 18 August 2018 в 17:20
поделиться
  • 1
    Таким образом, можно было бы подчеркнуть основное преимущество закрытий с помощью этого примера? Скажем, у меня есть функция emailError (sendToAddress, errorString), после чего я могу сказать devError = emailError("devinrhode2@googmail.com", errorString), а затем иметь собственную пользовательскую версию общей функции emailError? – Devin G Rhode 31 July 2011 в 07:42
  • 2
    После того, как я проложил себе путь сквозь множество «всплесков», я начал наконец понимать, для чего они нужны. Я подумал про себя: «ах, как частные переменные в объекте?» и бам. Это был следующий ответ, который я прочитал. – Mixologic 30 December 2012 в 23:20

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

80
ответ дан Rakesh Pai 18 August 2018 в 17:20
поделиться
  • 1
    Это лишь половина объяснения. Важно отметить, что, если внутренняя функция все еще упоминается после выхода внешней функции, старые значения внешней функции все еще доступны для внутренней. – pcorcoran 21 September 2008 в 23:29
  • 2
    На самом деле это не старые значения внешней функции, доступные для внутренней функции, а старые переменные , которые могут иметь новые значения, если какая-либо функция была способна измените их. – dlaliberte 16 August 2012 в 03:39

Ты спишь и приглашаешь Дэн. Вы говорите Дэну, чтобы он привел один контроллер 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.");
77
ответ дан StewShack 18 August 2018 в 17:20
поделиться
198
ответ дан 7 revs, 2 users 81% 18 August 2018 в 17:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: