86 Soru: JavaScript kapanışları nasıl çalışır?

tarafından oluşturulan soru Sun, Apr 9, 2017 12:00 AM

JavaScript kapanışlarını, oluşturdukları kavramları bilen (örneğin fonksiyonlar, değişkenler ve benzeri) bilgisi olan birisine nasıl açıklarsınız, ancak kapanmaların kendileri anlamıyor mu?

Vikipedi'de verilen Şema örneğini gördüm, ancak ne yazık ki yaptı yardım etmeyin.

    
7649
  1. Bu ve birçok cevapla ilgili sorunum, Javascript ve pratik durumlarda kapatmaların neden gerekli olduğunu açıklamaktan ziyade soyut, teorik bir bakış açısıyla yaklaşmalarıdır. İçinde onları kullandın. Sonunda "ama, neden?" Diye düşünmek zorunda kalacağınız bir makaleyle başlıyorsunuz. Basitçe başlamak isterim: Kapanışlar, JavaScript'in aşağıdaki iki gerçekliği ile ilgilenmenin temiz bir yoludur: a. kapsam fonksiyon düzeyindedir, blok seviyesinde değil ve b. JavaScript'te pratikte yaptığınız işlerin çoğu eşzamansız /olay odaklı.
    2013-03-08 17: 22: 59Z
  2. @ Redsandro Birincisi, etkinlik odaklı kodun yazılmasını çok kolaylaştırır. Sayfa HTML veya mevcut özellikler hakkındaki özellikleri belirlemek için yüklendiğinde bir işlevi ateşleyebilirim. Bir işleyiciyi bu işlevde tanımlayabilir ve ayarlayabilirim ve işleyicinin her yeniden çağrılmasında sorgulama yapmadan tüm bu bağlam bilgisine sahip olabilirim. Sorunu bir kez çözün, işleyicinin ihtiyaç duyduğu her sayfada, işleyicinin yeniden başlatılmasıyla ilgili genel giderlerin azaltılmasıyla yeniden kullanın. Hiç bir zaman aynı verinin kendileri olmayan bir dilde iki kez yeniden haritalandığını gördün mü? Kapanışlar, bu tür şeylerden kaçınmayı çok daha kolaylaştırır.
    2013-06-26 17: 02: 16Z
  3. Java programcıları için kısa cevap, bunun bir iç sınıfın fonksiyonuna eşdeğer olduğudur. Bir iç sınıf ayrıca dış sınıfın bir örneğini de örtülü bir işaretçi tutar ve aynı amaç için kullanılır (yani, olay işleyicileri oluşturmak için).
    2014-06-19 10: 04: 21Z
  4. Bunu bundan daha iyi anlayın: javascriptissexy.com/understand-javascript-closures-with-ease . Diğer cevapları okuduktan sonra kapanış için hala kapanmaya ihtiyaç vardı. :)
    2016-01-22 05: 41: 32Z
  5. Bu pratik örneği çok yararlı buldum: youtube.com/watch?v=w1s9PgtEoJs
    2016-07-06 17: 33: 55Z
30 Yanıtlar                              30                         

Yeni başlayanlar için JavaScript kapanışı

Morris tarafından Salı, 2006-02-21 10:19 tarihinde sunulmuştur. Topluluktan beri düzenlenmiş.

Kapanışlar sihirli değildir

Bu sayfa, çalışan bir JavaScript kodu kullanarak bir programcının onları anlayabilmesi için kapanışları açıklar. Guru ve işlevsel programcılar için değildir.

Kapanışlar zor değil , çekirdek kavram kurulduktan sonra anlaşılması zor. Ancak, teorik veya akademik yönelimli açıklamaları okuyarak anlamaları imkansızdır!

Bu makale, ana dilde bazı programlama deneyimi olan ve aşağıdaki JavaScript işlevini okuyabilen programcılar için hazırlanmıştır:

 
function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

İki kısa özet

  • Bir işlev (foo), diğer işlevleri (bar ve baz) bildirdiğinde, foo'da oluşturulan yerel değişkenler ailesi, işlevden çıktığında yok edilmez olur. Değişkenler sadece dış dünyaya görünmez hale gelir. foo bu sayede bar ve baz fonksiyonlarını şaşırtıcı bir şekilde iade edebilir ve okumaya, yazmaya ve cBu kapanmış değişkenler ailesi ("kapanış") aracılığıyla birbirleriyle karışabilir, gelecekte foo'u tekrar arayacak biri bile olmadan kimseyle karışamaz.

  • Bir kapatma, birinci sınıf işlevleri desteklemenin bir yoludur; kapsamındaki değişkenleri referans alabilen (ilk kez bildirildiğinde), bir değişkene atanabilen, bir fonksiyona argüman olarak geçirilebilen veya bir fonksiyon sonucu olarak döndürülebilen bir ifadedir.

Bir kapatma örneği

Aşağıdaki kod bir fonksiyona referans döndürür:

 
function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

Çoğu JavaScript programcısı, bir fonksiyona yapılan referansın, yukarıdaki koddaki bir değişkene (say2) nasıl döndürüldüğünü anlayacaktır. Eğer yapmazsan, kapatmayı öğrenmeden önce buna bakmalısın. C'yi kullanan bir programcı, işlevi bir işleve bir işaretçi olarak döndürür ve say ve say2 değişkenlerinin her birinin bir işleve işaretçi olduğunu düşünür.

Bir C işaretçisi bir işleve bir işleve bir JavaScript başvurusu ile kritik bir fark var. JavaScript'te, bir işlev referans değişkeninin hem hem de işlevine ilişkin bir işaretçiye sahip olduğunu, kapanmaya yönelik gizli bir işaretçi olduğunu düşünebilirsiniz.

Yukarıdaki kodun bir kapanışı vardır çünkü anonim işlev function() { console.log(text); }, başka bir işlevin içinde , bu örnekte sayHello2() olarak bildirilir. JavaScript’te, function anahtar sözcüğünü başka bir işlev içinde kullanırsanız, bir kapatma işlemi oluşturursunuz.

C ve diğer birçok ortak dilde, bir işlev döndükten sonra işlev döndürülür, yığın değişkeninin yok olması nedeniyle tüm yerel değişkenlere artık erişilemez.

JavaScript'te, başka bir işlev içindeki bir işlevi bildirirseniz, dış işlevin yerel değişkenleri, geri döndükten sonra erişilebilir kalabilir. Bu yukarıda gösterilmiştir, çünkü say2()'dan döndükten sonra sayHello2() işlevini çağırırız. Çağrdığımız kodun text değişkenine başvuruda bulunduğunu, sayHello2() işlevinin yerel değişkeni olduğunu unutmayın.

 
function() { console.log(text); } // Output of say2.toString();

say2.toString()'un çıkışına baktığımızda, kodun text değişkenine başvurduğunu görebiliriz. Anonim işlev, text değerini tutan 'Hello Bob''a başvurabilir, çünkü sayHello2() yerel değişkenleri gizlice kapatıldı.

Deha, JavaScript'te bir işlev referansının aynı zamanda içinde oluşturulmuş olan kapama için gizli bir referansa sahip olmasıdır - delegelerin bir yöntem işaretçisi ve bir nesneye yönelik gizli referans olmalarına benzer.

Daha fazla örnek

Bazı nedenlerden dolayı, kapanışları onlar hakkında okuduğunuzda anlamak gerçekten zor görünüyor, ancak bazı örnekler gördüğünüzde, nasıl çalıştıkları belli oluyor (bu beni biraz zaman aldı). Nasıl çalıştıklarını anlayana kadar örnekler üzerinde dikkatli bir şekilde çalışmanızı öneririm. Nasıl çalıştıklarını tam olarak anlamadan kapakları kullanmaya başlarsanız, yakında çok garip hatalar yaratırsınız!

Örnek 3

Bu örnek, yerel değişkenlerin kopyalanmadığını gösterir - referans olarak tutulur. Dış işlev çıktıktan sonra bile yığın-kare bellekte canlı kalır!

 
function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Örnek 4

Üç genel fonksiyonun da aynı kapanış için ortak bir referansı vardır, çünkü bunların tümü setupSomeGlobals()'a yapılan tek bir çağrıda bildirilir.

 
var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

Üç işlev aynı kapanmaya erişimi paylaştı - üç işlev tanımlandığında setupSomeGlobals() yerel değişkenleri.

Yukarıdaki örnekte, setupSomeGlobals()'u tekrar ararsanız, yeni bir kapatma (yığın çerçevesi!) oluşturulduğunu unutmayın. Eski gLogNumber, gIncreaseNumber, gSetNumber değişkenleri, yeni kapanışı olan new işlevleriyle değiştirilir. (JavaScript'te, başka bir işlev içinde bir işlev bildirdiğinizde, iç işlev (ler) dış işlev çağrıldığında her biri yeniden oluşturulur /yeniden oluşturulur.)

Örnek 5

Bu örnek, kapağın, çıkmadan önce dış işlev içinde bildirilen yerel değişkenleri içerdiğini gösterir. alice değişkeninin isimsiz işlevden sonra gerçekten bildirildiğine dikkat edin. Adsız işlev ilk önce bildirilir ve bu işlev çağrıldığında alice değişkenine erişebilir çünkü alice aynı kapsamdadır (JavaScript değişken kaldırma ). Ayrıca sayAlice()(), doğrudan sayAlice()'dan döndürülen işlev başvurusunu doğrudan çağırır - daha önce yapılanlarla aynıdır ancak geçici değişken olmadan aynıdır.

 
function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Zor: say değişkeninin kapağın içinde olduğunu ve sayAlice() içinde bildirilebilecek herhangi bir başka işlev tarafından erişilebileceğini veya iç işlev içinde yinelemeli olarak erişilebileceğini unutmayın.

Örnek 6

Bu, birçok insan için gerçek bir sonuçtur, o yüzden anlaman gerekiyor. Bir döngü içinde bir işlev tanımlıyorsanız çok dikkatli olun: kapanıştaki yerel değişkenler ilk düşündüğünüz gibi davranmayabilir.

Bu örneği anlamak için Javascript'teki "değişken kaldırma" özelliğini anlamanız gerekir.

 
function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

Satır result.push( function() {console.log(item + ' ' + list[i])}, sonuç dizisine üç kez adsız bir işleve bir başvuru ekler. Anonim işlevlere bu kadar aşina değilseniz, onun gibi düşünün:

 
pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

Örneği çalıştırdığınızda, "item2 undefined"'un üç kez günlüğe kaydedildiğini unutmayın! Bunun nedeni, önceki örneklerde olduğu gibi, buildList (result, i, list ve item olan) yerel değişkenler için yalnızca bir kapanış olmasıdır. Hatsız işlevler fnlist[j]() hatta çağrıldığında; hepsi aynı tek kapatmayı kullanırlar ve mevcut değeri i ve item için tek bir kapanışta kullanırlar (i'un 3 değeri vardır, çünkü item 'item2' değerine sahiptir). Not 0'dan indeksleme yaptığımızdan item'un değeri item2'dur. Ve i ++, i değerini 3 değerine yükseltir.

item anahtar sözcüğü aracılığıyla işlev kapsamındaki bir değişken bildirimi yerine, let değişkeninin blok düzeyinde bir bildirimi kullanıldığında (var anahtar sözcüğüyle) ne olacağını görmek yararlı olabilir. Bu değişiklik yapılırsa, result dizisindeki her isimsiz fonksiyon kendi kapanmasına sahiptir; örnek çalıştırıldığında çıktı aşağıdaki gibidir:

 
item0 undefined
item1 undefined
item2 undefined

