JavaScript'te bir kapatma için pratik kullanım nedir?


280

Ben ediyorum çalışırken JavaScript kapanışları kafamı sarmak için benim en zor.

Bir iç işlev döndürerek bunu elde edebilirim, üst ebeveynde tanımlanan herhangi bir değişkene erişebilir.

Bu benim için nerede yararlı olabilir? Belki de henüz kafamı bulamamıştım. Çevrimiçi gördüğüm örneklerin çoğu gerçek dünya kodu sağlamaz, sadece belirsiz örnekler verir.

Birisi bana gerçek dünyayı bir kapak kullanımını gösterebilir mi?

Mesela bu mu?

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
En zor denemek için +1 :-) Kapanışlar başlamak gerçekten zor görünebilir, onlar benim için olduklarını biliyorum. Bir kez onları asmak olsun, anında çok daha iyi bir kodlayıcı olacak.
Andy E

7
JavaScript'teki kapanışlarla ilgili helfpul bulabileceğiniz bir blog yazısı yazdım .
Skilldrick

@Skilldrick. bağlantı öldü ... ve ayrıca bu pratik örneği çok yardımcı buldum. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Yanıtlar:


241

Kapanışları aşağıdaki gibi şeyler yapmak için kullandım:

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

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

Gördüğünüz gibi a, şimdi yalnızca kapak içinde bulunan bir yöntem publicfunction( a.publicfunction()) ile bir nesne privatefunctionvar. Sen edebilirsiniz DEĞİL çağrı privatefunction(yani doğrudan a.privatefunction()sadece) publicfunction().

Bu minimal bir örnek ama belki de kullanımlarını görebilirsiniz? Bunu kamusal / özel yöntemleri uygulamak için kullandık.


26
Ah, bu bir kapanışsa, o zaman bilmeden kapanışları kullandım! Sık sık işlevleri böyle bir başkasına koyarım ve sonra örneğinizdeki gibi bir nesneyi döndürerek halka açık olan herhangi bir şeyi ortaya çıkarırım.
alex

1
Evet, gördüğünüz gibi, döndürdüğünüz nesne içindeki değişkenlere (ve işlevlere) başvurduğu için işlevin bağlamını korursunuz. Yani onları kullanıyorsun, bilmiyordun.
Francisco Soto

9
Teknik olarak bir tarayıcıda Javascript'te yaptığınız her işlev, pencere nesnesi ona bağlı olduğu için bir kapanıştır.
Adam Gent

9
Bunun eski bir soru olduğunu biliyorum, ama bana göre bu hala yeterli bir cevap vermiyor. Neden sadece fonksiyonu doğrudan çağırmıyorsunuz? Neden özel bir işleve ihtiyacınız var?
qodeninja

5
Olsa bile örnek sadece bir fonksiyona sahip olduğundan, o da değişkenleri olabilir değil dışarıdan erişilebilir. Say: var obj = (function () {var value = 0; return {get: function () {dönüş değeri;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 vb.
Francisco Soto

212

Kullanıcının bir web sayfasındaki bir düğmeyi kaç kez tıkladığını saymak istediğinizi varsayalım .
Bunun için onclick, değişkenin sayısını güncellemek üzere düğme durumunda bir işlevi tetiklersiniz

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

Şimdi şöyle birçok yaklaşım olabilir:

1) Genel bir değişken ve sayacı artırmak için bir işlev kullanabilirsiniz :

var counter = 0;

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

Ancak, tuzak, sayfadaki herhangi bir komut dosyasının sayacı, çağırmadan değiştirebilmesidirupdateClickCount() .


2) Şimdi, fonksiyonun içindeki değişkeni bildirmeyi düşünüyor olabilirsiniz:

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

Ama hey! Her updateClickCount()işlev çağrıldığında sayaç tekrar 1 olarak ayarlanır.


3) Yuvalanmış fonksiyonları düşünüyor musunuz?

Yuvalanmış işlevler, üstlerindeki "kapsama" erişebilir.
Bu örnekte, iç işlev updateClickCount(), üst işlevdeki sayaç değişkenine erişime sahiptircountWrapper()

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

Bu updateClickCount(), dışardan işleve ulaşabiliyorsanız ve aynı zamanda counter = 0her seferinde değil bir kez yürütmenin bir yolunu bulmanız gerekiyorsa , karşı ikilemi çözmüş olabilir .


4) Kurtarmaya Kapatma! (kendini çağırma işlevi) :

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

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

