Что такое практическое применение для закрытия в JavaScript?

Я пробую свое самое твердое для обертывания моей головы вокруг закрытий JavaScript.

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

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

Кто-то может показать мне использование реального мира закрытия?

Этот, например?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

257
задан Ram 22 July 2015 в 07:33
поделиться

8 ответов

Я использовал замыкания для таких вещей, как:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

Как вы можете видеть, a теперь является объектом с методом publicfunction ( a.publicfunction () ), который вызывает privatefunction , который существует только внутри укупорочного средства. Вы можете НЕ вызывать privatefunction напрямую (т.е. a.privatefunction () ), просто publicfunction () .

Это минимальный пример, но, может быть, вы увидите его применение? Мы использовали это для обеспечения соблюдения общедоступных / частных методов.

223
ответ дан 23 November 2019 в 02:43
поделиться

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

Самая распространенная проблема, которую я вижу в StackOverflow, - это когда кто-то хочет «отложить» использование переменной, которая увеличивается в каждом цикле, но поскольку переменная имеет ограниченную область видимости, каждая ссылка на переменную будет после завершения цикла, приводя к конечному состоянию переменной:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

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

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 
18
ответ дан 23 November 2019 в 02:43
поделиться

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

  1. Передача параметризованного поведения в алгоритм (классическое программирование высшего порядка):

     functionistance_sort (arr, midpoint) {
    arr.sort (function (a, b) {a - = midpoint; b - = midpoint; return a * a - b * b;}); 
    } 
     
  2. Моделирование объектно-ориентированного программирования:

     function counter () {{{1 }} var a = 0; 
    return {
    inc: function () {++ a; }, 
    dec: function () {--a; }, 
    get: function () {return a; }, 
    reset: function () {a = 0; } 
    } 
    } 
     
  3. Реализация экзотического управления потоком, такого как обработка событий jQuery и AJAX API.

66
ответ дан 23 November 2019 в 02:43
поделиться

Если вас устраивает концепция создания экземпляра класса в объектно-ориентированном смысле (т.е. для создания объекта этого класса), тогда вы близко к пониманию замыканий.

Подумайте об этом так: когда вы создаете экземпляры двух объектов Person, вы знаете, что переменная-член класса «Name» не используется совместно между экземплярами; у каждого объекта есть своя «копия». Точно так же, когда вы создаете замыкание, свободная переменная («namedCount» в вашем примере выше) привязывается к «экземпляру» функции.

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

Итак, предположим, что когда 'calledCount' достигнет определенного значения, вы хотите завершить сеанс пользователя; вам могут потребоваться разные значения для этого в зависимости от того, приходит ли запрос из локальной сети или из большого плохого Интернета (да, это надуманный пример). Для этого вы можете передать различные начальные значения для namedCount в warnUser (например, -3 или 0?).

Частично проблема с литературой - это номенклатура, используемая для их описания («лексическая область», «свободные переменные»). Не позволяйте ему вводить вас в заблуждение, замыкания проще, чем может показаться ... prima facie; -)

4
ответ дан 23 November 2019 в 02:43
поделиться
6
ответ дан 23 November 2019 в 02:43
поделиться

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

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

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

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

14
ответ дан 23 November 2019 в 02:43
поделиться

Некоторое время назад я написал статью о том, как можно использовать замыкания для упрощения кода обработки событий. В ней сравнивается обработка событий ASP.NET с клиентским jQuery.

http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/

1
ответ дан 23 November 2019 в 02:43
поделиться

Другим распространенным использованием замыканий является привязка this в методе к определенному объекту, что позволяет вызывать его из другого места (например, как обработчик событий).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Каждый раз, когда срабатывает событие mousemove, вызывается watcher.follow (evt) .

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

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

становится

fooer = function (x) {
    return function (...) {A x B}
}

, где A и B не синтаксические единицы, а строки исходного кода (не строковые литералы).

См. Конкретный пример в « Оптимизация моего javascript с помощью функции ».

5
ответ дан 23 November 2019 в 02:43
поделиться
Другие вопросы по тегам:

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