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


279

Я намагаюся обернути голову навколо закриття 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, що ви можете знайти helfpul.
Skilldrick

@Skilldrick. посилання мертва ... а також я знайшов цей практичний приклад дуже корисним. youtube.com/watch?v=w1s9PgtEoJs .
Абхі

Відповіді:


240

Я використовував закриття, щоб робити такі речі, як:

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

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

Як ви можете бачити там, aтепер є об'єкт із методом publicfunction( a.publicfunction()), який викликає privatefunction, який існує лише всередині закриття. Ви можете НЕ дзвонити privatefunctionбезпосередньо (тобто a.privatefunction()), просто publicfunction().

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


26
Ах, якщо це закриття, то я використав закриття, не знаючи про це! Я часто розміщую функції всередині іншого подібного, а потім розкриваю будь-яку мені потрібну публіку, повертаючи об’єкт буквально, як у вашому прикладі.
alex

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 і т. Д.
Франциско Сото

211

Припустимо, ви хочете порахувати, скільки разів користувач натискав кнопку на веб-сторінці.
Для цього ви запускаєте функцію в 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


50
Це перша відповідь , який змусив мене сказати : «Ах, ось чому я хотів би використовувати закриття!»
Адвокат диявола

6
u зробив мій день :)
JerryGoyal

15
Я просто прочитав сторінку w3schools про закриття, а потім прийшов сюди для отримання додаткової інформації. Це те саме, що і на сторінці w3schools
tyelford

1
@JerryGoyal ви могли б змусити його працювати з двома окремими кнопками? Я не можу зрозуміти, як не вдаватися до двох змінних (копій функції), які, здається, видаляють деякі основні переваги / зручність.
Тайлер Колліє

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 та API AJAX.


3
( int?) Востаннє я перевірив, JavaScript був мовою набраної качкою. Можливо, ви думали про Яву?
Привіт71

1
@ Hello71: Я думав про JavaScript, але старі звички важко помирають. Хороший улов.
Марсело Кантос

2
@MarceloCantos, схоже, ви забули кому в реалізації лічильника. Я відредагував вашу публікацію, щоб виправити її. Сподіваюся, що це нормально :)
Натан Стреппель

2
@Streppel: Гарний улов! Я більш ніж радий за те, щоб зробити мій код краще. :-)
Марсело Кантос

намагаючись зрозуміти №1 ... Як би ви назвали blizinity_sort?
Dave2081

26

Я знаю, що я дуже пізно відповідаю на це запитання, але це може допомогти тим, хто все ще шукає відповідь у 2018 році.

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

Дросель :

Throttling встановлює обмеження, оскільки максимальна кількість разів може бути викликана функцією протягом часу. Як і в "виконувати цю функцію не більше одного разу на 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)
    }
  }
}

Денонсування :

Заборгованість ставить обмеження на функцію, яку не потрібно повторно викликати, поки не пройде певний час, не викликаючи її. Як і в "виконайте цю функцію лише в тому випадку, якщо минуло 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змінній. Оскільки досі існує закриття, використовуючи змінну callCount, вона не видаляється після виходу функції, тому кожен виклик 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 перед головою.
alex

1
@ Алекс: дякую, я помітив відгук. Я мало не звик до анонімних попутів тут у SO. Це мене тільки дратує, тому що я дуже хотів би дізнатися, чи сказав я щось неточне чи неправильне, і вони, як правило, змушують вас думати, що вас щойно образив хтось інший відповідач, який хоче кращої наочності для власної відповіді. На щастя, я не мстивий тип ;-)
Енді Е

1
Я думаю, що це більше обхід для розбитого блоку JavaScripts. Ви повинні просто мати змогу додати var j = i; перед першим setTimeout і отримуйте сповіщення про використання цього j. Інша робота навколо - використовувати "з" як-от так: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Значення я був "+ 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об'єкта, але тоді він буде видимий для користувачів сценарію, і вони могли навіть змінити його, якщо захотіли, навіть не потребуючи вашого вихідного коду. Однак, оскільки вона закрита анонімною функцією, яка повертає функцію пошуку дати, вона доступна лише функцією пошуку, тому тепер захищена від несанкціонованого захисту.


2
Це може здатися німим, але чи не могли вони просто відкрити сам файл JavaScript і побачити вашу реалізацію?
itsmichaelwang

1
@Zapurdead: так, вони, звичайно, могли побачити реалізацію, але вони не змогли змінити реалізацію (випадково чи навмисно), не змінивши безпосередньо свій вихідний код. Я думаю, ви могли б порівняти його із захищеними членами на Java.
maerics

6

Існує розділ на практичних Closures в Mozilla Developer Network .


Переглядаючи це, я не бачу, як це "практично", ніби я видаляю весь return function ()...код, як і раніше працює добре. Закриття не є несекретним
вушна перука

@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 з функцією " для конкретного прикладу.


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 = привітання ("Біллі", "Боб"); і викликати grretBilly (); Це все-таки зробить те саме ?? хоча ви створюєте закриття чи ні, це інша проблема, але передача імені кожного разу тут не є проблемою.
user2906608

4

Якщо вам подобається концепція інстанціювання класу в об'єктно-орієнтованому сенсі (тобто для створення об'єкта цього класу), то ви близькі до розуміння закриттів.

Подумайте про це так: коли ви інстанціюєте два об'єкти Person, ви знаєте, що змінна елемента класу "Ім'я" не поділяється між екземплярами; кожен об'єкт має свою "копію". Аналогічним чином, коли ви створюєте закриття, вільна змінна ('callCount' у вашому прикладі вище) прив’язана до 'екземпляра' функції.

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

Отже, припустимо, коли "callCount" досягає певного значення, яким ви хочете закінчити сеанс користувача; ви можете мати різні значення для цього, залежно від того, чи надходить запит з локальної мережі чи великого поганого Інтернету (так, це надуманий приклад). Для цього ви можете передати різні початкові значення для callCount у warnUser (тобто -3, або 0?).

Частина проблеми з літературою - це номенклатура, яка використовується для їх опису ("лексична область", "вільні змінні"). Не дозволяйте це обдурити, закриття простіше, ніж здавалося б ... prima facie ;-)


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>


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>

У наведеному вище коді змінна назва зовнішньої функції доступна внутрішнім функціям, і іншого способу доступу до внутрішніх змінних немає, окрім як внутрішніх функцій. Внутрішні змінні внутрішньої функції виступають безпечними запасами для внутрішніх функцій. Вони містять "стійкі", але захищені дані для внутрішніх функцій, з якими можна працювати. Функції навіть не повинні призначатися змінній або мати ім'я. читайте тут докладно


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

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

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


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.