Kapatma nedir?


155

Her şimdi ve sonra “kapanışlar” denildiğini görüyorum ve aramaya çalıştım ama Wiki anladığım bir açıklama yapmıyor. Biri bana yardım edebilir mi?


Java / C # 'yı biliyorsanız, bu bağlantının yardımcı olacağını umarsınız- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
Gulshan

1
Kapakları anlamak zor. Bu Wikipedia makalesinin ilk cümlesindeki tüm bağlantıları tıklamayı ve önce bu makaleleri anlamayı denemelisiniz.
Zach


3
Bir kapanış ve bir sınıf arasındaki temel fark nedir? Tamam, sadece bir genel metodu olan bir sınıf.
biziclop

5
@biziclop: Bir sınıfla kapanışını taklit edebilirsiniz (Java devs'in yapması gereken budur). Ancak, genellikle oluşturmak için biraz daha az ayrıntılıdır ve etrafta dolaştığınız şeyi manuel olarak yönetmeniz gerekmez. (Sert lisperler benzer bir soru sorarlar, ancak muhtemelen başka bir sonuca varırlar - yani dil seviyesindeki OO desteği kapanırken gereksizdir).

Yanıtlar:


141

(Feragatname: bu temel bir açıklamadır; tanımın ilerleyişine göre, biraz basitleştiriyorum)

Bir kapatmayı düşünmenin en basit yolu , bir değişken olarak saklanabilen ("birinci sınıf bir işlev" olarak adlandırılır), oluşturulduğu kapsamda yerel olarak diğer değişkenlere erişme özelliğine sahip bir işlevdir.

Örnek (JavaScript):

var setKeyPress = function(callback) {
    document.onkeypress = callback;
};

var initialize = function() {
    var black = false;

    document.onclick = function() {
        black = !black;
        document.body.style.backgroundColor = black ? "#000000" : "transparent";
    }

    var displayValOfBlack = function() {
        alert(black);
    }

    setKeyPress(displayValOfBlack);
};

initialize();

Fonksiyonlar 1 atanır document.onclickve displayValOfBlackkapanır. Her ikisinin de boole değişkenine başvurduğunu görebilirsiniz black, ancak bu değişkene fonksiyonun dışında atanmış. İşlevin tanımlandığı kapsamda yerel olduğundan black, bu değişkene işaretçi korunur.

Bunu bir HTML sayfasına koyarsanız:

  1. Siyah olarak değiştirmek için tıklayın
  2. "True" ifadesini görmek için [enter] tuşuna basın
  3. Tekrar tıkla, beyaza dön
  4. "Yanlış" görmek için [Enter] tuşuna basın

Bu, her ikisinin de aynı erişime sahip olduğunu blackve herhangi bir sarmalayıcı nesnesi olmadan durumu depolamak için kullanılabileceğini göstermektedir .

Çağrı, setKeyPressbir işlevin herhangi bir değişken gibi nasıl iletilebileceğini göstermektir. Kapsamı kapatma korunmuş hala fonksiyon tanımlandı biridir.

Kapaklar, genellikle JavaScript ve ActionScript'te olay işleyicileri olarak kullanılır. Kapakların iyi kullanılması, bir nesne sarmalayıcı oluşturmak zorunda kalmadan değişkenleri olay işleyicilere dolaylı olarak bağlamanıza yardımcı olur. Bununla birlikte, dikkatsiz kullanım bellek sızıntılarına neden olacaktır (örneğin kullanılmayan ancak korunan bir olay işleyicisinin bellekteki büyük nesnelere, özellikle de DOM nesnelerini, çöp toplanmasını önleyen tek şey olduğu zaman).


1: Aslında, JavaScript'teki tüm işlevler kapanıyor.


3
Cevabınızı okurken aklımda bir ampulün yandığını hissettim. Çok takdir! :)
Jay

1
Yana blackbir işlev içinde ilan edilir yığın açıldıkça, bu ... yok olsun ki?
gablin