i değişkeni let yerine var kullanılarak da tanımlanmışsa, çıkış şu şekildedir:

 
item0 1
item1 2
item2 3

Örnek 7

Bu son örnekte, ana işleve yapılan her çağrı ayrı bir kapanma oluşturur.

 
function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Özet

Her şey tamamen belirsiz görünüyorsa, yapılacak en iyi şey örneklerle oynamaktır. Bir açıklama okumak, örnekleri anlamaktan daha zordur. Kapamalar ve istifleme çerçeveleri vb. Açıklamalarım teknik olarak doğru değil - anlamaya yardımcı olması amaçlanan brüt basitleştirmeler. Temel fikir toplandıktan sonra ayrıntıları daha sonra alabilirsiniz.

Son puan:

  • Başka bir işlevde function kullandığınızda, bir kapak kullanılır.
  • Bir işlev içinde eval() kullandığınızda, bir kapak kullanılır. eval metninde, işlevin yerel değişkenlerine başvuru yapılabilir ve eval içinde eval('var foo = …') kullanarak yeni yerel değişkenler bile oluşturabilirsiniz
  • new Function(…) kullandığınızda ( İşlev kurucusu ) bir fonksiyonun içinde bir kapanma oluşturmaz. (Yeni fonksiyon, dış fonksiyonun yerel değişkenlerine referans veremez.)
  • JavaScript’teki bir kapatma, tıpkı bir işlev çıktığında olduğu gibi tüm yerel değişkenlerin bir kopyasını tutmak gibidir.
  • Bir kapatmanın her zaman sadece bir işleve bir giriş oluşturulduğunu ve yerel değişkenlerin bu kapatıma eklendiğini düşünmek en iyisidir.
  • Kapanışlı bir işlev her çağrıldığında yeni bir yerel değişkenler kümesi tutulur (işlevin içinde bir işlev bildirimi olduğu ve bu işleve ilişkin bir başvuru ya da bunun için dış bir başvuru tutulduğu göz önüne alındığında) bir şekilde).
  • İki işlev aynı kaynak metne sahip gibi görünebilir, ancak 'gizli' kapatılmaları nedeniyle tamamen farklı davranışlara sahip olabilir. JavaScript kodunun aslında bir işlev referansının kapanıp kapanmadığını öğrenebileceğini sanmıyorum.
  • Herhangi bir dinamik kaynak kodu değişikliği (örneğin: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));) yapmaya çalışıyorsanız, myFunction bir kapanışsa işe yaramaz (tabii ki, çalışma zamanında kaynak kod dizesi değiştirmeyi asla düşünmezsiniz bile) ...).
  • İşlev bildirimleri içinde işlev bildirimleri içinde işlevler almak mümkündür… ve birden fazla düzeyde kapatma alabilirsiniz.
  • Normalde kapatmanın, hem yakalanan değişkenlerle hem de işlev için bir terim olduğunu düşünüyorum. Bu makalede bu tanımı kullanmadığımı unutmayın!
  • JavaScript’teki kapanışların normalde işlevsel dillerde bulunanlardan farklı olduğundan şüpheleniyorum.

Bağlantılar

sayesinde

Sadece kapanışları öğrendiyseniz (burada veya başka bir yerde!), o zaman bu makaleyi daha net hale getirebilecek önerebileceğiniz herhangi bir değişiklik hakkında sizden herhangi bir geri bildirim ile ilgileniyorum. Morrisjohns.com (morris_closure @) adresine e-posta gönderin. Lütfen JavaScript’te ne de kapanışlarda guru olmadığımı unutmayın.


Morris'in orijinal gönderisi Internet Arşivi'nde bulunabilir. .

    
6854
2019-06-05 17: 50: 04Z
  1. Brilliant. Özellikle seviyorum: "JavaScript'teki bir kapatma, tüm fonksiyonların bir kopyasında olduğu gibi tüm yerel değişkenlerin bir kopyasını tutmak gibidir."
    2008-09-21 14: 38: 40Z
  2. @ e-satis - Göründüğü gibi mükemmel, "işlev çıkarken olduğu gibi tüm yerel değişkenlerin bir kopyası" yanıltıcıdır. Değişkenlerin değerlerinin kopyalandığını öne sürüyor, ancak gerçekte işlev çağrıldıktan sonra değişmeyen değişkenlerin kümesidir (belki 'eval' hariç belki de: blog.rakeshpai.me/2008/10/… ). Kapatma oluşturulmadan önce işlevin dönmesi gerektiğini, ancak kapağın kapatma olarak kullanılmasından önce dönmesi gerekmediğini öne sürüyor.
    2011-08-08 15: 24: 48Z
  3. Kulağa hoş geliyor: "JavaScript’teki bir kapatma, tıpkı bir işlev çıktığında olduğu gibi tüm yerel değişkenlerin bir kopyasını tutmak gibi bir şey." Ancak birkaç nedenden dolayı yanıltıcıdır. (1) Bir işlev yaratmak için işlev çağrısının çıkması gerekmez. (2) Yerel değişkenlerin değerlerinin bir kopyası değil, değişkenlerin kendileridir. (3) Bu değişkenlere kimin erişimi olduğunu söylemez.
    2013-02-11 18: 20: 22Z
  4. Örnek 5, kodun amaçlandığı gibi çalışmadığı bir "gotcha" gösteriyor. Fakat nasıl düzeltileceğini göstermiyor. Bu diğer cevap bunu yapmanın bir yolunu gösterir.
    2013-06-24 19: 12: 21Z
  5. Bu yazının "Kapanışlar Büyü Değil" diyen büyük harflerle başlamasından ve ilk örneğini "Sihirli olan JavaScript'te de işlev referansı olarak bitirmekten hoşlanırım ".
    'da yaratıldığı kapanış için gizli bir referans var.
    2014-09-25 02: 30: 17Z

function anahtar sözcüğünü başka bir işlev içinde gördüğünüzde, iç işlev dış işlevdeki değişkenlere erişebilir.

 
function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Bu her zaman 16'yı kaydeder, çünkü bar, x'a argüman olarak tanımlanan foo'a erişebilir ve tmp'dan foo'a da erişebilir.

Bu is bir kapanış. Bir işlevin kapatılması için return olması gerekmez. Kısa zaman önce sözlü sözcük kapsamınız dışındaki değişkenlere erişmek bir kapatma oluşturur .

 
function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

Yukarıdaki işlev 16'yı da kaydeder, çünkü bar, doğrudan kapsam içinde olmasa da, x ve tmp'a başvurabilir.

Bununla birlikte, tmp hala bar'un kapanması içinde takılı kaldığından, aynı zamanda artmaktadır. Her bar'u aradığınızda artırılacak.

Bir kapatmanın en basit örneği şudur:

 
var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Bir JavaScript işlevi çağrıldığında, yeni bir yürütme içeriği oluşturulur. İşlev argümanları ve ana nesne ile birlikte, bu yürütme bağlamı ayrıca kendi dışında bildirilen tüm değişkenleri de alır (yukarıdaki örnekte hem 'a' hem de 'b').

Bir listesini döndürerek veya bunları global değişkenlere ayarlayarak birden fazla kapatma işlevi oluşturmak mümkündür. Bunların hepsi aynı x ve aynı tmp’a atıfta bulunacak, kendi kopyalarını üretmeyeceklerdir.

Burada x sayısı gerçek bir sayıdır. JavaScript'teki diğer hazır sözcüklerde olduğu gibi, foo çağrıldığında, x sayısı foo argümanı olarak x'a kopyalanır .

Diğer yandan, JavaScript nesnelerle uğraşırken daima referansları kullanır. Söylerseniz, bir nesneyle foo'u aradınız, döndürdüğü kapanış o orijinal nesneye başvuruda bulunacaktır !

 
function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Beklendiği gibi, bar(10)'a yapılan her çağrı x.memb'u arttıracaktır. Beklenmeyebilecek olan, x'un age değişkeni ile aynı nesneye gönderme yaptığıdır! bar’a yapılan birkaç aramadan sonra, age.memb, 2 olacak! Bu referans, HTML nesnelerle yapılan bellek sızıntılarının temelidir.

    
3907
2019-05-22 12: 58: 05Z
  1. @ feeela: Evet, her JS işlevi bir kapatma oluşturur. Başvuruda bulunmayan değişkenler büyük olasılıkla modern JS motorlarında çöp toplama için uygun hale getirilecektir, ancak bir yürütme bağlamı oluşturduğunuzda, bu bağlamın etrafındaki yürütme bağlamına ve değişkenlerine bir referansı olduğu gerçeğini değiştirmez. bu işlev, orijinal referansı koruyarak farklı bir değişken kapsamına yerleştirilme potansiyeline sahip bir nesnedir. Bu kapanış.
    2013-08-19 01: 31: 25Z
  2. @ Ali Daha önce verdiğim jsFiddle'ın delete'un başarısızlığından beri hiçbir şey kanıtlamadığını keşfettim. Bununla birlikte, işlevin [[Kapsam]] olarak taşıyacağı sözcüksel ortam (ve sonuçta çağrıldığında kendi sözcüksel ortamı için temel olarak kullanılır), işlevi tanımlayan ifade yürütülürken belirlenir. Bu, işlevinin, gerçekte hangi değerlere başvurduğuna ve kapsamdan çıkıp çıkmadığına bakılmaksızın, yürütme kapsamının ENTIRE içeriğini kapatması anlamına gelir. Lütfen spec
    'de 13.2 ve 10 numaralı bölümlere bakın.
    2013-08-20 17: 51: 46Z
  3. İlkel türler ve referansları açıklamaya çalışana kadar bu iyi bir cevaptı. Bunu tamamen yanlış anlıyor ve gerçekleri kopyalamaktan bahsediyor, ki bunun hiçbir şeyle ilgisi yok.
    2014-07-04 14: 53: 35Z
  4. Kapanışlar, JavaScript'in sınıf tabanlı, nesne yönelimli programlamaya cevabıdır. JS sınıf tabanlı değildir, bu yüzden başka türlü uygulanamayan bazı şeyleri uygulamak için başka bir yol bulmak zorunda kaldı.
    2014-09-18 10: 45: 56Z
  5. bu kabul edilen cevap olmalıdır. Sihir iç fonksiyonda asla gerçekleşmez. Dış işlevi bir değişkene atadığınızda olur. Bu, iç işlev için yeni bir yürütme bağlamı yaratır, böylece "özel değişken" biriktirilebilir. Elbette, dış fonksiyonun atadığı değişken, bağlamı koruduğundan beri yapabilir. İlk cevap, orada gerçekte ne olduğunu açıklamadan, her şeyi daha karmaşık hale getiriyor.
    2016-08-18 00: 26: 28Z

ÖNSÖZ: bu cevap, soru olduğu zaman yazılmıştır:

  

Eski Albert’in dediği gibi: “Bunu altı yaşındaki bir çocuğa açıklayamazsanız, gerçekten kendiniz anlamıyorsunuz.”. JS’yi 27 yaşındaki bir arkadaşa kapatmayı tamamen açıklamaya çalıştım. başarısız oldu.

     

Herkes consi yapabilir6 yaşımda olduğum için ve garip bir şekilde bu konuyla ilgileniyor muyum?

Kelimenin tam anlamıyla ilk soruyu sormaya çalışan tek insanlardan biri olduğuma eminim. O zamandan beri, soru birkaç kez değişti, bu yüzden cevabım şimdi inanılmaz derecede aptalca görünebilir. yerinde değil. Umarım hikayenin genel fikri bazıları için eğlenceli kalır.


