Apa itu penutupan?

155

Sesekali saya melihat "penutupan" disebutkan, dan saya mencoba mencarinya tetapi Wiki tidak memberikan penjelasan yang saya mengerti. Bisakah seseorang membantu saya di sini?

Gablin
sumber
Jika Anda tahu Java / C # harap tautan ini akan membantu- http://www.developerfusion.com/article/8251/the-beauty-of-closures/
Gulshan
1
Penutupan sulit dipahami. Anda harus mencoba mengklik semua tautan di kalimat pertama artikel Wikipedia itu, dan memahami artikel-artikel itu terlebih dahulu.
Zach
3
Apa perbedaan mendasar antara penutupan dan kelas? Oke, kelas dengan hanya satu metode publik.
biziclop
5
@biziclop: Anda bisa meniru penutupan dengan kelas (itulah yang harus dilakukan pengembang Jawa). Tapi mereka biasanya sedikit kurang verbal untuk dibuat dan Anda tidak harus mengelola secara manual apa yang Anda carying sekitar. (Para lispers hardcore mengajukan pertanyaan serupa, tetapi mungkin sampai pada kesimpulan lain - bahwa dukungan OO tingkat bahasa tidak diperlukan ketika Anda memiliki penutupan).

Jawaban:

141

(Penafian: ini adalah penjelasan dasar; sejauh definisi berjalan, saya menyederhanakan sedikit)

Cara paling sederhana untuk memikirkan penutupan adalah fungsi yang dapat disimpan sebagai variabel (disebut sebagai "fungsi kelas-pertama"), yang memiliki kemampuan khusus untuk mengakses variabel-variabel lain secara lokal dengan cakupan tempat ia dibuat.

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

Fungsi 1 ditugaskan untuk document.onclickdan displayValOfBlackmerupakan penutupan. Anda dapat melihat bahwa keduanya merujuk variabel boolean black, tetapi variabel tersebut ditugaskan di luar fungsi. Karena blackbersifat lokal pada ruang lingkup di mana fungsi didefinisikan , pointer ke variabel ini dipertahankan.

Jika Anda meletakkan ini di halaman HTML:

  1. Klik untuk berubah menjadi hitam
  2. Tekan [enter] untuk melihat "true"
  3. Klik lagi, ubah kembali menjadi putih
  4. Tekan [enter] untuk melihat "false"

Ini menunjukkan bahwa keduanya memiliki akses yang sama black , dan dapat digunakan untuk menyimpan keadaan tanpa objek pembungkus.

Panggilan ke setKeyPressadalah untuk menunjukkan bagaimana suatu fungsi dapat dilewati seperti variabel apa pun. The lingkup diawetkan dalam penutupan tersebut masih satu di mana fungsi didefinisikan.

Penutupan biasanya digunakan sebagai pengendali acara, terutama dalam JavaScript dan ActionScript. Penggunaan penutupan yang baik akan membantu Anda secara implisit mengikat variabel ke penangan acara tanpa harus membuat pembungkus objek. Namun, penggunaan yang ceroboh akan menyebabkan kebocoran memori (seperti ketika event handler yang tidak digunakan tetapi diawetkan adalah satu-satunya hal untuk mempertahankan benda besar dalam memori, terutama objek DOM, mencegah pengumpulan sampah).


1: Sebenarnya, semua fungsi dalam JavaScript adalah penutup.

Nicole
sumber
3
Ketika saya membaca jawaban Anda, saya merasakan bola lampu menyala di pikiran saya. Sangat dihargai! :)
Jay
1
Karena blackdideklarasikan di dalam suatu fungsi, bukankah itu akan hancur ketika tumpukan dibuka ...?
gablin
1
@ Gablin, itulah yang unik tentang bahasa yang memiliki penutupan. Semua bahasa dengan pengumpulan sampah bekerja dengan cara yang sama - ketika tidak ada lagi referensi yang dipegang pada suatu objek, ia dapat dihancurkan. Setiap kali suatu fungsi dibuat di JS, cakupan lokal terikat ke fungsi itu sampai fungsi itu dihancurkan.
Nicole
2
@ Gablin, itu pertanyaan yang bagus. Saya rasa mereka tidak dapat & mdash; tapi saya hanya memunculkan pengumpulan sampah sejak itu yang digunakan JS dan itulah yang sepertinya Anda maksud ketika Anda berkata "Karena blackdideklarasikan di dalam suatu fungsi, bukankah itu akan dihancurkan". Ingat juga bahwa jika Anda mendeklarasikan objek dalam suatu fungsi dan kemudian menetapkannya ke variabel yang hidup di tempat lain, objek itu dipertahankan karena ada referensi lain untuk itu.
Nicole
1
Objective-C (dan C under clang) mendukung blok, yang pada dasarnya adalah penutupan, tanpa pengumpulan sampah. Ini memerlukan dukungan runtime dan beberapa intervensi manual di sekitar manajemen memori.
quixoto
68