1
@gablin, kapanış dilleri konusunda benzersiz olan şey budur. Çöp toplama işlemine sahip tüm diller aynı şekilde çalışır - bir nesneye daha fazla atıf yapılmazsa, yok edilebilir. JS'de bir işlev oluşturulduğunda, yerel kapsam bu işlev yok olana kadar bu işleve bağlanır.
Nicole

2
@gablin, bu iyi bir soru. Onların sanmıyorum olamaz mdash: &; ama ben sadece çöp toplama topladım, çünkü JS'nin kullandığı ve “ blackBir işlevin içinde bildirildiği için, bu imha edilmeyecekti ” derken neyi kastediyordunuz . Ayrıca, bir işlevdeki bir nesneyi bildirir ve sonra onu başka bir yerde yaşayan bir değişkene atarsanız, o nesnenin korunduğu için başka referanslar olduğunu unutmayın.
Nicole

1
Objective-C (ve clang altındaki C), çöp toplamadan esasen kapanan blokları destekler. Çalışma zamanı desteği ve bellek yönetimi etrafında bir miktar manuel müdahale gerektirir.
quixoto

68

Bir kapatma, temel olarak bir nesneye bakmanın sadece farklı bir yoludur. Bir nesne, kendisine bağlı bir veya daha fazla işlevi olan veridir. Kapatma, kendisine bağlı bir veya daha fazla değişken içeren bir fonksiyondur. İkisi en azından bir uygulama düzeyinde temelde aynıdır. Asıl fark, nereden geldikleridir.

Nesne yönelimli programlamada, bir nesne sınıfını, üye değişkenlerini ve yöntemlerini (üye işlevleri) önceden tanımlayarak tanımlarsınız ve sonra o sınıfın örneklerini yaratırsınız. Her örnek, yapıcı tarafından ilklendirilen üye verilerinin bir kopyası ile birlikte gelir. Daha sonra, bir nesne türünde bir değişkene sahip olursunuz ve bunu bir veri parçası olarak geçirirsiniz, çünkü odak, veri olarak doğası üzerinedir.

Öte yandan, kapanışta, nesne bir nesne sınıfı gibi ön tarafta tanımlanmaz veya kodunuzdaki bir kurucu çağrı ile başlatılır. Bunun yerine, kapağı başka bir işlevin içine bir işlev olarak yazarsınız. Kapak, dış fonksiyonun herhangi bir yerel değişkenine atıfta bulunabilir ve derleyici bu algılayıcıları tespit eder ve bu değişkenleri dış fonksiyonun yığın alanından kapağın gizli nesne bildirimine taşır. Daha sonra bir kapatma tipi değişkenine sahipsiniz ve temel olarak kaputun altındaki bir nesne olmasına rağmen, bir fonksiyon referansı olarak etrafa geçirin çünkü odak bir fonksiyon olarak doğası üzerinde.


3
+1: İyi cevap. Bir tek bir yöntemle bir kapanış nesnesi ve bazı ortak temel veriler üzerinde (nesnenin üye değişkenleri) bir kapanış koleksiyonu olarak rastgele bir nesne görebilirsiniz. Bu iki görüşün oldukça simetrik olduğunu düşünüyorum.
Giorgio

3
Çok iyi cevap. Aslında kapanma içgörüsünü açıklar.
RoboAlex

1
@Mason Wheeler: Kapatma verileri nerede saklanıyor? Bir fonksiyon gibi yığında? Veya bir nesne gibi yığın halinde?
RoboAlex

1
@RoboAlex: Yığında, çünkü fonksiyona benzeyen bir nesne .
Mason Wheeler

1
@RoboAlex: Bir kapatmanın ve yakalanan verilerin depolandığı yer uygulamaya bağlıdır. C ++ 'da yığında veya yığında depolanabilir.
Giorgio

29

Kapatma terimi , bir kod parçasının (blok, işlev) , kod bloğunun tanımlandığı ortam tarafından kapatılan (yani bir değere bağlı ) serbest değişkenlere sahip olmasından kaynaklanmaktadır .

