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

280

Я изо всех сил стараюсь обернуть голову вокруг замыканий 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();
Алекс
источник
17
+1 за то, что ты старался изо всех сил. Как только вы освоите их, вы мгновенно станете гораздо лучшим программистом.
Энди Э
7
Я только что написал сообщение в блоге о замыканиях в JavaScript, которое вы можете найти полезным.
Скиллдрик
@Skilldrick. ссылка мертва ... а также я нашел этот практический пример очень полезным. youtube.com/watch?v=w1s9PgtEoJs .
Абхи

Ответы:

241

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

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

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

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

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

Франсиско Сото
источник
26
Ах, если это замыкание, то я использовал замыкания, не зная этого! Я часто помещаю функции в другие подобные объекты, а затем выставляю все, что мне нужно, публично, возвращая объектный литерал, как в вашем примере.
Алекс
1
Да, как вы видите, вы сохраняете контекст функции, потому что возвращаемый объект ссылается на переменные (и функции) внутри нее. Итак, вы использовали их, вы просто не знали об этом.
Франциско Сото
9
Технически каждая функция, которую вы делаете в Javascript в браузере, является закрытием, потому что объект окна связан с ним.
Адам Гент
9
Я знаю, что это старый вопрос, но для меня это все еще не дает адекватного ответа. Почему бы просто не вызвать функцию напрямую? Зачем вам нужна частная функция?
qodeninja
5
Потому что даже несмотря на то, пример имеет только функцию, она также может иметь переменные, которые не доступны извне. Скажем: var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 и т. Д.
Франциско Сото
212

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

<button onclick="updateClickCount()">click me</button>  

Теперь может быть много подходов, таких как:

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

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

Но подводный камень в том, что любой скрипт на странице может изменить счетчик без вызоваupdateClickCount() .


2) Теперь вы можете подумать об объявлении переменной внутри функции:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

Но эй! Каждый раз, когда updateClickCount()вызывается функция, счетчик снова устанавливается на 1.


3) Думаете о вложенных функциях ?

Вложенные функции имеют доступ к области «над» ними.
В этом примере внутренняя функция updateClickCount()имеет доступ к переменной счетчика в родительской функцииcountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

Это могло бы решить дилемму противодействия, если бы вы могли добраться до updateClickCount()функции извне, и вам также нужно найти способ выполнить counter = 0только один раз, а не каждый раз.


4) Закрытие на помощь! (функция, вызывающая себя) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

Самопризывающая функция запускается только один раз. Он устанавливает counterноль (0) и возвращает выражение функции.

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

Это называется закрытием JavaScript . Это позволяет функции иметь « частные » переменные.

Он counterзащищен областью действия анонимной функции и может быть изменен только с помощью функции добавления!

Более живой пример на Закрытие:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


Ссылка: https://www.w3schools.com/js/js_function_closures.asp

JerryGoyal
источник
50
Это первый ответ, который заставил меня сказать: «О, вот почему я бы использовал замыкания!»
Адвокат дьявола
6
ты сделал мой день :)
JerryGoyal
15
Я только что прочитал страницу w3schools по закрытию, а затем пришел сюда для получения дополнительной информации. Это то же самое, что и страница w3schools
tyelford
1
@JerryGoyal Вы могли бы заставить его работать с 2 отдельными кнопками? Я не могу понять, как, не прибегая к 2 переменным (копиям функции), которая, кажется, удаляет некоторые из основных преимуществ / удобства.
Тайлер Кольер
2
Хороший ответ. Обратите внимание, что замыкание не обязательно должно быть функцией, вызывающей сам себя, но это может быть. Когда замыкание вызывается самостоятельно (т. Е. Немедленно вызывается путем добавления () после функции), это означает, что возвращаемое значение вычисляется немедленно, а не возвращаемая функция , а возвращаемое значение вычисляется позже после вызова функции. Замыканием на самом деле может быть любая функция в другой функции, и ее ключевой характеристикой является то, что она имеет доступ к области действия родительской функции, включая ее переменные и методы.
Крис Холкроу
69

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

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

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

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

Марсело Кантос
источник
3
( int?) В последний раз, когда я проверял, JavaScript был языком типа «утка». Возможно, вы думали о Java?
Hello71
1
@ Hello71: я думал о JavaScript, но старые привычки умирают трудно. Хороший улов.
Марсело Кантос
2
@MarceloCantos похоже, что вы забыли запятую в реализации счетчика. Я отредактировал твой пост, чтобы исправить это. Надеюсь, что все в порядке :)
Натан Стреппел
2
@ Streppel: Хороший улов! Я более чем рад, что вы сделали мой код лучше. :-)
Марсело Кантос
пытаясь понять # 1 ... Как бы вы назвали бесконтактную сортировку?
Dave2081
26

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

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

Дросселирование :

Регулирование налагает ограничение на максимальное количество вызовов функции в течение определенного времени. Как в «выполнять эту функцию не чаще, чем раз в 100 миллисекунд».

Код:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Debouncing :

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

Код:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

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

Надеюсь, это кому-нибудь поможет.

Мохд Асим Сухайл
источник
18