Penutupan pada dasarnya hanya cara berbeda dalam memandang suatu objek. Objek adalah data yang memiliki satu atau lebih fungsi yang terikat padanya. Penutupan adalah fungsi yang memiliki satu atau lebih variabel terikat padanya. Keduanya pada dasarnya identik, setidaknya pada tingkat implementasi. Perbedaan sebenarnya adalah dari mana mereka berasal.

Dalam pemrograman berorientasi objek, Anda mendeklarasikan kelas objek dengan mendefinisikan variabel anggotanya dan metodenya (fungsi anggota) di muka, dan kemudian Anda membuat instance kelas itu. Setiap instance dilengkapi dengan salinan data anggota, diinisialisasi oleh konstruktor. Anda kemudian memiliki variabel tipe objek, dan menyebarkannya sebagai bagian dari data, karena fokusnya adalah pada sifatnya sebagai data.

Dalam penutupan, di sisi lain, objek tidak didefinisikan di muka seperti kelas objek, atau dipakai melalui panggilan konstruktor dalam kode Anda. Sebagai gantinya, Anda menulis penutupan sebagai fungsi di dalam fungsi lain. Penutupan dapat merujuk ke salah satu variabel lokal fungsi luar, dan kompiler mendeteksi itu dan memindahkan variabel-variabel ini dari ruang stack fungsi luar ke deklarasi objek tersembunyi penutupan. Anda kemudian memiliki variabel tipe penutupan, dan meskipun pada dasarnya objek di bawah tenda, Anda menyebarkannya sebagai referensi fungsi, karena fokusnya adalah pada sifatnya sebagai fungsi.

Mason Wheeler
sumber
3
+1: Jawaban yang bagus. Anda dapat melihat penutupan sebagai objek dengan hanya satu metode, dan objek sewenang-wenang sebagai kumpulan penutupan atas beberapa data dasar yang mendasari (variabel anggota objek). Saya pikir kedua pandangan ini cukup simetris.
Giorgio
3
Jawaban yang sangat bagus Ini sebenarnya menjelaskan wawasan tentang penutupan.
RoboAlex
1
@Mason Wheeler: Di mana data penutupan disimpan? Dalam tumpukan seperti fungsi? Atau di tumpukan seperti objek?
RoboAlex
1
@RoboAlex: Di heap, karena itu adalah objek yang terlihat seperti fungsi.
Mason Wheeler
1
@RoboAlex: Di mana penutupan dan data yang ditangkap disimpan tergantung pada implementasinya. Dalam C ++ dapat disimpan di heap atau di stack.
Giorgio
29

Istilah closure berasal dari fakta bahwa sepotong kode (blok, fungsi) dapat memiliki variabel bebas yang ditutup (yaitu terikat pada nilai) oleh lingkungan di mana blok kode didefinisikan.

Ambil contoh definisi fungsi Scala:

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

Dalam tubuh fungsi ada dua nama (variabel) vdan kmenunjukkan dua nilai integer. Nama vdiikat karena dideklarasikan sebagai argumen fungsi addConstant(dengan melihat deklarasi fungsi kita tahu bahwa vakan diberi nilai ketika fungsi dipanggil). Namanya kadalah free wrt fungsi addConstantkarena fungsi tidak mengandung petunjuk tentang apa nilai kterikat (dan bagaimana).

Untuk mengevaluasi panggilan seperti:

val n = addConstant(10)