Örneğin, Scala işlevi tanımını ele alalım:

def addConstant(v: Int): Int = v + k

İşlev gövdesinde iki ad vardır (değişkenler) vve kiki tam sayı değerini belirtir. Ad v, fonksiyonun argümanı olarak bildirildiği için sınırlıdır addConstant(işlev bildirgesine bakarak v, işlev çağrıldığında bir değere atanacağını biliyoruz ). Ad k, fonksiyon ile serbesttir addConstantçünkü fonksiyon hangi değere kbağlı olduğuna dair hiçbir ipucu içermez (ve nasıl).

Bir aramayı şöyle değerlendirmek için:

val n = addConstant(10)

ksadece ktanımlandığı bağlamda tanımlanmışsa gerçekleşebilecek bir değer atamak zorundayız addConstant. Örneğin:

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  def addConstant(v: Int): Int = v + k

  values.map(addConstant)
}

Şimdi tanımlanmış olması addConstantbağlamında ktanımlanan, addConstantbir haline gelmiştir kapatma bütün serbest değişkenler artık çünkü kapalı : (a değere bağlı) addConstantçağrılabilir ve bir işlev gibi etrafında geçti. Serbest değişkenin k, kapak tanımlandığında bir değere bağlı olduğuna v, kapama çağrıldığında argüman değişkeninin bağlı olduğuna dikkat edin .

Bu nedenle, kapatma, temel olarak, yerel olarak bulunmayan değerlere, içerik tarafından bağlandıktan sonra kendi serbest değişkenleri yoluyla erişebilen bir işlev veya kod bloğudur.

Birçok dilde, yalnızca bir kez kapatma kullanıyorsanız , isimsiz yapabilirsiniz;

def increaseAll(values: List[Int]): List[Int] =
{
  val k = 2

  values.map(v => v + k)
}

Serbest değişken içermeyen bir fonksiyonun özel bir kapatma durumu olduğuna dikkat edin (boş bir serbest değişkenler kümesiyle). Benzer şekilde, anonim bir işlev , anonim bir kapatmanın özel bir durumudur; yani anonim bir işlev, serbest değişken içermeyen bir anonim kapatmadır.


Bu, mantıktaki kapalı ve açık formüllerle iyi bir şekilde canlanır. Cevabınız için teşekkürler.
RainDoctor

@RainDoctor: Serbest değişkenler, mantık formüllerinde ve lambda hesabı ifadelerinde benzer şekilde tanımlanır: bir lambda ifadesindeki lambda, serbest / bağlı değişkenleri olan mantık formüllerinde bir niceleyici gibi çalışır.
Giorgio

9

JavaScript’te basit bir açıklama:

var closure_example = function() {
    var closure = 0;
    // after first iteration the value will not be erased from the memory
    // because it is bound with the returned alertValue function.
    return {
        alertValue : function() {
            closure++;
            alert(closure);
        }
    };
};
closure_example();

alert(closure)önceden yaratılan değeri kullanacaktır closure. Döndürülen alertValueişlevin ad alanı, closuredeğişkenin bulunduğu ad alanına bağlanır . Tüm işlevi closuresildiğinizde, değişkenin değeri silinir, ancak o zamana kadar, alertValueişlev her zaman değişkenin değerini okuyabilir / yazabilir closure.

Bu kodu çalıştırırsanız, ilk yineleme closuredeğişkene 0 değerini atar ve işlevi yeniden yazar:

var closure_example = function(){
    alertValue : function(){
        closure++;
        alert(closure);
    }       
}

Ve işlevi yürütmek alertValueiçin yerel değişkene ihtiyaç duyduğundan, closureönceden atanmış yerel değişkenin değerine bağlanır closure.

Ve şimdi, closure_examplefonksiyonu her çağırışınızda, closuredeğişkenin artan değerini yazacaktır çünkü alert(closure)bağlı.

closure_example.alertValue()//alerts value 1 
closure_example.alertValue()//alerts value 2 
closure_example.alertValue()//alerts value 3
//etc. 