Да, это хороший пример полезного закрытия. Вызов warnUser создает calledCountпеременную в своей области и возвращает анонимную функцию, которая хранится в warnForTamperпеременной. Поскольку по-прежнему есть замыкание, использующее переменную namedCount, оно не удаляется при выходе из функции, поэтому каждый вызов функции 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); 
Энди Э
источник
Интересно -1, я думаю, это не "практическое использование для закрытия в javascript"?
Энди Э
1
Я нашел некоторую пользу в чтении этого, поэтому я получил +1 перед понижением.
Алекс
1
@alex: спасибо, я заметил повышенный голос. Я почти привык к анонимным понижениям здесь, в SO. Это только раздражает меня, потому что я действительно хотел бы знать, сказал ли я что-то неточное или неправильное, и они, как правило, заставляют вас думать, что вас только что опроверг какой-то другой ответчик, который хочет лучше увидеть свой ответ. К счастью, я не мстительный тип ;-)
Энди Э
1
Я думаю, что это больше обходной путь для сломанных блоков JavaScripts. Вы должны просто иметь возможность добавить var j = i; до первого setTimeout и получить предупреждение, чтобы использовать это j. Другой обходной путь - использовать 'with' следующим образом: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Value of я был "+ i +", когда этот таймер был установлен ")}, 100);}}
davidbuttar
1
@AndyE Забавно, может быть, не то слово. Я только что заметил, что часто люди используют функции для самостоятельного вызова, чтобы объяснить замыкания, как и многие ответы на этой странице. Но функция обратного вызова в setTimeout также является закрытием; это может считаться «практическим использованием», поскольку вы можете получить доступ к некоторым другим локальным переменным из обратного вызова. Когда я узнал о замыканиях, я понял, что это полезно для меня - замыкания есть везде, а не только в аркадных шаблонах JavaScript.
Антуан
14

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

maerics
источник
2
Это может звучать глупо, но разве они не могут просто открыть сам файл JavaScript и увидеть вашу реализацию?
itsmichaelwang
1
@Zapurdead: да, они, конечно, могли видеть реализацию, но они не могли изменить реализацию (случайно или преднамеренно) без непосредственного изменения вашего исходного кода. Я полагаю, вы могли бы сравнить его с защищенными членами в Java.
maerics
6

Существует раздел на практических Closures в Mozilla Developer Network .

Алекс
источник
Просматривая это, я не вижу, насколько это «практично», как будто я удаляю весь return function ()...код, но он все еще работает нормально. Затвор не nessecary
Уховертка
@James_Parsons Вы не могли бы тогда назначить их обработчикам событий, как они сделали в примере тогда.
Алекс
5

Другое распространенное использование для замыканий - это привязка 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);

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

outis
источник
5

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

Без закрытия ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

С закрытием ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();
Люк Шланген
источник
1
Я не уверен, но все же без замыкания вы можете вызвать как var grretBilly =reeting ("Billy", "Bob"); и вызовите grretBilly (); Это все еще сделал бы то же самое ?? Хотя вы создаете закрытие или нет, это отдельная проблема, но передача имени каждый раз здесь не проблема.
user2906608
4

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

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

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

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

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

EdwardGarson
источник
3

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

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>

Abhilash
источник
2

Использование замыканий:

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

Пример :

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

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

Санни СМ
источник
2

Мне нравится пример фабрики функций Mozilla .

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);
Том
источник
11
Это тип примера, который, на мой взгляд, не помогает людям понять замыкания или то, для чего они нужны. Сколько раз вы когда-нибудь писали замыкание, возвращающее функцию для добавления чисел, кроме как в качестве примера?
Мохамад
2

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

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();
Томаш Грабовски
источник
1

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

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

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>
Даррен Крабб
источник
0

Справка: Практическое использование крышек

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

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

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

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

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

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

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

Также мы можем отметить применение функционалов как, например, метода forEach, который применяет функцию к массиву элементов:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

Функция применяется к аргументам (к списку аргументов - в применении и к позиционированным аргументам - в вызове):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Отложенные звонки:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

Функции обратного вызова:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Создание инкапсулированной области видимости с целью сокрытия вспомогательных объектов:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10
Дамодаран
источник
0

Большая часть кода, который мы пишем в интерфейсном JavaScript, основана на событиях - мы определяем некоторое поведение, а затем присоединяем его к событию, которое запускается пользователем (например, щелчок или нажатие клавиши). Наш код обычно присоединяется как обратный вызов: единственная функция, которая выполняется в ответ на событие. size12, size14 и size16 теперь являются функциями, которые изменят размер основного текста до 12, 14 и 16 пикселей соответственно. Мы можем прикрепить их к кнопкам (в данном случае ссылкам) следующим образом:

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;

скрипка

Мухаммед Усман
источник
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
Дональд Дак
1
этот пример, как мне кажется, может быть реализован без замыкания с помощью стандартной функции. Я пытаюсь найти пример чего-то, что НЕ МОЖЕТ быть реализовано без закрытия
Зак Смит
0

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

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

Различия сводятся к следующему:

Анонимные функции Определенные функции

Не может использоваться как метод. Может использоваться как метод объекта.

Существует только в той области, в которой он определен. Существует в объекте, в котором он определен.

Может вызываться только в той области, в которой он определен. Может вызываться в любой точке кода.

Может быть переназначено новое значение или удалено. Не может быть удалено или изменено.

Ссылки

оборота Пол Свитт
источник
0

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

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2

ShAkKiR
источник
0

Объясняя практическое использование для закрытия в JavaScript ---

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

Простой пример такой функции:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

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

Но как насчет того, когда у нас есть ссылка на эту область?

Давайте посмотрим на следующую функцию:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

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

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

Мандип Каур
источник
0

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

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

Кристиан Войцицки
источник