kita harus menetapkan knilai, yang hanya dapat terjadi jika nama ktersebut didefinisikan dalam konteks yang addConstantdidefinisikan. Sebagai contoh:

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

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

  values.map(addConstant)
}

Sekarang kita telah mendefinisikan addConstantdalam konteks di mana kdidefinisikan, addConstanttelah menjadi penutup karena semua variabel bebasnya sekarang ditutup (terikat pada nilai): addConstantdapat dipanggil dan diedarkan seolah-olah itu adalah fungsi. Perhatikan variabel bebas kterikat ke nilai ketika penutupan didefinisikan , sedangkan variabel argumen vterikat ketika penutupan dipanggil .

Jadi penutupan pada dasarnya adalah fungsi atau blok kode yang dapat mengakses nilai-nilai non-lokal melalui variabel bebasnya setelah ini terikat oleh konteks.

Dalam banyak bahasa, jika Anda menggunakan penutupan hanya sekali Anda bisa membuatnya anonim , misalnya

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

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

Perhatikan bahwa fungsi tanpa variabel bebas adalah kasus khusus dari penutupan (dengan set variabel bebas yang kosong). Secara analog, fungsi anonim adalah kasus khusus dari penutupan anonim , yaitu fungsi anonim adalah penutupan anonim tanpa variabel bebas.

Giorgio
sumber
Ini cocok dengan formula tertutup dan terbuka dalam logika. Terima kasih atas jawaban anda.
RainDoctor
@RainDoctor: Variabel bebas didefinisikan dalam rumus logika dan ekspresi kalkulus lambda dengan cara yang sama: lambda dalam ekspresi lambda bekerja seperti quantifier dalam rumus logika wrt free / bound variable.
Giorgio
9

Penjelasan sederhana dalam JavaScript:

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)akan menggunakan nilai yang dibuat sebelumnya dari closure. alertValueNamespace fungsi yang dikembalikan akan terhubung ke namespace di mana closurevariabel berada. Ketika Anda menghapus seluruh fungsi, nilai closurevariabel akan dihapus, tetapi sampai saat itu, alertValuefungsi akan selalu dapat membaca / menulis nilai variabel closure.

Jika Anda menjalankan kode ini, iterasi pertama akan memberikan nilai 0 ke closurevariabel dan menulis ulang fungsi untuk:

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

Dan karena alertValuemembutuhkan variabel lokal closureuntuk menjalankan fungsi, ia mengikat dirinya sendiri dengan nilai variabel lokal yang ditugaskan sebelumnya closure.

Dan sekarang setiap kali Anda memanggil closure_examplefungsi, itu akan menuliskan nilai closurevariabel yang bertambah karena alert(closure)terikat.

closure_example.alertValue()//alerts value 1 
closure_example.alertValue()//alerts value 2 
closure_example.alertValue()//alerts value 3
//etc. 
Muha
sumber
terima kasih, saya tidak menguji kode =) semuanya tampak baik-baik saja sekarang.
Muha
5

"Penutupan" pada dasarnya adalah beberapa negara bagian dan beberapa kode, digabungkan menjadi satu paket. Biasanya, keadaan lokal berasal dari lingkup (leksikal) di sekitarnya dan kode tersebut (pada dasarnya) merupakan fungsi dalam yang kemudian dikembalikan ke luar. Penutupan kemudian merupakan kombinasi dari variabel yang ditangkap yang dilihat fungsi dalam dan kode fungsi dalam.

Ini salah satu hal yang, sayangnya, agak sulit dijelaskan, karena tidak terbiasa.

Salah satu analogi yang berhasil saya gunakan di masa lalu adalah "bayangkan kita memiliki sesuatu yang kita sebut 'buku', di penutupan kamar, 'buku' adalah salinan itu di sana, di sudut, dari TAOCP, tetapi di meja-penutupan , itu salinan buku File Dresden. Jadi, tergantung pada penutupan apa Anda, kode 'beri saya buku' menghasilkan hal-hal yang berbeda terjadi. "