Zor kavramları açıklarken büyük bir analoji ve metafor hayranıyım, bu yüzden elimi bir hikaye ile deneyeyim.

Bir zamanlar:

Bir prenses vardı ...

 
function princess() {

Macera dolu harika bir dünyada yaşadı. Prens Büyüleyicisiyle tanıştı, tek boynuzlu atı, dövülmüş ejderhalar, konuşan hayvanlar ve diğer birçok fantastik şeyle dünyasında dolaştı.

 
    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Ama her zaman sıkıcı ev işleri ve yetişkin dünyasına geri dönmek zorunda kalacaktı.

 
    return {

Ve onlara sık sık en son inanılmaz maceralarını bir prenses olarak anlatırdı.

 
        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Ama tek görecekleri küçük bir kız ...

 
var littleGirl = princess();

... sihir ve fantaziyle ilgili hikayeler anlatıyor.

 
littleGirl.story();

Ve yetişkinler gerçek prensesleri bilseler de, tekboynuzlara veya ejderhalara asla inanmazlar, çünkü onları asla göremezlerdi. Yetişkinler, yalnızca küçük kızın hayal gücünün içinde var olduklarını söyledi.

Ama gerçek gerçeği biliyoruz; İçinde prenses olan küçük kız ...

... gerçekten içinde küçük bir kızı olan bir prenses.

    
2348
2017-11-01 11: 40: 09Z
  1. Bu açıklamayı gerçekten seviyorum. Bunu okuyup takip etmeyenler için analoji şudur: princess () işlevi özel veriler içeren karmaşık bir kapsamdır. İşlev dışında, özel verilere erişilemez veya erişilemez. Prenses, tek boynuzlu atları, ejderhaları, maceraları vs. hayal gücünde tutar (özel veriler) ve yetişkinler onları kendileri için göremez. Ancak, prensesin hayal gücü, story() örneğinin sihir dünyasında ortaya çıkardığı tek arayüz olan littleGirl işlevi için kapatıldı.
    2013-02-28 07: 49: 20Z
  2. Yani burada story kapatma, ancak kodun var story = function() {}; return story; olması durumunda, littleGirl kapatma olur. En azından bu, MDN’nin 'özel' kullanımından aldığım izlenimi bu 'kapanmalı yöntemler : "Bu üç ortak işlev, aynı ortamı paylaşan kapaklardır."
    2016-02-23 00: 58: 37Z
  3. @ icc97, evet, story, princess kapsamında sağlanan çevreye referans veren bir kapatmadır. princess aynı zamanda bir başka ima edildi kapatmasıdır, yani princess ve littleGirl, parents'un bulunduğu ve littleGirl'un tanımlandığı ortamda /kapsamda geri kalan bir princess dizisine yapılan herhangi bir referansı paylaşır. >
    2016-03-01 16: 00: 04Z
  4. @ BenjaminKrupp princess bünyesinde, yazıldığından daha fazla işlem olduğunu göstermek /ima etmek için açık bir kod yorumu ekledim. Ne yazık ki bu hikaye şimdi bu konu üzerinde biraz yerinde. Asıl soru, "JavaScript'in 5 yaşından eskilere açıklanması"; Cevabım, bunu yapmaya çalışan tek kişiydi. Sefil bir şekilde başarısız olacağından şüphem yok, ama en azından bu yanıtın 5 yaşına kadar ilgilenme şansı olmuş olabilir.
    2017-09-11 18: 45: 36Z
  5. Aslında, bana göre bu mükemmel bir anlam ifade ediyordu. İtiraf etmeliyim ki, sonunda prenseslerin ve maceraların masallarını kullanarak bir JS kapanışını anlamak beni biraz garip hissettiriyor.
    2017-10-02 10: 03: 34Z

Soruyu ciddiye alarak, 6 yaşındaki tipik bir çocuğun bilişsel olarak neler yapabileceğini öğrenmeliyiz, ancak kuşkusuz, JavaScript ile ilgilenen biri çok tipik değildir.

Çocukluk Gelişimi:5 ila 7 Yıl yazıyor:

  

Çocuğunuz iki adımlı talimatları izleyebilecek. Örneğin, çocuğunuza “Mutfağa git ve bana bir çöp torbası getirin” derseniz, bu yönü hatırlayabilecekler.

Bu örneği, kapanışları aşağıdaki gibi açıklamak için kullanabiliriz:

  

Mutfak, trashBags olarak adlandırılan yerel bir değişkene sahip bir kapatmadır. Mutfağın içinde getTrashBag adında bir çöp torbası alıp geri gönderen bir işlev var.

Bunu JavaScript’te şunun gibi kodlayabiliriz:

 
function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

Kapakların neden ilginç olduğunu açıklayan diğer noktalar:

  • makeKitchen() her çağrıldığında, kendi ayrı trashBags'uyla yeni bir kapatma oluşturulur.
  • trashBags değişkeni, her bir mutfağın içinde yereldir ve dışarıdan erişilemez, ancak getTrashBag özelliğindeki iç işlev ona erişime sahiptir.
  • Her işlev çağrısı bir kapatma oluşturur, ancak kapağın iç kısmına erişimi olan bir iç işlev, kapağın dışından çağrılmadığı sürece, kapağı kapatmaya gerek kalmaz. getTrashBag işleviyle nesneyi döndürmek burada bunu yapar.
722
2018-10-10 17: 50: 14Z
  1. Aslında, kafa karıştıran, makeKitchen işlevi call , döndürdüğü mutfak nesnesi değil gerçek kapatmadır.
    2016-06-27 17: 56: 00Z
  2. Diğerleri arasında yolumu bulduğumda, bu cevabı closures.is'in ne ve neden olduğunu açıklamanın en kolay yolu olarak buldum.
    2016-08-12 15: 12: 01Z
  3. Çok fazla menü ve iştah açıcı, yeterince et ve patates yok. Bu cevabı aşağıdaki gibi kısa bir cümleyle geliştirebilirsiniz: "Kapatma, bir sınıfın sağladığı herhangi bir kapsam belirleme mekanizmasının bulunmaması nedeniyle, işlevin mühürlü bağlamıdır."
    2017-05-13 16: 30: 28Z

Saman Adam

Bir düğmenin kaç kez tıklandığını bilmem ve her üç tıklamada bir şey yapmam gerekiyor ...

Oldukça Açık Çözüm>

 
// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Şimdi bu işe yarayacak, ancak tek amacı sayımı takip etmek olan bir değişken ekleyerek dış kapsama alanını içine çekiyor. Bazı durumlarda, dış uygulamanızın bu bilgilere erişmesi gerekebileceğinden bu tercih edilebilir. Ancak bu durumda, yalnızca her üç tıklamanın davranışını değiştiriyoruz, bu nedenle bu işlevselliği etkinlik işleyicisine dahil etmek tercih edilir.

Bu seçeneği değerlendirin

 
var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Burada bir kaç şey farkedin.

Yukarıdaki örnekte, JavaScript’in kapanma davranışını kullanıyorum. Bu davranış, herhangi bir işlevin, oluşturulduğu kapsama süresiz olarak erişebilmesine izin verir. Bunu pratikte uygulamak için hemen başka bir işlevi döndüren bir işlevi çağırıyorum ve döndürdüğüm işlevi olduğu için dahili sayım değişkenine erişim (yukarıda açıklanan kapatma davranışı nedeniyle), sonuçta ortaya çıkan işlev tarafından kullanılmak üzere özel bir kapsam ortaya çıkar ... Bu kadar basit değil mi? Hadi sulandıralım ...

Tek satırlı basit bir kapatma

 
//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Döndürülen işlev dışındaki tüm değişkenler döndürülen işlev için kullanılabilir, ancak bunlar döndürülen işlev nesnesi için doğrudan kullanılamazlar ...

 
func();  // Alerts "val"
func.a;  // Undefined

Anladın mı? Bu nedenle, birincil örneğimizde, sayım değişkeni kapanış içinde bulunur ve olay işleyicisine her zaman erişilebilirdir, bu nedenle durumunu tıklatmaktan korur.

Ayrıca, bu özel değişken durumuna hem okuma hem de özel kapsam değişkenlerine atama için tamamen erişilebilir.

İşte gidiyorsunuz; şimdi bu davranışı tamamen kapsıyorsunuz.

Tam Blog Yazısı (jQuery dahil) hususlar)

    
560
2017-11-13 04: 54: 05Z
  1. Bir kapatmanın ne olduğu ile ilgili tanımınıza katılmıyorum. Kendini çağırması için hiçbir sebep yok. Aynı zamanda biraz basit (ve yanlış) "iade edilmek" zorunda olduğunu söylemek (bu soruya verilen cevabın yorumlarında bu konuda çok fazla tartışma)
    2013-02-26 19: 51: 22Z
  2. @ James katılmasanız bile, onun örneği (ve yazının tamamı) gördüğüm en iyilerden biri. Soru eski değil ve benim için çözülmemiş olsa da, tamamen bir + 1'i hak ediyor.
    2013-02-27 11: 20: 09Z
  3. "Bir düğmenin kaç kez tıklandığını bilmem ve her üç tıklatmada da bir şeyler yapmam gerekiyor ..." Dikkat. Bir kullanım durumu ve kapatmanın böyle gizemli bir şey olmadığını ve bizim için birçoğunun bunları yazdığını ancak resmi adını tam olarak bilmediğini gösteren bir çözüm.
    2014-01-10 13: 49: 37Z
  4. Güzel bir örnek çünkü 2. örnekteki "count" in "count" değerini "sakladığını" ve "element" her tıklandığında 0 olarak sıfırlanmadığını gösterir. Çok bilgilendirici!
    2014-07-21 06: 19: 54Z
  5. + 1. Javascript'te kapatma davranışını işlevlerle sınırlayabilir miyiz veya bu kavram dilin diğer yapılarına da uygulanabilir mi?
    2015-03-08 19: 32: 34Z

Kapakları açıklamak zordur, çünkü herkesin sezgisel olarak çalışmayı beklediği bazı davranışları yürütmek için kullanılırlar. Onları açıklamanın en iyi yolunu buluyorum (ve ne yaptıklarını öğrendim) durumun onlarsız olduğunu hayal etmektir:

 
    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

JavaScript Kapanışları bilmezse, burada ne olur? Son satırdaki çağrıyı yalnızca kendi yöntem gövdesiyle değiştirin (temelde hangi işlevlerin yaptığıdır) ve şunu elde edin:

 
console.log(x + 3);

Şimdi, x'un tanımı nerede? Mevcut kapsamda tanımlamadık. Tek çözüm plus5'un kapsamını (veya bunun yerine ebeveyninin kapsamını) etrafına taşımasına izin vermektir. Bu şekilde, x iyi tanımlanır ve 5 değerine bağlanır.

    
472
2017-03-17 08: 51: 15Z
  1. Kabul ediyorum. Fonksiyonlara geleneksel isimler yerine anlamlı isimler vermek de bana çok yardımcı oluyor. Anlambilim önemlidir.
    2010-04-08 14: 16: 40Z
  2. öyleyse sahte bir dilde, temelde alert(x+3, where x = 5)'a benzer. where x = 5 kapatmadır. Haklı mıyım?
    2010-12-22 09: 52: 33Z
  3. @ Jus12: aynen. Sahnelerin arkasında kapatma, geçerli değişken değerlerinin ("ciltlemeler") örneğinizde olduğu gibi depolandığı alandır.
    2010-12-22 11: 28: 09Z
  4. Bu, tam olarak birçok kişiyi, döndürülen işlevde kullanılan değerler olduğunu düşünerek yanlış yönlendiren bir örnektir. değişken değişken kendisi. Eğer "return x + = y" olarak değiştirildiyse, ya da daha iyisi "x * = y" işlevini değiştirdiyse, hiçbir şeyin kopyalanmadığı açık olacaktır. Çerçeveleri yığınlamak için kullanılan insanlar için, işlev döndükten sonra da var olmaya devam edebilen yığın çerçeveleri kullandığınızı hayal edin.
    2013-06-21 12: 36: 49Z
  5. @ Matt Buna katılmıyorum. Bir örnek, exi için değil 'dir.tüm özellikleri düzenli bir şekilde belgelemek. İndirgeyici olması ve bir konseptin belirgin özelliğini göstermek içindir. OP basit bir açıklama istedi (“altı yaşındakiler için”). Kabul edilen cevabı alın: Tamamen başarısız olur , tam olarak ayrıntılı olmaya çalıştığı için kısa bir açıklama sunar. (Bağlamanın değere göre referans olarak kullanılmasının JavaScript’in önemli bir özelliği olduğuna katılıyorum… ancak yine de başarılı bir açıklama, minimum seviyeye indirgeyen bir açıklamadır.)
    2013-06-21 14: 30: 42Z

Bu, diğer cevapların bazılarında görünen kapanışlarla ilgili birkaç (olası) yanlış anlaşılmayı gidermeye yönelik bir girişimdir.

  • Bir kapatma yalnızca bir iç işlev döndürdüğünüzde yaratılmaz. Aslında, kapatma işlevinin oluşturulması için çevreleme işlevinin hiç geri dönmesi gerekmez . Bunun yerine, iç işlevinizi bir dış kapsamdaki bir değişkene atayabilir veya onu hemen veya herhangi bir zaman sonra çağrılabilecek başka bir işleve argüman olarak iletebilirsiniz. Bu nedenle, kuşatma işlevinin kapatılması, kuşatma işlevinin çağrıldığı andan itibaren yaratılır, çünkü iç işlev çağrıldığında, çevreleme işlevinin geri dönmesinden önce veya sonra, herhangi bir iç işlev bu kapağa erişebilir.
  • Bir kapatma, kapsamındaki değişkenlerin eski değerlerinin bir kopyasına başvuruda bulunmaz. Değişkenlerin kendileri kapanmanın bir parçasıdır ve bu nedenle bunlardan birine erişirken görülen değer değişkenler, erişildiği zamanki en son değerdir. Bu, döngülerin içinde oluşturulan içsel fonksiyonların zorlu olmasının nedeni, her birinin, işlevin yaratıldığı veya çağrıldığı sırada değişkenlerin bir kopyasını almak yerine aynı dış değişkenlere erişebilmesidir.
  • Bir kapanıştaki "değişkenler", işlev içinde bildirilen adlandırılmış işlevleri içerir . Ayrıca, fonksiyonun argümanlarını da içerirler. Bir kapatma, aynı zamanda, global kapsamın sonuna kadar, içerdiği kapanış değişkenlerine de erişebilir.
  • Kapanışlar belleği kullanır, ancak bellek sızıntılarına neden olmaz çünkü JavaScript kendiliğinden yapılmayan kendi dairesel yapılarını temizler. Kapaklarla ilgili Internet Explorer bellek sızıntıları, kapanmalara başvuran DOM özniteliği değerlerinin bağlantısını kesemediğinde ve bu nedenle de olası dairesel yapılara yapılan referansları koruduğunda oluşturulur.
359
2016-05-05 15: 00: 49Z
  1. Bu arada, bu soruyu doğrudan asıl soruyu doğrudan ele almamaya yönelik açıklamalar ile ekledim. Bunun yerine, herhangi bir basit cevabın (6 yaşındakiler için) bu karmaşık konu hakkında yanlış düşünceler getirmediğini umuyorum. Örneğin. yukarıdaki popüler wiki-cevap "İç işlevini geri döndürdüğünüzde bir kapanış olduğunu" söylüyor. Dilbilgisi yanlışı olmasının yanı sıra, bu teknik olarak yanlıştır.
    2011-07-21 14: 15: 29Z
  2. James, kapatma uygulamasının çağrı sırasında "büyük olasılıkla" olduğunu söyledim, çünkü bir uygulamanın kapatma oluşturma işlemini erteleyebileceği makul bir süre sonra, kapanmaya karar verdiğinde kesinlikle gerekli. Muhafaza fonksiyonunda tanımlanmış bir iç fonksiyon yoksa, kapatmaya gerek kalmaz. Bu yüzden, belki de ilk içsel işlev yaratılana kadar bekleyip sonra çevreleyen işlevinin çağrı bağlamında bir kapanma yaratabilir.
    2012-07-10 17: 27: 03Z
  3. @ Beetroot-Pancar Dış işlev dönmeden önce kullanıldığı , başka bir işleve aktarılan bir iç işlevimiz olduğunu varsayalım ve varsayalım aynı işlevi dış işlevden de döndürürüz. Her iki durumda da aynı fonksiyondur, ancak dış fonksiyon geri dönmeden önce, iç fonksiyonun çağrı yığınına "bağlı" olduğunu söylersiniz, oysa geri döndükten sonra iç fonksiyon aniden bir kapanmaya bağlanır. Her iki durumda da aynı şekilde davranır; anlambilim aynıdır, bu yüzden sadece uygulama detaylarından bahsetmiyor musunuz?
    2012-10-16 16: 06: 13Z
  4. @ Beetroot-Beetroot, geri bildiriminiz için teşekkür ederiz ve sizi düşündüğüme sevindim. Dış fonksiyonun canlı içeriği ile fonksiyonun geri dönmesiyle kapandığı zaman aynı bağlam arasında hala anlamsal bir fark görmüyorum (tanımınızı anlıyorsam). İç fonksiyon umursamıyor. Çöp toplama, iç fonksiyonun her iki şekilde de bağlam /kapanışa bir referans sağlaması nedeniyle önemsemez ve dış fonksiyonun arayanı, sadece çağrı bağlamına referansını bırakır. Ancak bu, insanlara kafa karıştırıcıdır ve belki de sadece çağrı bağlamı olarak adlandırmak daha iyidir.
    2012-10-18 00: 26: 54Z
  5. Bu makalenin okunması zor, ancak aslında söylediklerimi desteklediğini düşünüyorum. Diyor ki: "Bir fonksiyon nesnesinin [...] döndürülmesi veya doğrudan böyle bir fonksiyon nesnesine, örneğin global bir değişkene referans verilmesiyle bir kapatma oluşuyor." GC'nin alakasız olduğunu kastetmiyorum. Aksine, GC nedeniyle ve iç işlev dış fonksiyonun çağrı bağlamına (veya makalenin yazdığı gibi [[kapsam]]) bağlı olduğundan, dış işlev çağrısının iç kısımla bağlandığı için geri dönüp dönmemesi önemli değildir. fonksiyon önemli bir şeydir.
    2012-10-21 01: 49: 59Z

Tamam, 6 yaşında bir kapanış hayranı. En basit kapatma örneğini duymak ister misiniz?

Sıradaki durumu düşünelim: bir arabada bir sürücü oturuyor. O araba bir uçağın içinde. Uçak havaalanında. Sürücünün arabası dışındaki, ancak uçağın içindeki nesnelere, o uçak havaalanından ayrılsa bile, erişebilme yeteneği kapanıyor. Bu kadar. 27 yaşını doldurduğunuzda daha ayrıntılı açıklamaya veya aşağıdaki örneğe bakın.

İşte uçak hikayemi koduma nasıl dönüştürebileceğimi aşağıda bulabilirsiniz.

 
var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");
    
357
2018-10-10 18: 38: 01Z
  1. Orijinal posteri iyi oynadı ve cevapladı. Bence bu en iyi cevap. Ben de bagajları benzer şekilde kullanacaktım: büyükannenin evine gittiğinizi ve nintendo DS çantanızı kasanızın içinde oyun kartlarıyla doldurduğunuzu hayal edin, fakat sonra çantanızın çantasını içine koyun ve ayrıca oyun kartlarını sırt çantanıza yerleştirin ve Daha sonra her şeyi daha fazla oyun kartına sahip büyük bir çantaya koyuyorsun. Büyükannenin evine vardığınızda, tüm dış davalar açık olduğu sürece DS'nizdeki herhangi bir oyunu oynayabilirsiniz. ya da bu etkiye bir şey.
    2013-09-19 00: 37: 32Z

Bir kapatılması bir nesneye çok benzer. Ne zaman bir işlev çağırırsanız başlatılıyor.

JavaScript’teki bir kapatmanın kapsamı sözlükseldir; bu, kapatmanın işlevi içinde bulunan her şeyin, içinde bulunan herhangi bir değişkene erişimi olduğu anlamına gelir. o.

Eğer

, kapanışında bir değişken var
  1. var foo=1; ile atayın veya
  2. sadece var foo; yazın

Bir iç işlev (başka bir işlevin içinde yer alan bir işlev), böyle bir değişkene kendi kapsamı içinde var ile tanımlamadan erişirse, değişkenin içeriğini dış kapanışındaki değiştirir. p>

Bir kapatma , onu oluşturan işlevin çalışma süresini uzatır. Diğer işlevler tanımlandıkları kapatma /kapsamı 'dan çıkarsa (örneğin, iade değerleri olarak), bunlar kapatmaya başvurmaya devam edeceklerdir.

Örnek

 
function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

Çıktı

 
somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
    
352
2018-10-10 18: 39: 20Z
  1. Vay, console.log'daki dize değişimlerini böyle kullanabileceğinizi asla bilmiyordum. Orada başka biri ilgileniyorsadaha fazlası: developer.mozilla.org/en-US/docs/DOM /...
    2013-01-04 02: 59: 32Z
  2. İşlevin parametre listesinde bulunan değişkenler de kapanmanın bir parçasıdır (örneğin, var ile sınırlı değildir).
    2015-03-18 03: 38: 09Z
  3. Kapanışlar, nesneler ve sınıflar vb. gibi daha fazla ses çıkarır. Neden birçok insanın bu ikisini karşılaştırmamasından emin değiliz - acemilerimizin öğrenmesi daha kolay olur!
    2019-05-23 12: 37: 10Z

Bir süre önce kapanışları açıklayan bir blog yazısı yazdım. İşte size neden istediğinizi neden olarak kapatma konusunda söylediklerim.

  

Kapaklar, bir işleve izin vermenin bir yoludur    kalıcı, özel değişkenler var -   yani, yalnızca bir değişken   fonksiyon nerede olduğunu bilir   önceki zamanlardan gelen bilgileri takip et   çalıştırıldığını.

Bu anlamda, bir işlevin, özel özelliklere sahip bir nesne gibi biraz hareket etmesine izin verir.

Tam yayın:

Öyleyse bu kapatma şeyleri nelerdir?

    
230
2013-01-28 02: 23: 21Z
  1. Öyleyse, kapanışların ana yararı bu örnekle vurgulanabilir mi? Bir fonksiyonum var emailError (sendToAddress, errorString) Daha sonra devError = emailError("devinrhode2@googmail.com", errorString) diyebilirim ve paylaşılan emailError fonksiyonunun kendi özel sürümüne sahip olabilirmiyim?
    2011-07-31 06: 42: 49Z
  2. Çok fazla 'bürünme' yolunda kürekleştikten sonra nihayet ne olduklarını anlamaya başladım. Kendi kendime "ah, bir nesnedeki özel değişkenleri gibi?" Diye düşündüm. ve bam. Bu bir sonraki cevap okudum.
    2012-12-30 21: 20: 07Z
  3. Bu açıklama ve (closeysys kapanış) bağlantısındaki mükemmel bir örnek, kapanışları anlamanın en iyi yoludur ve en tepede olmalı!
    2019-05-05 09: 45: 39Z

Kapaklar basittir:

Aşağıdaki basit örnek, JavaScript kapanışlarının tüm ana noktalarını kapsar. *

İşte ekleyebilen ve artırabilen hesap makineleri üreten bir fabrika:

 
function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

Anahtar nokta: Her bir make_calculator numaralı çağrı, n’dan sonra, bu hesap makinesinin add ve multiply işlevleri tarafından kullanılmaya devam eden make_calculator numaralı yeni bir yerel değişken oluşturur.

Yığın çerçevelerine aşina iseniz, bu hesap makineleri garip görünüyor: n'dan sonra make_calculator'a erişmeye nasıl devam edebilirler? Cevap, JavaScript’in “yığın çerçevelerini” kullanmadığını, bunun yerine onları döndüren işlev çağrısından sonra da devam edebilen “yığın çerçevelerini” kullandığını hayal etmektir.

** dış işlevinde bildirilen değişkenlere erişen add ve multiply gibi iç işlevlere kapatma denir.

Kapanacak tek şey bu kadar.



* Örneğin, başka bir cevap , yalnızca değişkenlerin bildirilmeden önce kullanılabileceğini gösteren örnek 6 hariç, bilmesi ancak kapanmalarla tamamen alakasız olduğunu bilmek güzel bir gerçek. Ayrıca, argümanlarını yerel değişkenlere kopyalayan noktalar (1) haricinde, kabul edilen yanıt bölümündeki tüm noktaları kapsar. (adlandırılmış işlev argümanları) ve (2) sayıların kopyalanması yeni bir sayı oluşturur, ancak bir nesne referansının kopyalanması, aynı nesneye başka bir başvuru verir. DenizAyrıca bilmek iyi ancak yine tamamen kapanma ile ilgisi yok. Ayrıca, bu cevabı içindeki örneğe çok benziyor, ancak biraz daha kısa ve daha az soyut. bu cevabı veya bu yorum , bu JavaScript'in bir döngü değişkeninin geçerli değerini eklemeyi zorlaştırdığı iç işlevinize: "takma" adımı yalnızca iç işlevinizi çevreleyen ve her döngü yinelemesinde çağrılan bir yardımcı işlevle yapılabilir. (Kesinlikle konuşursak, iç işlev, bir şey takılı olmak yerine, yardımcı işlevinin değişkenin kopyasına erişir.) Yine, kapaklar oluştururken çok kullanışlıdır, ancak kapamanın ne olduğu ya da nasıl çalıştığının bir parçası değildir. Değişkenlerin depolama alanı yerine değerlere bağlı olduğu ML gibi işlevsel dillerde farklı çalışan kapaklar nedeniyle ek bir kafa karışıklığı vardır, bu da bir şekilde kapama yöntemlerini (yani "takma" şeklinde) anlayan sabit bir insan akışı sağlar. Değişkenlerin her zaman depolama alanına ve asla değerlere bağlanmadığı JavaScript için yalnızca yanlış.

** Birkaç iç içe geçmişse veya genel bağlamda bile olsa, herhangi bir dış işlev, bu cevap açıkça işaret ediyor.

    
210
2017-05-23 12: 10: 46Z
  1. Eğer aranırsanız ne olur: second_calculator = first_calculator (); second_calculator = make_calculator (); ? Aynı olmalı, değil mi?
    2016-10-07 05: 02: 17Z
  2. @ Ronen: first_calculator bir nesne (işlev değil) olduğundan, second_calculator = first_calculator;'da parantez kullanmamalısınız, çünkü işlev çağrısı değil atamadır. Sorunuzu cevaplamak için daha sonra make_calculator'e yalnızca bir çağrı olacaktı, bu yüzden sadece bir hesap makinesi yapılabilecekti ve first_calculator ve second_calculator değişkenleri aynı hesap makinesine atıfta bulunacaktı, böylece cevaplar 3, 403, 4433, 44330 olacaktır.
    2016-10-10 09: 49: 48Z

Bunu altı yaşındaki bir çocuğa nasıl açıklarım:

Yetişkinlerin nasıl bir eve sahip olabileceklerini biliyorsunuz, ve onu eve mi çağırıyorlar? Bir annenin çocuğu olduğunda, çocuk gerçekten hiçbir şeye sahip değildir, değil mi? Fakat ebeveynlerinin bir evi var, bu yüzden birisi çocuğa “Eviniz nerede?” Diye sorduğunda, o eve! ”Diye cevap verebilir ve ebeveynlerinin evine işaret edebilir. Bir "Kapanış", çocuğun her zaman (yurtdışında olsa bile) bir evi olduğunu söyleyebilmesidir, gerçekten evin sahibi olan ebeveyn olsa bile.

    
203
2016-01-16 02: 30: 44Z

Kapatmayı açıklayabilir misiniz? 5 yaşında bir çocuk mu? *

Hala Google’ın açıklamasının çok iyi çalıştığını düşünüyorum. özlü:

 
/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Bu örneğin, iç işlev dönmese bile bir kapanma oluşturduğunun kanıtı

* Bir C # sorusu

    
195
2017-05-23 11: 47: 32Z
  1. Açıklamayı okursanız, örneğinizin doğru olmadığını görürsünüz. İnnerFunction çağrısı dış fonksiyonun kapsamı içindedir ve açıklamanın dediği gibi dış fonksiyon geri döndükten sonra değildir. OuterFunction işlevini her çağırdığınızda, yeni bir innerFunction oluşturulur ve sonra kapsamda kullanılır.
    2010-12-06 16: 11: 06Z
  2. @ Moss benim yorumum değil, onlar bir Google geliştiricisiydi
    2010-12-06 23: 09: 14Z
  3. innerFunction işlevinin outerFunction kapsamı dışında bulunmadığını görmek, tercümanın kapatılması gerekmeyecek kadar akıllı mıdır?
    2011-03-07 05: 49: 31Z
  4. Kod, bir dışına dönme işleminden sonra kapatmanın kullanılmasıyla ilgili yorum bölümünü içermese bile, bir kapatma örneği olarak "doğru" dur. Yani bu harika bir örnek değil. İnnerFunction işlevini iade etmeyi gerektirmeyen bir kapak kullanmanın birçok yolu vardır. Örneğin. innerFunction hemen çağrıldığı veya saklanıp bir süre sonra çağrıldığı başka bir fonksiyona geçirilebilir ve her durumda, çağrıldığında oluşturulan outerFunction bağlamına erişebilir.
    2011-08-04 14: 01: 11Z
  5. @ syockit Hayır, Moss yanlıştır. İşlevin tanımlandığı kapsamdan hiç çıkıp çıkmadığına dair ne olursa olsun bir kapatma oluşturulur ve koşulsuz olarak oluşturulmuş bir başvuru, ebeveynin sözlü ortamına bakılmaksızın, ana kapsamdaki tüm değişkenleri tüm işlevler için kullanılabilir kılar oluşturuldukları kapsamın dışında mı yoksa içinde mi çağrıldıkları.
    2013-08-21 13: 41: 18Z

İYİ /KÖTÜ karşılaştırmalarıyla daha iyi öğrenme eğilimindeyim. Çalışma kodunu izlemekten hoşlanır, ardından çalışmayan bir kodla karşılaşırsınız. Bir karşılaştırma yapan ve bulabildiğim en basit açıklamalar arasındaki farkları azaltmaya çalışan jsFiddle 'ı bir araya getirdim ile.

Kapaklar doğru yapıldı:

 
console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • Yukarıdaki kodda createClosure(n), döngünün her yinelemesinde çağrılır. Unutmayın, n değişkenini, yeni bir işlev kapsamında oluşturulan yeni bir değişken olduğunu ve dış etki alanına bağlı olan index ile aynı değişken olmadığını vurgulamak için adlandırdığımı unutmayın.

  • Bu yeni bir kapsam yaratır ve n bu kapsama bağlıdır; bu, her yineleme için bir tane olmak üzere 10 ayrı kapsamımız olduğu anlamına gelir.

  • createClosure(n), n'yi bu kapsam dahilinde döndüren bir işlev döndürür.

  • Her kapsamda n, createClosure(n) çağrıldığında sahip olduğu değere bağlı olduğundan, döndürülen yuvalanmış işlev her zaman n çağrıldığında sahip olduğu createClosure(n) değerini döndürür.

Kapaklar yanlış yapıldı:

 
console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • Yukarıdaki kodda döngü createClosureArray() işlevi içinde taşındı ve işlev artık tamamlanan diziyi döndürdü; bu ilk bakışta daha sezgisel görünüyor.

  • Açık olan şey, createClosureArray()'un yalnızca bir kez çağrılmasından bu yana, döngünün her yinelemesi için bir tane yerine bu işlev için yalnızca bir kapsam oluşturulmuş olmasıdır.

  • Bu işlev içinde index adlı bir değişken tanımlanır. Döngü çalışır ve index döndüren diziye işlevler ekler. index'un, yalnızca bir kez çağrılan createClosureArray işlevi içinde tanımlandığını unutmayın.

  • createClosureArray() işlevi içinde yalnızca bir kapsam olduğundan, index yalnızca bu kapsam içindeki bir değere bağlanır. Başka bir deyişle, döngü index'un değerini her değiştirdiğinde, o kapsamda kendisine başvuran her şey için onu değiştirir.

  • Diziye eklenen tüm işlevler, ilk örnek gibi 10 farklı kapsamdan 10 farklı yerine tanımlandığı ana kapsamdan SAME index değişkenini döndürür. Sonuçta 10 fonksiyonun tümü aynı kapsamdan aynı değişkeni döndürür.

  • Döngü bittikten ve index değiştirildikten sonra son değer 10'du, bu nedenle diziye eklenen her işlev, şimdi 10'a ayarlanan tek index değişkeninin değerini döndürür.

Sonuca

  

DOĞRU YAPILAN DOĞRULAR
  n = 0
  n = 1
  n = 2
  n = 3
  n = 4
  n = 5
  n = 6
  n = 7
  n = 8
  n = 9

     

YANLIŞ YAPILAN AÇIKLAMALAR
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10

    
169
2017-03-27 17: 56: 11Z
  1. Güzel ekleme, teşekkürler. Daha da netleştirmek için, "kötü" dizinin her yinelemede "kötü" döngüde nasıl oluşturulduğunu hayal edebilirsiniz: 1. yineleme: [function () {return 'n =' + 0;}] 2. yineleme: [( function () {return 'n =' + 1;}), (function () {return 'n =' + 1;})] 3. yineleme: [(function () {return 'n =' + 2;}) , (function () {return 'n =' + 2;}), (function () {return 'n =' + 2;})] vb. Dolayısıyla, dizin değeri değiştiğinde, her seferinde tüm işlevlerde yansıtılır. diziye zaten eklenmiş.
    2016-04-08 22: 38: 08Z
  2. let için var kullanmak farkı düzeltir.
    2017-10-16 10: 25: 19Z
  3. Burada değil "Kapatma doğru yapıldı", "kapatma içinde kapatma" örneğidir?
    2017-12-29 10: 17: 01Z
  4. Demek istediğim, her fonksiyon teknik olarak bir kapanış ama önemli olan, fonksiyonun içinde yeni bir değişken tanımlaması. Dönen fonksiyon sadece yeni bir kapamada yaratılan n referansını verir. Sadece bir işlev döndürürüz, onu dizide depolayabilir ve daha sonra çağırabiliriz.
    2017-12-29 18: 52: 58Z
  5. Eğer sonucu ilk iterasyonda dizide saklamak istiyorsanız, şu şekilde satır içi yapabilirsiniz: arr[index] = (function (n) { return 'n = ' + n; })(index);. Ancak, sonuçta ortaya çıkan dizgiyi, örneğimin noktasını yitiren bir işlevi çağırmak için bir işlev yerine saklıyorsunuz.
    2017-12-29 18: 54: 55Z

Kapanışlarda Wikipedia :

  

Bilgisayar bilimlerinde, kapatma, söz konusu işlevin yerel olmayan adları (serbest değişkenler) için başvuruda bulunan bir ortamla birlikte bir işlevdir.

Teknik olarak, JavaScript 'te her işlev bir kapatmadır . Her zaman çevre kapsamında tanımlanan değişkenlere erişebilir.

JavaScript’te kapsamı tanımlayan yapı bir işlevdir , diğer birçok dilde olduğu gibi bir kod bloğu değil, genellikle ne demek istediğimizi JavaScript’te kapatıyoruz bir daha önce çevreleyen fonksiyonda tanımlanmış olan yerel olmayan değişkenlerle çalışan bir fonksiyondur .

Kapaklar, bazı gizli özel verilerle işlevler oluşturmak için kullanılır (ancak her zaman böyle değildir).

 
var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

EMS

Yukarıdaki örnek, bir kez çalıştırılan adsız bir işlev kullanıyor. Ancak olmak zorunda değildir. Her çağrıldığında bir veritabanı işlevi oluşturarak adlandırılabilir (örneğin mkdb) ve daha sonra çalıştırılabilir. Üretilen her işlevin kendi gizli veritabanı nesnesi olacaktır. Kapatmaların bir başka kullanım örneği, bir işlevi döndürmediğimizde, ancak her biri aynı verilere erişen farklı işlevler için birden çok işlev içeren bir nesnedir.

    
161
2013-12-18 16: 48: 34Z
  1. Bu, JavaScript kapatmaları için en iyi açıklamadır. Seçilen cevap olmalı. Gerisi yeterince eğlenceli ancak bu gerçek dünya JavaScript kodlayıcıları için pratik bir şekilde faydalıdır.
    2018-02-04 12: 32: 46Z

Kapakların nasıl çalıştığını açıklamak için etkileşimli bir JavaScript öğreticisi hazırladım. Kapanış Nedir?

İşte örneklerden biri:

 
var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here
    
135
2014-10-25 22: 38: 03Z
  

Çocuklar, ebeveynleri olduktan sonra bile ebeveynleriyle paylaştıkları sırları daima hatırlayacaklar   gitmiş. Bu, fonksiyonlar için kapakların ne olduğudır.

JavaScript işlevlerinin sırları özel değişkenlerdir

 
var parent = function() {
 var name = "Mary"; // secret
}

Her aradığınızda, yerel değişken "name" oluşturulur ve "Mary" adı verilir. Ve işlev her çıktığında değişken kaybolur ve ad unutulur.

Tahmin edebileceğiniz gibi, değişkenler işlev her çağrıldığında yeniden yaratıldığından ve başka hiç kimse bunları bilmeyeceğinden, depolandıkları gizli bir yer olmalıdır. Sırlar Odası veya yığın veya yerel kapsam olarak adlandırılabilir, ancak bunun önemi yoktur. Orada olduklarını, bir yerlerde, bellekte saklandıklarını biliyoruz.

Ancak, JavaScript’te, diğer işlevler içinde oluşturulan işlevlerin ebeveynlerinin yerel değişkenlerini de bildiği ve yaşadıkları sürece koruyabilen çok özel bir şey vardır.

 
var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Ebeveyn işlevindeyken, gizli değişkenleri gizli yerden paylaşan bir veya daha fazla alt işlev oluşturabilir.

Ancak üzücü olan, eğer çocuk ebeveyn işlevinin özel bir değişkeni ise, ebeveyn bitince de ölecek ve sırlar onlarla birlikte ölecektir.

Öyleyse yaşamak için çocuğun çok geç olmadan gitmesi gerekiyor

 
var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

Ve şimdi, Mary “artık koşmuyor” olsa bile, hafızası kaybolmuyor ve çocuğu, birlikte geçirdikleri süre boyunca adını ve diğer sırlarını her zaman hatırlayacak.

Öyleyse, çocuğa "Alice" adını verirseniz cevap verir

 
child("Alice") => "My name is Alice, child of Mary"

Tüm söyleyeceğiniz bu kadar.

    
124
2017-07-13 11: 27: 32Z
  1. Bu, benim için en anlamlı olan açıklamadır çünkü teknik terimlerle ilgili önemli bir ön bilgiyi almaz. Buradaki en çok oy alan açıklama, kapanışları anlamayan bir kişinin, 'sözcüksel kapsam' ve 'yürütme bağlamı' gibi terimlerin tam ve eksiksiz bir anlayışa sahip olduğunu varsaymaktadır - bunları kavramsal olarak anlayabilirim. Olması gerektiği gibi onların ayrıntılarıyla rahat, ve içinde hiç jargon olmayan bir açıklama, kapanışların sonunda benim için tıkladığı şeydir, teşekkür ederim. Bonus olarak, bence hangi kapsamın çok net bir şekilde açıklandığını da açıklıyor.
    2015-05-17 20: 30: 27Z

Yanıtların neden bu kadar karmaşık olduğunu anlamıyorum.

İşte bir kapanış:

 
var a = 42;

function b() { return a; }

Evet. Muhtemelen bunu günde birçok kez kullanıyorsunuz.


  

Kapanışların belirli sorunları ele almak için karmaşık bir tasarım hackü olduğuna inanmak için hiçbir neden yoktur. Hayır, kapanışlar, yalnızca işlevin ilan edildiği (çalıştırılmadığı) açısından daha yüksek kapsamdan gelen bir değişkeni kullanmakla ilgilidir.

     

Şimdi izin verdiği şey daha muhteşem olabilir, diğer cevaplara bakın.

    
102
2015-02-21 23: 48: 56Z
  1. Bu cevabın insanları şaşırtmasına yardımcı görünmüyor. Geleneksel bir programlama dilinde kaba bir eşdeğer, b () 'ı ayrıca özel bir sabite veya a özelliğine sahip olan bir nesne üzerinde bir yöntem olarak oluşturmak olabilir. Benim düşünceme göre sürpriz, JS kapsamı nesnesinin bir sabit yerine a'u etkin bir özellik olarak sağlamasıdır. Ve bu önemli davranışı ancak değiştirirseniz, return a++;’da olduğu gibi fark edersiniz
    2015-05-15 01: 34: 07Z
  2. Aynen Jon'un söylediği gibi. Sonunda kapanışlardan önce pratik örnekler bulmakta zorlandım. Evet, floribon bir kapanış yarattı, ancak beni eğitmediyse, bu bana kesinlikle hiçbir şey öğretmedi.
    2015-10-29 23: 15: 01Z
  3. Bu bir kapatmanın ne olduğunu tanımlamıyor - sadece bir örnekbu bir kullanır. Ve kapsam sona erdiğinde olanların nüansını ele almaz; Tüm kapsamlar hala etraftayken ve özellikle de küresel bir değişken söz konusu olduğunda kimsenin sözcüksel kapsam hakkında bir sorusu olduğunu sanmıyorum.
    2016-08-09 15: 51: 36Z

dlaliberte tarafından ilk nokta için örnek:

  

Bir kapatma sadece bir iç işlev döndürdüğünüzde yaratılmaz. Aslında, kapatma işlevinin geri dönmesi gerekmez. Bunun yerine, iç işlevinizi dış kapsamdaki bir değişkene atayabilir veya onu hemen kullanılabileceği başka bir işleve argüman olarak iletebilirsiniz. Bu nedenle, kapatma işlevinin kapatılması, muhtemelen herhangi bir iç işlev çağrıldığı anda erişebildiğinden, ek işlevinin çağrıldığı anda zaten vardır.

 
var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
    
91
2016-01-16 02: 39: 35Z
  1. FYI: yukarıdakileri gösterir = > 9
    2010-05-19 20: 24: 03Z
  2. Olası bir belirsizlik hakkında küçük açıklamalar. "Aslında, kapatma işlevinin geri dönmesi gerekmez" dediğimde. "Değeri döndür" demek istemedim ama "hala aktif" demiştim. Dolayısıyla, örnek bu yönü göstermez, ancak iç fonksiyonun dış kapsama geçirilebileceği başka bir yol gösterir. Yapmaya çalıştığım asıl nokta, kapatma işleminin zamanı ile ilgilidir (kapatma işlevi için), çünkü bazı insanlar kapatma işlevi döndüğünde olduğunu düşünüyor gibi görünmektedir. Kapatma işleminin denilen .
    olduğunda oluşturulduğunu göstermek için farklı bir örnek gerekir.
    2011-07-21 14: 03: 12Z

Bir kapatma, bir iç fonksiyonun dış fonksiyonundaki değişkenlere erişime sahip olduğu durumdur. Bu muhtemelen kapanışlar için alabileceğiniz en basit tek satırlık açıklamadır.

    
86
2012-12-24 11: 10: 56Z
  1. Açıklamanın sadece yarısı. Kapaklar hakkında dikkat edilmesi gereken önemli şey, eğer iç işlev dış işlevden çıktıktan sonra hala bahsedilirse, dış işlevin eski değerlerinin hala iç işlev için mevcut olmasıdır.
    2008-09-21 22: 29: 13Z
  2. Aslında, iç işlev için kullanılabilen dış işlevin eski değerleri değil, eski değişkenleri , eğer bir işlev onları değiştirebilseydi, yeni değerleri olabilirdi.
    2012-08-16 02: 39: 42Z

Zaten çok sayıda çözüm olduğunu biliyorum, ancak bu küçük ve basit komut dosyasının kavramı göstermek için yararlı olabileceğini düşünüyorum:

 
// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
    
84
2016-05-09 11: 32: 47Z

Uyuya kaldın ve Dan'i davet ettin. Dan'a bir XBox denetleyicisi getirmesini söyle.

Dan, Paul'ü davet eder. Dan, Paul'den bir denetleyici getirmesini istiyor. Partiye kaç denetçi getirildi?

 
function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
    
81
2011-07-20 15: 16: 26Z

JavaScript işlevleri şunlara erişebilir:

  1. değişkenleri
  2. Yereller (yani, yerel değişkenleri ve yerel işlevleri)
  3. İçeren ortam:
    • DOM dahil olmak üzere globals
    • dış işlevlerdeki her şey

Bir işlev ortamına erişiyorsa, işlev bir kapatmadır.

Dış işlevlerin gerekli olmadığına dikkat edin, ancak faydalar sunsalar da burada tartışmıyorum. Ortamındaki verilere erişerek, bir kapatma bu verileri canlı tutar. Dış /iç işlevlerin alt harfinde, bir dış işlev yerel veriler oluşturabilir ve sonunda çıkabilir ve yine de, dış işlev çıktıktan sonra herhangi bir iç işlev kalırsa, iç işlev dış işlevin yerel verilerini koruyabilir canlı.

Küresel ortamı kullanan bir kapatma örneği:

Yığın Taşması Oylama ve Oylama Aşağı düğmesi olaylarının, dış değişkenlere erişimi olan ve global olarak tanımlanmış dış değişkenlere erişimi olan kapamalar, voteUp_click ve voteDown_click olarak gerçekleştirildiğini hayal edin. (Sadelik uğruna, Cevap Oy düğmeleri dizisi dizisinden değil StackOverflow'un Soru Oy düğmelerinden bahsediyorum.)

Kullanıcı Oylama düğmesini tıklattığında voteUp_click işlevi, oy oyu mu yoksa yalnızca aşağı oyu mu iptal etmek için isVotedDown == true olup olmadığını kontrol eder. VoteUp_click, çevreye eriştiği için kapanma işlevidir.

 
var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

Bu işlevlerin dördü de hepsi çevrelerine eriştiği için kapanma işlevidir.

    
77
2016-06-08 22: 16: 22Z

Kapaklar 'ın yazarı, nedenleri açıklayarak kapanışları oldukça iyi açıkladı neden onlara ihtiyacımız var ve ayrıca kapanışları anlamak için gerekli olan LexicalEnvironment'ı açıklıyoruz.
İşte özeti:

Bir değişkene erişilirse, ancak yerel değilse ne olur? Buradaki gibi:

 Buraya resim açıklaması girin

Bu durumda, tercüman değişkeni dış LexicalEnvironment nesnesi.

İşlem iki adımdan oluşur:

  1. İlk önce, bir f fonksiyonu yaratıldığında, boşta yaratılmaz alanı. Geçerli bir LexicalEnvironment nesnesi var. Durumda yukarıda, penceresi (a işlev sırasında tanımsız yaratma).

 Buraya resim açıklaması girin

Bir işlev oluşturulduğunda, geçerli LexicalEnvironment öğesine başvuran [[Kapsam]] adlı gizli bir özellik alır.

 Buraya resim açıklaması girin

Bir değişken okunur, ancak hiçbir yerde bulunamazsa, bir hata oluşturulur.

İç içe işlevler

İşlevler birbirlerinin içine yerleştirilebilir ve bu da kapsam zinciri olarak da adlandırılabilecek bir LexicalEnvironments zinciri oluşturur.

 Buraya resim açıklaması girin

Öyleyse, g işlevi g, a ve f işlevlerine erişebilir.

Kilitler

Dış işlev bittikten sonra iç içe geçmiş bir işlev çalışmaya devam edebilir:

 Buraya resim açıklaması girin

LexicalEnvironments'ı İşaretleme:

 Buraya resim açıklaması girin

Gördüğümüz gibi, this.say, kullanıcı nesnesindeki bir özelliktir, bu nedenle Kullanıcı tamamlandıktan sonra yaşamaya devam eder.

Ve eğer hatırlarsanız, this.say oluşturulduğunda, (her fonksiyonda olduğu gibi) mevcut LexicalEnvironment için this.say.[[Scope]] dahili referans alır. Bu nedenle, geçerli kullanıcı yürütmesinin LexicalEnvironment bellekte kalır. Kullanıcının tüm değişkenleri aynı zamanda mülkleridir, bu nedenle de genellikle olduğu gibi önemsiz olmayan, aynı zamanda dikkatlice tutulurlar.

Bütün mesele, iç fonksiyonun gelecekte bir dış değişkene erişmek istiyorsa, bunu yapabilmesini sağlamaktır.

Özetlemek için:

  1. İç işlev dışa bir referans tutar LexicalEnvironment.
  2. İç işlev ondan değişkenlere erişebilir dış işlev olsa bile her zamanbitti.
  3. Tarayıcı, LexicalEnvironment ürününü ve tüm özelliklerini (değişkenlerini) kendisine referans veren bir iç işlev alana kadar bellekte tutar.

Buna kapatma denir.

    
75
2018-05-14 20: 51: 20Z

6 yaşında bir çocuğun babası olarak, şu anda küçük çocuklara öğretmenlik yapıyor (ve örgün bir eğitim almamış kodlama için göreceli bir acemi, bu yüzden düzeltmeler gerekli olacak), dersin uygulamalı oyunda en iyi şekilde yapışacağını düşünüyorum. 6 yaşındaki bir kapanmanın ne olduğunu anlamaya hazırsa, o zaman kendileri gitmek için yeterince yaşlılar. Kodu jsfiddle.net'e yapıştırmanızı, biraz açıklamanızı ve benzersiz bir şarkı söyleyebilmek için onları rahat bırakmanızı öneririm. Aşağıdaki açıklayıcı metin muhtemelen 10 yaşında bir çocuk için daha uygundur.

 
function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

TALİMATLAR

VERİ: Veriler bir gerçekler topluluğudur. Sayılar, kelimeler, ölçümler, gözlemler veya hatta şeylerin sadece açıklamaları olabilir. Dokunamaz, koklayamaz veya tadına bakamazsınız. Yazabilir, konuşabilir ve duyabilirsiniz. Bir bilgisayarı kullanarak oluşturun dokunma kokusuna ve tadına bakmak için kullanabilirsiniz. Kod kullanan bir bilgisayar tarafından kullanılabilir.

KOD: Yukarıdaki tüm yazılara kod denir. JavaScript ile yazılmıştır.

JAVASCRIPT: JavaScript bir dildir. İngilizce ya da Fransızca ya da Çince gibi dillerdir. Bilgisayarlar ve diğer elektronik işlemciler tarafından anlaşılan birçok dil var. JavaScript'in bir bilgisayar tarafından anlaşılması için bir tercümana ihtiyacı vardır. Sadece Rusça bilen bir öğretmenin okuldaki sınıfınızı öğretmeye geldiğini hayal edin. Öğretmen "все садятся" dediğinde, sınıf anlamazdı. Ama neyse ki, sınıfınızda herkese bunun "herkesin oturması" anlamına geldiğini söyleyen bir Rus öğrenciniz var - yani hepiniz varsınız. Sınıf bir bilgisayar gibidir ve Rus öğrenci tercümandır. JavaScript için en yaygın tercümana tarayıcı denir.

BROWSER: Bir web sitesini ziyaret etmek için bir bilgisayara, tablete veya telefondan İnternete bağlandığınızda, bir tarayıcı kullanırsınız. Bilebileceğiniz örnekler Internet Explorer, Chrome, Firefox ve Safari'dir. Tarayıcı JavaScript'i anlayabilir ve bilgisayara ne yapması gerektiğini söyleyebilir. JavaScript talimatlarına fonksiyon denir.

FONKSİYON: JavaScript'teki bir işlev fabrika gibidir. İçinde sadece bir makine bulunan küçük bir fabrika olabilir. Veya her biri farklı işlerde çalışan birçok makineye sahip başka birçok küçük fabrika içerebilir. Gerçek hayattaki bir kıyafet fabrikasında, kumaş parçalarını ve iplik bobinlerini ve tişörtleri ve kot pantolonlarını çıkartabilirsiniz. JavaScript fabrikamız yalnızca verileri işler, dikemez, bir delik açamaz veya metal eritebilir. JavaScript fabrikamızda veriler giriyor ve veriler çıkıyor.

Tüm bu veri işleri biraz sıkıcı geliyor, ama gerçekten çok havalı; bir robota akşam yemeğinde ne yapmamız gerektiğini söyleyen bir işleve sahip olabiliriz. Diyelim ki sizi ve arkadaşınızı evime davet ediyorum. Tavuk budu en çok seversin, sosisleri severim, arkadaşın her zaman istediğini ister ve arkadaşım et yemez.

Alışverişe çıkmaya vaktim olmadığından, fonksiyonun karar almak için buzdolabında ne olduğunu bilmemiz gerekiyor. Her bir malzemenin farklı bir pişirme süresi vardır ve her şeyin aynı anda robot tarafından sıcak sunulmasını istiyoruz. Fonksiyona ne istediğimizi gösteren veriler sağlamalıyız, fonksiyon buzdolabında 'konuşabilir' ve fonksiyon robotu kontrol edebilir.

Bir işlevin normalde bir adı, parantez ve parantezleri vardır. Bunun gibi:

 
function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

/*...*/ ve //'un durdurma kodunun tarayıcı tarafından okunmakta olduğunu unutmayın.

ADI: İstediğiniz sözcükle ilgili bir işlevi çağırabilirsiniz. "CookMeal" örneği, iki kelimeyi bir araya getirme ve ikincisine başında büyük harf verme konusunda tipiktir - ancak bu gerekli değildir. İçinde yer olamaz ve kendi başına bir sayı olamaz.

PARENTESLER: "Parantezler" veya (), JavaScript işlevli fabrikanın kapısındaki harf kutusudur ya da fabrikaya bilgi paketleri göndermek için sokakta bir posta kutusudur. Bazen posta kutusu örneğin cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) olarak işaretlenebilir; bu durumda hangi verileri vermeniz gerektiğini bilirsiniz.

BRACES: Bu {}'a benzeyen "Braces" fabrikamızın renkli camlarıdır. Fabrikanın içinden görebilirsiniz, ancak dışarıdan göremezsiniz.

YUKARIDAKİ UZUN KOD ÖRNEĞİ

Kodumuz işlevi kelimesiyle başlar, bu yüzden bir tane olduğunu biliyoruz! Öyleyse, sing fonksiyonunun adı - bu, fonksiyonun ne hakkında olduğunu kendi tarifimdir. Sonra parantezes () . Parantezler bir işlev için her zaman oradadır. Bazen boşlar, bazen içeride bir şeyler oluyor. Bunun bir kelimesi var: (person). Bundan sonra { gibi bir ayraç var. Bu, sing () fonksiyonunun başlangıcını işaretler. sing () sonunu işaretleyen bir ortağı var, bunun gibi }

 
function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

Bu nedenle, bu işlevin şarkı ile ilgisi olabilir ve bir kişi hakkında bazı bilgilere ihtiyaç duyabilir. İçinde bu verilerle bir şeyler yapmak için talimatlar var.

Şimdi, sing () işlevinden sonra, kodun sonuna yakın satır

 
var person="an old lady";

DEĞİŞKEN: var harfleri "değişken" anlamına gelir. Bir değişken zarf gibidir. Dış kısımda bu zarf "kişi" olarak işaretlenmiştir. İçeride fonksiyonumuzun ihtiyaç duyduğu bilgiyi içeren bir kağıt parçası, bazı harfler ve boşluklar "yaşlı bir bayan" okuyarak bir cümle oluşturan bir ip (bir ip olarak adlandırılır) gibi bir araya geldi. Zarfımız sayılar (tamsayılar adı verilir), talimatlar (işlevler adı verilir), listeler ( diziler olarak adlandırılır) gibi başka şeyler içerebilir. Bu değişken tüm parantezlerin {} dışına yazıldığından ve parantezlerin içindeyken renkli camların içinden görebildiğiniz için, bu değişken kodun herhangi bir yerinden görülebilir. Buna 'global değişken' diyoruz.

GLOBAL DEĞİŞKEN: kişi global bir değişkendir; bu, değerini "yaşlı bir bayandan" "genç bir adama" değiştirirseniz, kişinin kalmasını sağlar. Tekrar değiştirmeye karar verinceye kadar genç bir adam olmak ve koddaki herhangi bir işlevin onun genç bir adam olduğunu görebilir. Bir tarayıcının geliştirici konsolunu açmak için F12 düğmesine basın veya Seçenekler ayarlarına bakın ve bu değerin ne olduğunu görmek için "kişi" yazın. Değiştirmek için person="a young man", daha sonra değiştiğini görmek için tekrar "kişi" yazın.

Bundan sonra hattımız var

 
sing(person);

Bu satır işlevi bir köpek çağırıyormuşçasına çağırıyor

  

"Hadi şarkı söyle , Gelin ve kişiyi edinin !"

Tarayıcı JavaScript kodunu yüklediğinde bu satıra ulaştığında, işlevi başlatacaktır. Tarayıcının çalıştırmak için ihtiyaç duyduğu tüm bilgilere sahip olduğundan emin olmak için satırı sonuna koydum.

İşlevler eylemleri tanımlar - ana işlev şarkı söylemeye ilişkindir. Şarkının her ayeti için geçerli kişi hakkında şarkı söyleyenler için geçerli olan firstPart adlı bir değişken içerir: "Yutulan" + kişi + "vardı". Konsola firstPart yazarsanız, değişken bir fonksiyonda kilitli olduğundan cevap alamazsınız - tarayıcı parantezin renkli camlarının içinde göremez.

CLOSURES: Kapaklar, büyük sing () fonksiyonunun içindeki daha küçük fonksiyonlardır. Büyük fabrikanın içindeki küçük fabrikalar. Her birinin kendi diş telleri vardır, bu da içlerindeki değişkenlerin dışarıdan görülemeyeceği anlamına gelir. Bu nedenle değişkenlerin adları ( yaratık ve sonuç ) kapaklarda ancak farklı değerlerle tekrarlanabilir. Bu değişken adlarını konsol penceresine yazarsanız, değerini iki renkli cam penceresinin gizlediği için elde edemezsiniz.

Kapanışların tümü, sing () işlevinin firstPart adlı değişkeninin ne olduğunu bilir, çünkü renkli pencerelerinden görebilirler.

Kapanışlardan sonra satırlar geliyor

 
fly();
spider();
bird();
cat();

sing () işlevi, bu işlevlerin her birini verilen sırayla çağırır. Sonra sing () işlevinin çalışması yapılır.

    
59
2016-06-08 22: 11: 57Z

Tamam, 6 yaşında bir çocukla konuşurken muhtemelen şu dernekleri kullanırım.

  

Düşünün - tüm evde küçük kardeşlerinizle birlikte oynuyorsunuz ve oyuncaklarınızla dolaşıyorsunuz ve bazılarını abinin odasına getirdiniz. Bir süre sonra ağabeyiniz okuldan döndü ve odasına gitti ve içine kilitlendi, böylece artık orada bırakılan oyuncaklara doğrudan bir şekilde erişemiyordunuz. Ama kapıyı çalabilir ve kardeşinden o oyuncakları isteyebilirsin. Buna toy'nin kapatılması ; Kardeşin senin için yaptı ve şu anda dış kapsamına girdi .

Bir kapının fıçı tarafından kilitlendiği ve içeride kimse bulunmadığı (genel işlev yürütme) ve ardından bazı yerel yangınların meydana gelip odayı yakmasıyla (çöp toplayıcı: D), sonra yeni bir oda yapıldı ve orada başka bir oyuncak bırakabilirsiniz (yeni işlev örneği),İlk oda örneğinde kalanlar.

İleri bir çocuk için aşağıdakine benzer bir şeyler koyardım. Mükemmel değil, ama ne olduğuna dair hissettiriyor:

 
function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Gördüğünüz gibi, odada bırakılan oyuncaklara, kardeşler aracılığıyla erişilebilir durumdalar ve oda kilitli olsa bile. Burada oynamak için bir jsbin var.

    
55
2014-10-25 22: 52: 13Z

Altı yaşındaki bir çocuğun cevabı (bir fonksiyonun ne olduğunu ve bir değişkenin ne olduğunu ve hangi verilerin olduğunu bildiğini varsayarsak):

İşlevler verileri döndürebilir. Bir işlevden döndürebileceğiniz bir tür veri başka bir işlevdir. Bu yeni işlev döndürüldüğünde, onu oluşturan fonksiyonda kullanılan tüm değişkenler ve argümanlar kaybolmaz. Bunun yerine, bu üst işlev "kapanır". Başka bir deyişle, içine hiçbir şey bakamaz ve döndürdüğü işlev dışında kullandığı değişkenleri görebilir. Bu yeni işlevin, onu yaratan işlevin içine tekrar bakabilmesi ve içindeki verileri görebilmesi için özel bir yeteneği vardır.

 
function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Bunu anlatmanın gerçekten basit bir yolu, kapsam açısından:

Daha büyük bir kapsamın içinde daha küçük bir kapsam oluşturduğunuzda, daha küçük kapsam her zaman daha büyük kapsamda olanı görebilecek.

    
50
2014-10-25 23: 02: 19Z
  1. Kapatmanın, O.O içindeki sınıflara ve iç sınıflara denk olduğu görülüyor.
    2017-02-15 06: 29: 45Z

JavaScript’teki bir işlev yalnızca bir dizi komutun referansı değildir (C dilinde olduğu gibi), aynı zamanda kullandığı tüm yerel olmayan değişkenlere referanslardan oluşan gizli bir veri yapısını da içerir (yakalanan değişkenler). Bu iki parçalı fonksiyona kapatma denir. JavaScript’teki her işlev bir kapatma olarak kabul edilebilir.

Kapaklar, durumlu bir fonksiyondur. "Bu", aynı zamanda bir işlev için durum sağlar, ancak "bu" ayrı nesneler anlamındadır ("bu" sadece süslü bir parametredir ve onu kalıcı olarak bağlamak için tek yoldur). işlevi bir kapatma oluşturmaktır). "Bu" ve işlev her zaman ayrı ayrı yaşarken, bir işlev kapanmasından ayrılamaz ve dil yakalanan değişkenlere erişmek için hiçbir yol sağlamaz.