teşekkürler, kodu test etmedim =) şimdi her şey yolunda görünüyor.
Muha

5

Bir "kapanış", esasen, bir paket halinde birleştirilen bazı yerel devletler ve bazı kodlardır. Tipik olarak, yerel durum çevreleyen (sözcüksel) bir kapsamdan gelir ve kod (esasen) daha sonra dışa döndürülen bir iç işlevdir. Daha sonra kapatma, iç fonksiyonun gördüğü yakalanan değişkenlerin ve iç fonksiyonun kodunun bir kombinasyonudur.

Tanıdık olmadığından, açıklanması biraz zor olan şeylerden biri.

Geçmişte başarılı bir şekilde kullandığım bir benzetme “kitap” olarak adlandırdığımız bir şeye sahip olduğumuzu düşünün, oda kapanışında, “kitap”, orada, TAOCP köşesinde, masa kapağında, oradaki kopyadır. Bir Dresden Files kitabının kopyası budur. Bu nedenle, ne kapattığınıza bağlı olarak, 'kitabı bana ver' kodu farklı şeylerin ortaya çıkmasına neden olur. ”



3
Hayır, bilinçli olarak o sayfayı kapatmamayı seçtim.
Vatine

"Durum ve işlev": staticYerel değişkenli bir C işlevi kapatma olarak kabul edilebilir mi? Haskell'deki kapanışlar devleti içeriyor mu?
Giorgio

2
Haskell'deki @Giorgio Kapanışları (inanıyorum) tanımlandıkları sözcük kapsamındaki argümanları kapatıyorlar, bu yüzden, "evet" derdim (en iyi Haskell ile aşina olmamama rağmen). Statik değişkenli AC fonksiyonu, en iyi ihtimalle, çok sınırlı bir kapatmadır (gerçekten tek bir fonksiyondan çok sayıda kapatma yapabilmek istersiniz, staticyerel bir değişken ile, tam olarak bir tane vardır).
Vatine

Bu soruyu bilerek sordum çünkü statik değişkenli bir C fonksiyonunun bir kapanma olmadığını düşünüyorum: statik değişken lokal olarak tanımlanır ve sadece kapanmanın içinde bilinir, çevreye erişmez. Ayrıca,% 100 emin değilim ama ifadenizi tam tersi şekilde formüle ederim: farklı işlevler oluşturmak için kapatma mekanizmasını kullanırsınız (bir işlev bir kapatma tanımıdır + serbest değişkenleri için bir bağlayıcıdır).
Giorgio

5

“Devlet” kavramını tanımlamaksızın kapatmanın ne olduğunu tanımlamak zor.

Temel olarak, fonksiyonlara birinci sınıf değerler gibi davranan, tam anlamsal kapsamı olan bir dilde, özel bir şey olur. Eğer böyle bir şey yaparsam:

function foo(x)
return x
end

x = foo

Değişken xsadece referans vermekle function foo()kalmaz, aynı zamanda son dönüşte kalan devlete de atıfta bulunur foo. Asıl sihir foo, kendi kapsamı içinde daha başka tanımlanmış fonksiyonlara sahip olduğunda gerçekleşir ; kendi mini ortamı gibi ('normalde olduğu gibi' küresel bir ortamda fonksiyonları tanımlarız).