Kendini çağırma işlevi yalnızca bir kez çalışır. Bu değeri countersıfır (0) olarak ayarlar ve bir işlev ifadesi döndürür.

Bu şekilde updateClickCountbir işlev haline gelir. "Harika" kısmı, ana kapsamdaki sayaca erişebilmesidir.

Buna JavaScript kapatma adı verilir . Bir fonksiyonun " özel " değişkenlere sahip olmasını mümkün kılar .

counterAnonim fonksiyon kapsamı ile korunmaktadır ve sadece eklenti fonksiyonu kullanılarak değiştirilebilir!

Closure hakkında daha canlı örnek:

<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>


Referans: https://www.w3schools.com/js/js_function_closures.asp


50
Bu bana "Oh, bu yüzden kapak kullanacağım!" Diyen ilk cevap.
Devil's Advocate

6
u made my day :)
JerryGoyal

15
Sadece w3schools sayfasını kapanışlarda okudum ve daha fazla bilgi için buraya geldim. Bu W3Schools sayfa aynıdır: w3schools.com/js/js_function_closures.asp
tyelford

1
@JerryGoyal 2 ayrı düğmeyle çalışmasını sağlayabilir misiniz? Bazı temel faydaları / rahatlığı kaldırmak gibi görünüyor 2 değişken (işlevin kopyaları) başvurmadan nasıl anlayamıyorum.
Tyler Collier

2
İyi cevap. Bununla birlikte, bir kapağın kendi kendini çağıran bir işlev olması gerekmez , ancak olabilir . Bir kapama zaman olan (yani hemen işlevi sonra () eklenerek adlandırılır) kendi kendine çağıran, bu araçlar dönüş değeri hemen hesaplanmıştır yerine bir fonksiyonu döndürülen ve dönüş değeri hesaplanmıştır daha sonra fonksiyonu çağrılır kez. Kapatma aslında başka bir işlev içindeki herhangi bir işlev olabilir ve temel özelliği değişkenleri ve yöntemleri de dahil olmak üzere üst işlevin kapsamına erişebilmesidir.
Chris Halcrow

69

Verdiğiniz örnek mükemmel bir örnek. Kapaklar, endişeleri çok temiz bir şekilde ayırmanıza izin veren bir soyutlama mekanizmasıdır. Örneğin, enstrümantasyonu (çağrıları sayma) anlambilimden (hata raporlama API'sı) ayırma durumudur. Diğer kullanımlar şunları içerir:

  1. Parametreli davranışı bir algoritmaya geçirmek (klasik üst düzey programlama):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. Nesneye yönelik programlamanın simülasyonu:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. JQuery'nin Olay işleme ve AJAX API'leri gibi egzotik akış kontrolünün uygulanması.


3
( int?) Son kontrol ettiğimde JavaScript ördek türünde bir dildi. Belki de Java'yı düşünüyordunuz?
Merhaba71

1
@ Merhaba71: JavaScript'i düşünüyordum, ancak eski alışkanlıklar zor ölüyor. İyi yakalama.
Marcelo Cantos

2
@MarceloCantos, sayacın uygulanmasında bir virgül unuttun. Düzeltmek için yayınınızı düzenledim. Umarım sorun yok :)
Natan Streppel

2
@Streppel: İyi yakaladın! Kodumu daha iyi hale getirmen için çok mutluyum. :-)
Marcelo Cantos

# 1'i anlamaya çalışıyorum ... proximity_sort'u nasıl ararsınız?
Dave2081

26

Bu soruyu cevaplamak için çok geç olduğumu biliyorum, ancak 2018'de hala cevabı arayan herkes için yardımcı olabilir.

Javascript kapatmaları, uygulamanıza gaz ve geri dönme işlevini uygulamak için kullanılabilir.

Kısma :

Kısma, bir fonksiyonun zaman içinde en fazla kaç kez çağrılabileceğini sınırlar. "Bu işlevi en fazla 100 milisaniyede bir kez gerçekleştirin."

Kod:

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)
    }
  }
}

Çıkarma :

Sökme, çağrılmadan belirli bir süre geçinceye kadar tekrar çağrılmayacak bir işleve bir sınır koydu. "Bu işlevi yalnızca 100 milisaniye çağrılmadan geçtiyse" yürütün.