Sözlüksel olarak iç içe geçmiş bir işlev tarafından başvurulan tüm bu dış değişkenler, gerçekte sözcüksel olarak ekleyen işlevler zincirindeki yerel değişkenlerdir (genel değişkenlerin, bazı kök işlevlerinin yerel değişkenleri olduğu varsayılabilir) ve bir işlevin her bir çalıştırması oluşturulur. Yerel değişkenlerinin yeni örnekleri, iç içe geçmiş bir işlevin geri döndürülmesinin (veya onu geri çağırmak gibi başka bir şekilde devretmenin) gerçekleştirilmesinin, iç içe geçmiş bir işlevin yeni bir kapatma (kendi benzersiz potansiyel başvurulan yerel olmayan değişken kümesiyle) oluşturmasını izler. yürütme içeriğini temsil eder).

Ayrıca, JavaScript’teki yerel değişkenlerin yığın karesinde değil öbek üzerinde yaratıldığını ve yalnızca kimseye gönderme yapmadığında imha edildiğini de belirtmek gerekir. Bir işlev geri döndüğünde, yerel değişkenlerine referanslar azalır, ancak mevcut yürütme sırasında bir kapanmanın parçası olmuşlarsa ve hala kendi iç içe geçmiş işlevleri tarafından başvuruda bulunulursa yine de boş olabilirler (bu sadece referanslar eğer bu iç içe geçmiş işlevler döndürüldü veya başka bir dış koda aktarıldı).