Vatine
sumber
Anda lupa ini: en.wikipedia.org/wiki/Closure_(computer_programming) dalam jawaban Anda.
S.Lott
3
Tidak, saya secara meyakinkan memilih untuk tidak menutup halaman itu.
Vatine
"Status dan fungsi.": Dapatkah fungsi C dengan staticvariabel lokal dianggap sebagai penutupan? Apakah penutupan di Haskell melibatkan negara?
Giorgio
2
@Giorgio Closures di Haskell melakukan (saya percaya) menutup argumen dalam lingkup leksikal yang mereka definisikan, jadi, saya akan mengatakan "ya" (walaupun saya paling tidak terbiasa dengan Haskell). Fungsi AC dengan variabel statis, paling banter, adalah penutupan yang sangat terbatas (Anda benar-benar ingin dapat membuat banyak penutupan dari satu fungsi, dengan staticvariabel lokal, Anda memiliki tepat satu).
Vatine
Saya mengajukan pertanyaan ini dengan sengaja karena saya berpikir bahwa fungsi C dengan variabel statis bukan merupakan penutupan: variabel statis didefinisikan secara lokal dan hanya diketahui di dalam penutupan, itu tidak mengakses lingkungan. Juga, saya tidak 100% yakin tetapi saya akan merumuskan pernyataan Anda sebaliknya: Anda menggunakan mekanisme penutupan untuk membuat fungsi yang berbeda (fungsi adalah definisi penutupan + pengikatan untuk variabel bebasnya).
Giorgio
5

Sulit untuk mendefinisikan apa penutupan itu tanpa mendefinisikan konsep 'negara'.

Pada dasarnya, dalam bahasa dengan pelingkupan leksikal penuh yang memperlakukan fungsi sebagai nilai kelas satu, sesuatu yang istimewa terjadi. Jika saya melakukan sesuatu seperti:

function foo(x)
return x
end

x = foo

Variabel xtidak hanya referensi function foo()tetapi juga referensi negara foodibiarkan terakhir kali dikembalikan. Keajaiban nyata terjadi ketika foofungsi-fungsi lain didefinisikan lebih lanjut dalam ruang lingkupnya; itu seperti lingkungan mini sendiri (seperti 'biasanya' kita mendefinisikan fungsi dalam lingkungan global).

Secara fungsional ia dapat memecahkan banyak masalah yang sama dengan kata kunci 'statis' C ++ (C?), Yang mempertahankan status variabel lokal melalui beberapa panggilan fungsi; namun itu lebih seperti menerapkan prinsip yang sama (variabel statis) ke suatu fungsi, karena fungsi adalah nilai kelas pertama; closure menambahkan dukungan untuk seluruh fungsi keadaan untuk disimpan (tidak ada hubungannya dengan fungsi statis C ++).

Memperlakukan fungsi sebagai nilai kelas pertama dan menambahkan dukungan untuk penutupan juga berarti Anda dapat memiliki lebih dari satu instance dari fungsi yang sama dalam memori (mirip dengan kelas). Apa artinya ini adalah Anda dapat menggunakan kembali kode yang sama tanpa harus mengatur ulang status fungsi, seperti yang diperlukan ketika berhadapan dengan variabel statis C ++ di dalam suatu fungsi (mungkin salah tentang ini?).

Berikut ini beberapa pengujian dukungan penutupan Lua.

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

hasil:

nil
20
31
42

Ini bisa menjadi rumit, dan mungkin bervariasi dari satu bahasa ke bahasa lain, tetapi tampaknya di Lua bahwa setiap kali suatu fungsi dieksekusi, statusnya diatur ulang. Saya mengatakan ini karena hasil dari kode di atas akan berbeda jika kita mengakses myclosurefungsi / negara secara langsung (alih-alih melalui fungsi anonim mengembalikannya), seperti pvalueakan diatur ulang kembali ke 10; tetapi jika kita mengakses status myclosure melalui x (fungsi anonim) Anda dapat melihat bahwa pvalueitu hidup dan berada di suatu tempat di memori. Saya menduga ada sedikit lebih dari itu, mungkin seseorang dapat lebih menjelaskan sifat implementasi.

PS: Saya tidak tahu sedikitpun tentang C ++ 11 (selain apa yang ada di versi sebelumnya) jadi perlu dicatat bahwa ini bukan perbandingan antara penutupan di C ++ 11 dan Lua. Juga, semua 'garis yang ditarik' dari Lua ke C ++ adalah kesamaan karena variabel statis dan penutupan tidak 100% sama; bahkan jika mereka kadang-kadang digunakan untuk memecahkan masalah yang sama.