Kod:

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

Gördüğünüz gibi, her web uygulamasının sorunsuz UI deneyimi işlevselliği sağlamak için gereken iki güzel özelliğin uygulanmasına yardımcı olan kapaklar.

Umarım birine yardım eder.


18

Evet, bu yararlı bir kapanışa iyi bir örnektir. WarnUser çağrısı calledCountdeğişkeni kendi alanında oluşturur ve değişkende saklanan anonim bir işlev döndürür warnForTamper. CalledCount değişkenini kullanan hala bir kapatma olduğundan, işlevin çıkışından sonra silinmez, bu nedenle her çağrı çağrılanma warnForTamper()kapsamı değişkenini artırır ve değeri uyarır.

StackOverflow üzerinde gördüğüm en yaygın sorun, birisi her döngüde artırılan bir değişkenin kullanımını "geciktirmek" istediği yerdir, ancak değişken kapsamlandırıldığı için, değişkene yapılan her başvuru döngü sona erdikten sonra olur. değişkenin son durumu:

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

Bu, her uyarının aynı değeri i, döngü sona erdiğinde artırıldığı değeri göstermesine neden olur. Çözüm, değişken için ayrı bir kapsam olan yeni bir kapak oluşturmaktır. Bu, değişkeni alan ve durumunu bağımsız değişken olarak depolayan anında yürütülen anonim bir işlev kullanılarak yapılabilir:

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); 

İlginç -1, sanırım bu "javascript bir kapatma için pratik bir kullanım" değil mi?
Andy E

1
Okumak için biraz fayda buldum, bu yüzden aşağı oydan önce +1 verdim.
alex

1
@alex: teşekkürler, upvote'u fark ettim. Neredeyse burada SO anonim downvotes alışkınım. Bu sadece beni rahatsız ediyor çünkü yanlış ya da yanlış bir şey söyleyip söylemediğimi gerçekten bilmek istiyorum ve onlar size kendi cevapları için daha iyi görünürlük isteyen başka bir cevap verenin sizi aşağıladığını düşündürüyor. Neyse ki, ben intikamcı tip değilim ;-)
Andy E

1
Bu daha fazla JavaScripts kırık blok kapsamı için bir çalışma olduğunu düşünüyorum. Sadece var j = i; İlk setten önce Zamanaşımı ve j'yi kullanma uyarısı alın. Etrafında başka bir çalışma 'ile' böyle kullanmaktır: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert (" bu zamanlayıcı ayarlandığında "+ i +" idi ")}, 100);}}
davidbuttar

1
@AndyE Funny doğru sözcük olmayabilir. Sadece insanların sık sık, bu sayfadaki birçok cevap gibi, kapanışları açıklamak için kendini çağırma işlevlerini kullandığını fark ettim. Ancak setTimeout'taki geri çağrı işlevi de bir kapatmadır; geri aramadan diğer bazı yerel değişkenlere erişebildiğiniz için "pratik kullanım" olarak düşünülebilir. Kapakları öğrenirken, bunun benim için yararlı olduğunu fark ettim - kapakların sadece çarşı JavaScript kalıplarında değil, her yerde olduğunu.
antoine

14

JavaScript (veya herhangi bir ECMAScript) dilinde, özellikle, arayüzler hala arabirimi açığa çıkarırken işlevselliğin uygulanmasını gizlemede yararlıdır.

Örneğin, bir tarih sınıfı yardımcı programı yöntemleri yazdığınızı ve kullanıcıların hafta içi adlarını dizine göre aramasına izin vermek istediğinizi, ancak başlık altında kullandığınız ad dizisini değiştirmelerini istemediğinizi düşünün.

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];
    };
  }())
};

Not daysDizi basitçe bir özelliği olarak saklanabilir dateUtilnesne ama sonra senaryonun kullanıcılara görünür olacaktır ve istedikleri takdirde onlar bile bile kaynak kodunu gerek kalmadan, bunu değiştirebilir. Ancak, tarih arama işlevini döndüren anonim işlev tarafından çevrildiğinden, yalnızca arama işlevi tarafından erişilebilir, böylece artık kurcalamaya karşı korumalıdır.


2
Bu aptalca gelebilir, ancak JavaScript dosyasının kendisini açıp uygulamanızı göremediler mi?
itsmichaelwang