Bir örnek:

 
function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
    
49
2016-05-05 16: 04: 06Z

Belki de hepsinden öte, altı yaşındakilerin en erken tanıdıklarından biri, ancak JavaScript'te kapanma kavramını benimsememe yardımcı olan birkaç örnek benim için tıklayın.

Kapatma, başka bir işlevin kapsamına erişimi olan bir fonksiyondur (değişkenleri ve işlevleri). Bir kapanış oluşturmanın en kolay yolu, bir fonksiyon içindeki bir fonksiyondur; Bunun nedeni, JavaScript’te bir işlevher zaman içerik işlevinin kapsamına erişebilir.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

UYARI: maymun

Yukarıdaki örnekte, sırayla innerFunction adı verilen outerFunction adı verilir. OuterVar'ın outerVar'ın değerini doğru şekilde uyarmasıyla kanıtlandığı şekilde innerFunction için uygun olduğunu unutmayın.

Şimdi aşağıdakileri göz önünde bulundurun:

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

UYARI: maymun

referenceToInnerFunction, innerFunction işlevine basit bir başvuru döndüren outerFunction () öğesine ayarlanır. ReferenceToInnerFunction çağrıldığında outerVar değerini döndürür. Yine, yukarıda olduğu gibi, bu innerFunction'ın outerFar değişkeninin outerVar'a erişebildiğini göstermektedir. Ayrıca, dış erişimin çalışması tamamlandıktan sonra bile bu erişimi koruduğunu not etmek ilginçtir.