Hal yang saya tidak yakin adalah, dalam contoh kode di atas, apakah fungsi anonim atau fungsi urutan lebih tinggi dianggap sebagai penutupan?

Trae Barlow
sumber
4

Penutupan adalah fungsi yang memiliki status terkait:

Dalam perl Anda membuat penutupan seperti ini:

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

Jika kita melihat fungsionalitas baru yang disediakan dengan C ++.
Ini juga memungkinkan Anda untuk mengikat keadaan saat ini ke objek:

#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");
}
Martin York
sumber
2

Mari kita pertimbangkan fungsi sederhana:

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

Fungsi ini disebut fungsi tingkat atas karena tidak bersarang di dalam fungsi lain. Setiap fungsi JavaScript terkait dengan dirinya sendiri daftar objek yang disebut "Rantai Lingkup" . Rantai lingkup ini adalah daftar objek yang diurutkan. Setiap objek ini mendefinisikan beberapa variabel.

Dalam fungsi tingkat atas, rantai lingkup terdiri dari satu objek, objek global. Sebagai contoh, fungsi di f1atas memiliki rantai lingkup yang memiliki objek tunggal di dalamnya yang mendefinisikan semua variabel global. (perhatikan bahwa istilah "objek" di sini tidak berarti objek JavaScript, itu hanya objek implementasi yang didefinisikan yang bertindak sebagai wadah variabel, di mana JavaScript dapat "mencari" variabel.)

Ketika fungsi ini dipanggil, JavaScript membuat sesuatu yang disebut "objek Aktivasi" , dan meletakkannya di bagian atas rantai lingkup. Objek ini berisi semua variabel lokal (misalnya di xsini). Karenanya sekarang kita memiliki dua objek dalam rantai lingkup: yang pertama adalah objek aktivasi dan di bawahnya adalah objek global.

Catat dengan sangat hati-hati bahwa kedua objek dimasukkan ke dalam rantai lingkup pada waktu yang BERBEDA. Objek global diletakkan ketika fungsi didefinisikan (yaitu, ketika JavaScript mem-parsing fungsi dan membuat objek fungsi), dan objek aktivasi masuk ketika fungsi dipanggil.

Jadi, kita sekarang tahu ini:

  • Setiap fungsi memiliki rantai lingkup yang terkait dengannya
  • Ketika fungsi didefinisikan (ketika objek fungsi dibuat), JavaScript menyimpan rantai lingkup dengan fungsi itu
  • Untuk fungsi tingkat atas, rantai lingkup hanya berisi objek global pada waktu definisi fungsi dan menambahkan objek aktivasi tambahan di atas pada waktu doa

Situasi menjadi menarik ketika kita berurusan dengan fungsi bersarang. Jadi, mari kita buat satu:

function f1(x) {

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

}

Ketika f1didefinisikan, kita mendapatkan rantai lingkup karena hanya berisi objek global.

Sekarang ketika f1dipanggil, rantai lingkup f1mendapat objek aktivasi. Objek aktivasi ini berisi variabel xdan variabel f2yang merupakan fungsi. Dan, perhatikan bahwa f2semakin didefinisikan. Karenanya, pada titik ini, JavaScript juga menyimpan rantai cakupan baru untuk f2. Rantai lingkup disimpan untuk fungsi dalam ini adalah rantai lingkup saat ini berlaku. Rantai lingkup saat ini yang berlaku adalah dari f1. Oleh karena itu f2's rantai lingkup adalah f1' s saat ini ruang lingkup rantai - yang berisi objek aktivasi f1dan obyek global.

Ketika f2dipanggil, ia mendapatkan objek aktivasi itu sendiri berisi y, ditambahkan ke rantai lingkupnya yang sudah berisi objek aktivasi f1dan objek global.

Jika ada fungsi bersarang lain yang didefinisikan di dalam f2, rantai cakupannya akan berisi tiga objek pada waktu definisi (2 objek aktivasi dari dua fungsi luar, dan objek global), dan 4 pada waktu doa.

Jadi, sekarang kami mengerti bagaimana rantai lingkup bekerja tetapi kami belum membicarakan tentang penutupan.