1
@Zapurdead: evet, onlar elbette olabilir bkz uygulanmasını ama onlar olamazdı değiştirmek doğrudan kaynak kodunu değiştirmeden uygulanmasını (yanlışlıkla veya kasıtlı olarak). Sanırım bunu Java'daki korumalı üyelerle karşılaştırabilirsiniz.
maerics

6

Bir bölüm vardır Pratik Closures de Mozilla Geliştirici Ağı .


Bu bakarak, ben return function ()...kod hala iyi çalışıyor sanki bütün kaldırmak gibi nasıl "pratik" olduğunu görmüyorum . Kapanış nessecary değil
bir earwig

@James_Parsons Ardından, örnekte yaptıkları gibi olay işleyicilerine atayamazsınız.
alex

5

Kapaklar için bir diğer yaygın kullanım this, bir yöntemde belirli bir nesneye bağlanarak başka bir yerde (olay işleyici gibi) çağrılmasına izin vermektir.

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);

Bir fare engelleme olayı her tetiklendiğinde watcher.follow(evt)çağrılır.

Kapanışlar, aynı zamanda, farklı bölümleri parametrelendirerek, tek bir üst düzey işlev olarak birden çok benzer işlevi yeniden yazmanın çok yaygın örüntüsüne izin veren üst düzey işlevlerin önemli bir parçasıdır. Soyut bir örnek olarak,

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

olur

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

burada A ve B sözdizimsel birimler değil, kaynak kod dizeleri (dizgi değişmez değerleri değil).

Somut bir örnek için bkz. " Javascriptimi bir işlevle düzleştirme ".


5

Burada, birkaç kez söylemek istediğim bir selamım var. Bir kapanış oluşturursam, selamlamayı kaydetmek için bu işlevi çağırabilirim. Eğer kapanışı oluşturmazsam, adımı her seferinde iletmem gerekir.