Ve işte işler gerçekten ilginçleşiyor. OuterFunction işlevinden kurtulacak olsaydık, null değerine ayarlayın diyoruz, referenceToInnerFunction işlevinin outerVar değerine erişimini kaybedeceğini düşünebilirsiniz. Ancak bu durum böyle değil.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

UYARI: maymun UYARI: maymun

Ama bu nasıl? ReferenceToInnerFunction, artık outerFunction öğesinin null olarak ayarlandığına ilişkin outerVar'ın değerini hala nasıl bilebilir?

referenceToInnerFunction işlevinin outerVar değerine hala erişebilmesinin nedeni, kapak ilk önce inner işlevinin outer işlevinin içine yerleştirilmesiyle oluşturulmuş olması nedeniyle, innerFunction'ın dış zincirinin kapsamına (değişkenleri ve işlevleri) bir referans zinciri ekledi. Bunun anlamı, innerFunction'ın outerVar dahil tüm outerFunction değişkenleri için bir işaretçisi veya referansı olduğu anlamına gelir. Böylece outerFunction'ın çalışması bitmiş olsa bile, ya da silinmiş ya da null olarak ayarlanmış olsa bile, kapsamındaki değişkenler, outerVar gibi, getirilen innerFunction işlevinin kendilerine yapılan olağanüstü referansı nedeniyle belleğe yapışır. referenceToInnerFunction. OuterVar'ı ve outerFunction'ın diğer değişkenlerini bellekten gerçekten serbest bırakmak için, bu olağanüstü referanstan kurtulmanız gerekir, örneğin referenceToInnerFunction işlevini null değerine ayarlayarak.