Kombinasi objek fungsi dan ruang lingkup (satu set binding variabel) di mana variabel fungsi diselesaikan disebut penutupan dalam literatur ilmu komputer - JavaScript panduan definitif oleh David Flanagan

Sebagian besar fungsi dipanggil menggunakan rantai lingkup yang sama yang berlaku ketika fungsi didefinisikan, dan tidak terlalu penting bahwa ada penutupan yang terlibat. Penutupan menjadi menarik ketika mereka dipanggil di bawah rantai lingkup yang berbeda dari yang berlaku ketika mereka didefinisikan. Ini terjadi paling umum ketika objek fungsi bersarang dikembalikan dari fungsi yang didefinisikan.

Ketika fungsi kembali, objek aktivasi itu dihapus dari rantai lingkup. Jika tidak ada fungsi bersarang, tidak ada lagi referensi ke objek aktivasi dan mengumpulkan sampah. Jika ada fungsi bersarang yang ditentukan, maka masing-masing fungsi tersebut memiliki referensi ke rantai lingkup, dan rantai lingkup tersebut merujuk ke objek aktivasi.

Namun, jika objek fungsi bersarang tetap berada di dalam fungsi luarnya, maka objek itu sendiri akan menjadi sampah yang dikumpulkan, bersama dengan objek aktivasi yang dimaksud. Tetapi jika fungsi mendefinisikan fungsi bersarang dan mengembalikannya atau menyimpannya di properti di suatu tempat, maka akan ada referensi eksternal ke fungsi bersarang. Itu bukan sampah yang dikumpulkan, dan objek aktivasi yang dimaksud juga bukan sampah yang dikumpulkan.

Dalam contoh kami di atas, kami tidak kembali f2dari f1, karenanya, ketika panggilan untuk f1kembali, objek aktivasi akan dihapus dari rantai cakupannya dan sampah dikumpulkan. Tetapi jika kita memiliki sesuatu seperti ini:

function f1(x) {

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

    return f2;
}

Di sini, pengembalian f2akan memiliki rantai lingkup yang akan berisi objek aktivasi f1, dan karenanya itu tidak akan menjadi sampah yang dikumpulkan. Pada titik ini, jika kita memanggil f2, ia akan dapat mengakses f1variabel xmeskipun kita tidak ada f1.

Oleh karena itu kita dapat melihat bahwa fungsi menjaga rantai ruang lingkup dengannya dan dengan rantai lingkup datang semua objek aktivasi fungsi luar. Inilah inti dari penutupan. Kami mengatakan bahwa fungsi dalam JavaScript adalah "dibatasi secara leksikal" , yang berarti bahwa mereka menyimpan ruang lingkup yang aktif ketika didefinisikan sebagai bertentangan dengan ruang lingkup yang aktif ketika mereka dipanggil.

Ada sejumlah teknik pemrograman yang kuat yang melibatkan penutupan seperti mendekati variabel pribadi, pemrograman berbasis peristiwa, aplikasi parsial , dll.

Perhatikan juga bahwa semua ini berlaku untuk semua bahasa yang mendukung penutupan. Misalnya PHP (5.3+), Python, Ruby, dll.

treecoder
sumber
-1

Penutupan adalah pengoptimalan kompiler (alias sintaksis gula?). Beberapa orang menyebut ini sebagai Obyek Orang Miskin juga.

Lihat jawabannya oleh Eric Lippert : (kutipan di bawah)

Kompiler akan menghasilkan kode seperti ini:

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

Masuk akal?
Anda juga meminta perbandingan. VB dan JScript keduanya membuat penutupan dengan cara yang hampir sama.

LamonteCristo
sumber
Jawaban ini adalah CW karena saya tidak pantas mendapat poin atas jawaban Eric yang luar biasa. Harap pilih sesuai keinginan Anda. HTH
goodguys_activate
3
-1: Penjelasan Anda terlalu root di C #. Penutupan digunakan dalam banyak bahasa dan lebih dari sekadar gula sintaksis dalam bahasa-bahasa ini dan mencakup fungsi dan status.
Martin York
1
Tidak, penutupan bukan hanya "optimisasi kompiler" atau gula sintaksis. -1