Я пробую свое самое твердое для обертывания моей головы вокруг закрытий 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();
Я использовал замыкания для таких вещей, как:
a = (function () {
var privatefunction = function () {
alert('hello');
}
return {
publicfunction : function () {
privatefunction();
}
}
})();
Как вы можете видеть, a
теперь является объектом с методом publicfunction
( a.publicfunction ()
), который вызывает privatefunction
, который существует только внутри укупорочного средства. Вы можете НЕ вызывать privatefunction
напрямую (т.е. a.privatefunction ()
), просто publicfunction ()
.
Это минимальный пример, но, может быть, вы увидите его применение? Мы использовали это для обеспечения соблюдения общедоступных / частных методов.
Да, это хороший пример полезного закрытия. Вызов 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);
Приведенный вами пример отличный. Замыкания - это механизм абстракции, который позволяет очень четко разделять проблемы. Ваш пример представляет собой случай отделения инструментария (подсчет вызовов) от семантики (API-интерфейс для отчетов об ошибках). Другие варианты использования включают:
Передача параметризованного поведения в алгоритм (классическое программирование высшего порядка):
functionistance_sort (arr, midpoint) {
arr.sort (function (a, b) {a - = midpoint; b - = midpoint; return a * a - b * b;});
}
Моделирование объектно-ориентированного программирования:
function counter () {{{1 }} var a = 0;
return {
inc: function () {++ a; },
dec: function () {--a; },
get: function () {return a; },
reset: function () {a = 0; }
}
}
Реализация экзотического управления потоком, такого как обработка событий jQuery и AJAX API.
Если вас устраивает концепция создания экземпляра класса в объектно-ориентированном смысле (т.е. для создания объекта этого класса), тогда вы близко к пониманию замыканий.
Подумайте об этом так: когда вы создаете экземпляры двух объектов Person, вы знаете, что переменная-член класса «Name» не используется совместно между экземплярами; у каждого объекта есть своя «копия». Точно так же, когда вы создаете замыкание, свободная переменная («namedCount» в вашем примере выше) привязывается к «экземпляру» функции.
Я думаю, что вашему концептуальному скачку немного мешает тот факт, что каждая функция / закрытие, возвращаемое функцией warnUser (кроме: это функция высшего порядка ), закрытие связывает 'namedCount' с тем же начальным значением (0), тогда как часто при создании замыканий более полезно передавать различные инициализаторы в функцию более высокого порядка, что очень похоже на передачу разных значений конструктору класса.
Итак, предположим, что когда 'calledCount' достигнет определенного значения, вы хотите завершить сеанс пользователя; вам могут потребоваться разные значения для этого в зависимости от того, приходит ли запрос из локальной сети или из большого плохого Интернета (да, это надуманный пример). Для этого вы можете передать различные начальные значения для namedCount в warnUser (например, -3 или 0?).
Частично проблема с литературой - это номенклатура, используемая для их описания («лексическая область», «свободные переменные»). Не позволяйте ему вводить вас в заблуждение, замыкания проще, чем может показаться ... prima facie; -)
В Mozilla Developer Network есть раздел о Практические закрытия.
В языке 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
, но тогда он будет виден пользователям сценария, и они могут даже изменить его, если они хотел, даже не нуждаясь в вашем исходном коде. Однако, поскольку он заключен в анонимную функцию, которая возвращает функцию поиска даты, он доступен только функции поиска, поэтому теперь он защищен от несанкционированного доступа.
Некоторое время назад я написал статью о том, как можно использовать замыкания для упрощения кода обработки событий. В ней сравнивается обработка событий ASP.NET с клиентским jQuery.
http://www.hackification.com/2009/02/20/closures-simplify-event-handling-code/
Другим распространенным использованием замыканий является привязка 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 с помощью функции ».