Kapatma olmadan ( 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");

Bir kapanışla ( 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
Emin değilim ama hala bir kapatma olmadan u var grretBilly = tebrik ("Billy", "Bob"); ve grretBilly () öğesini çağırınız; Stilll de aynısını yapar mı ?? Her ne kadar u kapatma oluşturmak veya farklı bir sorun thats ama adının her zaman geçen burada bir sorun değil.
user2906608

4

Bir sınıfı nesne yönelimli anlamda başlatmaktan (yani o sınıfın bir nesnesini oluşturmaktan) hoşlanıyorsanız, kapanışları anlamaya yakınsınız demektir.

Bunu şu şekilde düşünün: İki Person nesnesini somutlaştırdığınızda, sınıf üyesi değişkeni "Name" in örnekler arasında paylaşılmadığını bilirsiniz; her nesnenin kendi 'kopyası' vardır. Benzer şekilde, bir kapatma oluşturduğunuzda, serbest değişken (yukarıdaki örnekte 'calledCount') işlevin 'örneğine' bağlanır.

Ben kavramsal sıçrama sizin warnUser işlevi tarafından döndürülen her işlev / kapatma (bir kenara: bu daha yüksek dereceli bir işlev ) kapanış 'başlangıç' aynı başlangıç ​​değeri (0) ile bağlanırken sık sık engellenir düşünüyorum, oysa sık sık kapatmalar oluştururken farklı başlatıcıları, bir sınıfın yapıcısına farklı değerler iletmek gibi, daha üst düzey fonksiyona geçirmek daha yararlıdır.

Dolayısıyla, 'calledCount' kullanıcının oturumunu sonlandırmak istediğiniz belirli bir değere ulaştığında varsayalım; isteğin yerel ağdan mı yoksa büyük kötü internetten mi geldiğine bağlı olarak bunun için farklı değerler isteyebilirsiniz (evet, bu anlaşmalı bir örnektir). Bunu başarmak için, callCount için farklı başlangıç ​​değerlerini warnUser'a (yani -3 veya 0?) Geçirebilirsiniz.

Literatürdeki sorunun bir kısmı, bunları tanımlamak için kullanılan terminolojidir ("sözlüksel kapsam", "serbest değişkenler"). Sizi kandırmasına izin vermeyin, kapaklar göründüğünden daha basit ... prima facie ;-)


3

Burada E-ticaret sitemizde veya diğer pek çok alanda kullanabileceğimiz basit bir kapanış konsepti örneğim var. Örnekle jsfiddle bağlantımı ekliyorum. 3 öğeden oluşan küçük bir ürün listesi ve bir alışveriş sepeti sayacı içerir.

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

Kapakların Kullanımı:

Kapanışlar JavaScript'in en güçlü özelliklerinden biridir. JavaScript, işlevlerin iç içe yerleştirilmesine izin verir ve iç işleve, dış işlev içinde tanımlanan tüm değişkenlere ve işlevlere (ve dış işlevin erişebildiği diğer tüm değişkenler ve işlevlere) tam erişim sağlar. Bununla birlikte, dış fonksiyonun, iç fonksiyonun içinde tanımlanan değişkenlere ve fonksiyonlara erişimi yoktur. Bu, iç fonksiyonun değişkenleri için bir çeşit güvenlik sağlar. Ayrıca, iç işlev dış işlevin kapsamına eriştiğinden, eğer iç işlev dış işlevin ömrünün ötesinde hayatta kalmayı başarırsa, dış işlevde tanımlanan değişkenler ve işlevler dış işlevin kendisinden daha uzun yaşayacaktır.

Misal :

<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>

Yukarıdaki kodda, dış işlevin ad değişkenine iç işlevlerden erişilebilir ve iç işlevlere erişmek dışında iç değişkenlere erişmenin başka bir yolu yoktur. İç fonksiyonun iç değişkenleri, iç fonksiyonlar için güvenli depolar olarak işlev görür. Çalışmak için iç işlevlere ilişkin "kalıcı" ancak güvenli veriler tutarlar. Fonksiyonların bir değişkene atanması veya bir ismi olması gerekmez. detay için burayı okuyun


2

Mozilla'nın işlev fabrikası örneğini seviyorum .

function makeAdder(x) {

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

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
Bu, insanların kapanışları veya ne için iyi olduklarını anlamalarına yardımcı olmayan bir örnek. Kaç kez örnek eklemek dışında bir işlev eklemek için bir işlev döndürmek için bir kapak yazdınız?
Mohamad

2

JavaScript modülü düzeni kapanışları kullanır. Onun güzel desen benzer bir şey "kamu" ve "özel" vars sağlar.

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

Bu konu, kapakların nasıl çalıştığını daha iyi anlamamda bana çok yardımcı oldu. O zamandan beri kendi deneylerimi yaptım ve bazı diğer kişilerin kapakların pratik bir şekilde nasıl kullanılabileceğini ve statik ve / veya küresel değişkenlerin üzerine yazılma veya bunlarla karıştırılma riski olmayan global değişkenler. Bunun yaptığı, her bir düğme için yerel düzeyde ve küresel düzeyde, her düğmeye tıklamayı sayan ve tek bir şekle katkıda bulunan düğme tıklamalarını takip etmektir. Not Bunu yapmak için herhangi bir global değişken kullanmadım, bu da egzersizin bir noktası - küresel bir şeye katkıda bulunan herhangi bir düğmeye uygulanabilen bir işleyiciye sahip olmak.

Lütfen uzmanlar, burada kötü uygulamalarda bulunup bulunmadığımı bana bildirin! Ben hala bu şeyleri kendim öğreniyorum.

<!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

Referans: Kapakların pratik kullanımı

Uygulamada, kapaklar çeşitli hesapların, ertelenmiş aramaların, geri aramaların, kapsüllenmiş kapsamın vb. Özelleştirilmesine izin veren zarif tasarımlar oluşturabilir.

Sıralama koşulu işlevini bağımsız değişken olarak kabul eden dizilerin sıralama yöntemine bir örnek:

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

İşlevselleri, yeni bir diziyi işlev bağımsız değişkeninin durumuna göre eşleyen dizilerin eşleme yöntemi olarak eşleme:

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

Genellikle arama işlevlerini, arama için neredeyse sınırsız koşulları tanımlayan işlevsel bağımsız değişkenler kullanarak uygulamak uygundur:

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

Ayrıca, örneğin bir dizi öğeye bir işlev uygulayan bir forEach yöntemi olarak işlevlerin uygulanmasını not edebiliriz:

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

Bir işlev, bağımsız değişkenlere (uygulamada ve konumdaki bağımsız değişkenlere - çağrıda) bir argümanlar listesine uygulanır:

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

Ertelenmiş aramalar:

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

Geri arama işlevleri:

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
};

Yardımcı nesneleri gizlemek için kapsüllenmiş bir kapsamın oluşturulması:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

Ön uç JavaScript'te yazdığımız kodun çoğu olay tabanlıdır - bazı davranışları tanımlarız, ardından kullanıcı tarafından tetiklenen bir etkinliğe (tıklama veya tuşa basma gibi) ekleriz. Kodumuz genellikle bir geri arama olarak eklenir: olaya yanıt olarak yürütülen tek bir işlev. size12, size14 ve size16, şimdi gövde metnini sırasıyla 12, 14 ve 16 piksele yeniden boyutlandıracak işlevlerdir. Bunları düğmelere (bu durumda bağlantılar) aşağıdaki gibi ekleyebiliriz:

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;

Vaktini boşa harcamak


Bu kod soruyu cevaplayabilirken, sorunun nasıl ve / veya neden çözüldüğüne dair ek bağlam sağlamak yanıtlayıcının uzun vadeli değerini artıracaktır.
Donald Duck

1
Bu örnek, bana göre, standart bir fonksiyon ile kapatılmadan uygulanabilir. Kapanmadan uygulanamayacak bir şeyin örneğini bulmaya çalışıyorum
Zach Smith

0

Kapaklar oluşturmak için yararlı bir yoldur , isteğe bağlı olarak artırılan bir dizi:

    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

Farklılıklar aşağıdaki gibi özetlenmiştir:

Anonim işlevler Tanımlı işlevler

Yöntem olarak kullanılamaz Nesnenin yöntemi olarak kullanılabilir

Yalnızca tanımlandığı kapsamda bulunur Tanımlandığı nesne içinde bulunur

Sadece tanımlandığı kapsamda çağrılabilir Kodun herhangi bir noktasında çağrılabilir

Yeni bir değer yeniden atanabilir veya silinebilir Silinemez veya değiştirilemez

Referanslar


0

Verilen örnekte ekteki değişkenin 'sayacının' değeri korunur ve sadece verilen fonksiyonlar kullanılarak (artış, azalma) değiştirilebilir. çünkü kapanışta,

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'te bir Kapatma için pratik kullanımın açıklanması ---

Başka bir fonksiyonun içinde bir fonksiyon yarattığımızda, bir kapanış yaratıyoruz. Kapaklar güçlüdür, çünkü dış işlevlerinin verilerini okuyabilir ve değiştirebilirler. Bir işlev çağrıldığında, bu çağrı için yeni bir kapsam oluşturulur. İşlev içinde bildirilen yerel değişken bu kapsama aittir ve bunlara yalnızca bu işlevden erişilebilir. İşlev yürütmeyi tamamladığında, kapsam genellikle yok edilir.

Böyle bir işlevin basit bir örneği şudur:

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

Yukarıdaki örnekte, buildName () işlevi yerel bir değişken selamlama bildirir ve döndürür. Her işlev çağrısı, yeni bir yerel değişkenle yeni bir kapsam oluşturur. İşlev yürütüldükten sonra, bu kapsama tekrar başvurmamızın bir yolu yoktur, bu yüzden çöp toplanır.

Ama bu kapsamla bir bağlantımız olduğunda ne olacak?

Bir sonraki işleve bakalım:

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

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

Bu örnekteki sayName () işlevi bir kapatma işlemidir. SayName () işlevinin kendi yerel kapsamı vardır (hoş geldiniz değişkeni ile) ve ayrıca dış (çevreleyen) işlevin kapsamına erişimi vardır. Bu durumda, buildName () öğesinden selamlama değişkeni.

BuildName yürütülmesi tamamlandıktan sonra, bu durumda kapsam yok olmaz. SayMyName () işlevinin hala buna erişimi vardır, bu nedenle çöp toplanmaz. Ancak, kapatma dışında verilere dış kapsamdan erişmenin başka bir yolu yoktur. Kapatma, küresel bağlam ve dış kapsam arasında bir geçit görevi görür.


0

Ben clousures öğrenmeye çalışıyorum ve ben yarattığım örnek pratik kullanım örneği olduğunu düşünüyorum. Snippet'i çalıştırabilir ve sonucu konsolda görebilirsiniz. ayrı verileri olan iki ayrı kullanıcımız var. Her biri gerçek durumu görebilir ve güncelleyebilir.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.