İşlevsel olarak, birden fazla işlev çağrısı sırasında yerel bir değişkenin durumunu koruyan C ++ (C?) 'Statik' anahtar sözcüğüyle aynı sorunların çoğunu çözebilir; bununla birlikte, fonksiyonlar birinci sınıf değerler olduğu için aynı prensibi (statik değişken) bir fonksiyona uygulamak gibidir; kapatma, tüm durumun kaydedilmesi için destek ekler (C ++ 'ın statik işlevleriyle ilgisi yoktur).

İşlevleri birinci sınıf değerler olarak kabul etmek ve kapanışlar için destek eklemek, aynı fonksiyonun birden fazla örneğini bellekte (sınıflara benzer şekilde) alabileceğiniz anlamına da gelir. Bunun anlamı, bir fonksiyonun içindeki C ++ statik değişkenleriyle uğraşırken gerektiği gibi, fonksiyonun durumunu sıfırlamak zorunda kalmadan aynı kodu tekrar kullanabilmenizdir (bu konuda yanlış olabilir mi?).

İşte Lua'nın kapatma desteğinin bazı testleri.

--Closure testing
--By Trae Barlow
--

function myclosure()
    print(pvalue)--nil
    local pvalue = pvalue or 10
    return function()
        pvalue = pvalue + 10 --20, 31, 42, 53(53 never printed)
        print(pvalue)
        pvalue = pvalue + 1 --21, 32, 43(pvalue state saved through multiple calls)
        return pvalue
    end
end

x = myclosure() --x now references anonymous function inside myclosure()

x()--nil, 20
x() --21, 31
x() --32, 42
    --43, 53 -- if we iterated x() again

Sonuçlar:

nil
20
31
42

Zorlaşabilir ve muhtemelen dilden dile değişebilir, ancak Lua'da görünen o ki, bir işlev yürütüldüğünde durumunun sıfırlandı. Bunu söylüyorum, çünkü yukarıdaki kodun sonuçları, myclosurefonksiyona / duruma doğrudan erişirsek (döndürdüğü anonim işlev yerine), pvalue10'a geri döndürüleceğimizden farklı olacaktır; ancak gizlilik durumuna x (adsız işlev) üzerinden erişirsek, pvaluebunun canlı ve bellekte bir yerde olduğunu görebilirsiniz . Bundan biraz daha fazlası olduğundan şüpheleniyorum, belki birileri uygulamanın doğasını daha iyi açıklayabilir.

Not: C ++ 11'in bir yalamasını bilmiyorum (önceki sürümlerde olanlar dışında), bunun C ++ 11 ve Lua'daki kapanışlar arasında bir karşılaştırma olmadığını unutmayın. Ayrıca, Lua'dan C ++ 'ya çizilen tüm' çizgiler ', statik değişkenlerle benzerlik gösterir ve kapanışlar% 100 değildir; bazen benzer sorunları çözmek için kullanılsalar bile.

Emin değilim, yukarıdaki kod örneğinde, adsız işlevin mi, yoksa üst düzey işlevin mi kapanma olarak değerlendirileceği mi?


4

Kapatma, ilişkili durumu olan bir işlevdir:

Perl'de şöyle kapaklar yaratırsınız:

#!/usr/bin/perl

# This function creates a closure.
sub getHelloPrint
{
    # Bind state for the function we are returning.
    my ($first) = @_;a

    # The function returned will have access to the variable $first
    return sub { my ($second) = @_; print  "$first $second\n"; };
}

my $hw = getHelloPrint("Hello");
my $gw = getHelloPrint("Goodby");

&$hw("World"); // Print Hello World
&$gw("World"); // PRint Goodby World

C ++ ile sağlanan yeni işlevselliğe bakarsak.
Ayrıca mevcut durumu nesneye bağlamanıza da olanak sağlar:

#include <string>
#include <iostream>
#include <functional>


std::function<void(std::string const&)> getLambda(std::string const& first)
{
    // Here we bind `first` to the function
    // The second parameter will be passed when we call the function
    return [first](std::string const& second) -> void
    {   std::cout << first << " " << second << "\n";
    };
}

int main(int argc, char* argv[])
{
    auto hw = getLambda("Hello");
    auto gw = getLambda("GoodBye");

    hw("World");
    gw("World");
}

2

Basit bir fonksiyon düşünelim:

function f1(x) {
    // ... something
}

Bu işleve bir üst düzey işlev adı verilir çünkü başka bir işlevin içine yerleştirilmez. Her JavaScript işlevi, kendisiyle "Kapsam Zinciri" olarak adlandırılan nesnelerin bir listesini oluşturur . Bu kapsam zinciri sıralı bir nesne listesidir. Bu nesnelerin her biri bazı değişkenleri tanımlar.

Üst düzey fonksiyonlarda, kapsam zinciri tek bir nesneden, genel nesneden oluşur. Örneğin, f1yukarıdaki işlev , tüm global değişkenleri tanımlayan içinde tek bir nesneye sahip olan bir kapsam zincirine sahiptir. (buradaki "nesne" teriminin, JavaScript nesnesi anlamına gelmediğini, yalnızca JavaScript'in değişkenleri "arayabildiği" değişken bir kapsayıcı olarak çalışan, uygulama tarafından tanımlanmış bir nesne olduğunu unutmayın.)

Bu işlev çağrıldığında, JavaScript "Aktivasyon nesnesi" denilen bir şey yaratır ve onu kapsam zincirinin en üstüne yerleştirir. Bu nesne tüm yerel değişkenleri içerir (örneğin xburada). Dolayısıyla şimdi kapsam zincirinde iki nesnemiz var: birincisi aktivasyon nesnesi ve altındaki ise küresel nesne.

Dikkatlice, iki nesnenin farklı zamanlarda kapsam zincirine konulduğunu unutmayın. Genel nesne, işlev tanımlandığında (yani, JavaScript işlevi ayrıştırdığında ve işlev nesnesini oluştururken) yerleştirilir ve işlev çağrıldığında etkinleştirme nesnesi girer.

Yani şimdi bunu biliyoruz:

  • Her işlevin kendisiyle ilişkilendirilmiş bir kapsam zinciri vardır.
  • İşlev tanımlandığında (işlev nesnesi oluşturulduğunda), JavaScript bu işlevle bir kapsam zinciri kaydeder
  • Üst düzey işlevler için, kapsam zinciri, işlev tanımı zamanında yalnızca genel nesneyi içerir ve başlatma zamanında en üstte ek bir etkinleştirme nesnesi ekler

İç içe geçmiş fonksiyonlarla uğraşırken durum ilginçleşiyor. Öyleyse bir tane yaratalım:

function f1(x) {

    function f2(y) {
        // ... something
    }

}

Ne zaman f1tanımlanmış olur biz sadece küresel nesne içeren bir kapsam zinciri olsun.

Şimdi f1çağrıldığında, kapsam zinciri f1aktivasyon nesnesini alır. Bu aktivasyon nesnesi değişkeni xve f2bir işlev olan değişkeni içerir. Ve f2tanımlandığını not edin . Bu nedenle, bu noktada, JavaScript, bunun için yeni bir kapsam zinciri kaydeder f2. Bu iç işlev için kaydedilen kapsam zinciri, geçerli geçerli zincirdir. Geçerli kapsam zinciri yürürlükte f1olanıdır. Bu nedenle f2'in kapsamı zinciridir f1s' mevcut aktivasyon nesnesi içeren - kapsam zinciri f1ve küresel nesne.

Çağrıldığında f2, yiçerdiği aktivasyon nesnesini, aktivasyon nesnesini f1ve genel nesnesini içeren kapsam zincirine ekledi .

İçinde tanımlanmış başka bir iç içe geçmiş işlev varsa f2, kapsam zinciri tanım zamanında üç nesne (iki dış işlevin 2 etkinleştirme nesnesi ve genel nesne) ve başlatma zamanında 4 nesne içerir.

Şimdi, kapsam zincirinin nasıl çalıştığını anlıyoruz ama henüz kapanmalardan bahsetmedik.

Bir fonksiyon nesnesinin ve fonksiyon değişkenlerinin çözümlendiği bir kapsam (bir dizi değişken bağlama) kombinasyonuna bilgisayar bilimi literatüründe kapatma denir - JavaScript David Flanagan'ın kesin rehberi

Çoğu işlev, işlev tanımlandığında geçerli olan aynı kapsam zinciri kullanılarak çağrılır ve bununla ilgili bir kapanmanın olması önemli değildir. Kapaklar, tanımlandıklarında geçerli olandan farklı bir kapsam zinciri altında çalıştırıldığında ilginçleşir. Bu, genellikle iç içe geçmiş bir işlev nesnesi tanımlandığı işlevden döndürüldüğünde gerçekleşir .

İşlev döndüğünde, bu etkinleştirme nesnesi kapsam zincirinden kaldırılır. Yuvalanmış işlev yoksa, etkinleştirme nesnesine daha fazla atıfta bulunulmaz ve çöp toplanır. Tanımlanmış iç içe geçmiş işlevler varsa, bu işlevlerin her birinin kapsam zincirine bir referansı vardır ve bu kapsam zinciri etkinleştirme nesnesini ifade eder.

Bununla birlikte, iç içe geçmiş bu işlevler nesneleri dış işlevleri içinde kaldıysa, o zaman kendileri de atıfta bulundukları etkinleştirme nesnesinin yanı sıra toplanacaklardır. Ancak, işlev iç içe geçmiş bir işlevi tanımlar ve onu döndürür veya başka bir yerde bir özelliğe depolarsa, iç içe geçmiş işleve harici bir başvuru olacaktır. Toplanan çöpler olmayacak ve atıfta bulunduğu etkinleştirme nesnesi de çöp toplanmayacaktır.

Yukarıdaki örnekte, dönmeyen f2gelen f1bir çağrı zaman, bu nedenle, f1geri dönüş, kendi aktivasyon nesne toplanan kapsamı zinciri ve çöp kaldırılır. Ama böyle bir şeyimiz olsaydı:

function f1(x) {

    function f2(y) {
        // ... something
    }

    return f2;
}

Burada geri dönen f2, aktivasyon nesnesini içerecek bir kapsam zincirine sahip olacak f1ve dolayısıyla çöp toplanmayacaktır. Bu noktada, eğer ararsak f2, dışarı f1çıksak xbile değişkene erişebilecek f1.

Dolayısıyla bir fonksiyonun onun kapsam zincirini onunla birlikte tuttuğunu ve kapsam zinciri ile dış fonksiyonların tüm aktivasyon nesnelerinin geldiğini görebiliriz. Bu kapanmanın özüdür. JavaScript’teki işlevlerin "kapsamsal kapsamda " olduğunu , yani çağrıldıklarında etkin olan kapsamın aksine tanımlandıklarında etkin olan kapsamı koruduklarını söylüyoruz.

Özel değişkenlere yaklaşma, olaya dayalı programlama, kısmi uygulama vb. Gibi kapanışları içeren çok sayıda güçlü programlama tekniği vardır .

Ayrıca, tüm bunların, kapanışları destekleyen tüm diller için geçerli olduğunu unutmayın. Örneğin PHP (5.3+), Python, Ruby, vb.


-1

Bir kapatma derleyici optimizasyonu (aka sentaktik şeker?). Bazı insanlar buna Zavallı Adam'ın Nesnesi olarak da bahsetti .

Eric Lippert'in cevabına bakınız : (aşağıda alıntı)

Derleyici şöyle bir kod üretecektir:

private class Locals
{
  public int count;
  public void Anonymous()
  {
    this.count++;
  }
}

public Action Counter()
{
  Locals locals = new Locals();
  locals.count = 0;
  Action counter = new Action(locals.Anonymous);
  return counter;
}

Mantıklı olmak?
Ayrıca, karşılaştırmalar istediniz. VB ve JScript, hemen hemen aynı şekilde kapaklar oluşturur.


Bu cevap CW çünkü Eric'in mükemmel cevabı için puan hak etmiyorum. Lütfen uygun gördüğünüz gibi yükseltin. HTH
goodguys_activate

3
-1: Açıklamanız C # 'da çok köklü. Kapanış birçok dilde kullanılır ve bu dillerdeki sözdizimsel şekerden çok daha fazladır ve hem işlevi hem de durumu kapsar.
Martin York

1
Hayır, kapatma sadece ne “derleyici optimizasyonu” ne de sözdizimsel şeker değildir. -1
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.