//////////

Dikkat edilmesi gereken kapanışlarla ilgili diğer iki şey. Birincisi, kapatma her zaman içerme işlevinin son değerlerine erişebilir.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

UYARI: goril

İkincisi, bir kapak oluşturulduğunda, tüm çevreleyen işlevinin değişkenlerine ve işlevlerine bir atıfta bulunur; seçmek ve seçmek alamaz. Ve buna rağmen, kapaklar hafızada yoğun olabileceğinden, en azından dikkatli bir şekilde kullanılmalıdır; Bir içerme fonksiyonunun çalışması bittikten sonra çok sayıda değişken hafızada tutulabilir.

    
48
2015-04-29 15: 37: 06Z

Onları basitçe Mozilla Closures sayfasına yönlendiririm. Bu, kapatma temelleri ve bulduğum pratik kullanımın en iyi, en kısa ve en basit açıklamasıdır. JavaScript öğrenen herkese şiddetle tavsiye edilir.

Ve evet, 6 yaşındaki birine bile önerebilirim - 6 yaşındaki kapanışlarla ilgili bir şey öğreniyorsa, o zaman mantıklıdırlar ki özlü ve basit bir açıklama yapmalılar makalede verilmiştir.

    
45
2014-10-25 22: 54: 15Z
  1. Katılıyorum: söz konusu Mozilla sayfası özellikle basit ve özlüdür. Şaşırtıcı bir şekilde, gönderiniz diğerleri kadar yaygın olarak beğenilmemiştir.
    2018-04-28 08: 21: 59Z
kaynak yerleştirildi İşte
Diğer sorular