Apa gunanya praktis untuk penutupan dalam JavaScript?


280

Saya berusaha sekuat tenaga untuk membungkus kepala saya di sekitar penutupan JavaScript.

Saya mendapatkan bahwa dengan mengembalikan fungsi bagian dalam, itu akan memiliki akses ke variabel apa pun yang ditentukan dalam induk langsungnya.

Di mana ini berguna bagi saya? Mungkin aku belum cukup memahaminya. Sebagian besar contoh yang saya lihat online tidak memberikan kode dunia nyata, hanya contoh samar.

Bisakah seseorang menunjukkan kepada saya penggunaan penutupan di dunia nyata?

Apakah ini, misalnya?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
+1 untuk berusaha sekuat tenaga :-) Penutupan bisa terasa sangat menakutkan untuk memulai, saya tahu itu untuk saya. Setelah Anda terbiasa, Anda akan langsung menjadi pembuat kode yang jauh lebih baik.
Andy E

7
Saya baru saja menulis posting blog tentang penutupan dalam JavaScript yang mungkin Anda temukan helfpul.
Skilldrick

@ Skilldrick. Tautan sudah mati ... dan saya juga menemukan contoh praktis ini sangat membantu. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Jawaban:


241

Saya telah menggunakan penutupan untuk melakukan hal-hal seperti:

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

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

Seperti yang Anda lihat di sana, asekarang adalah objek, dengan metode publicfunction( a.publicfunction()) yang memanggil privatefunction, yang hanya ada di dalam penutupan. Anda TIDAK bisa menelepon privatefunctionlangsung (yaitu a.privatefunction()), adil publicfunction().

Ini contoh minimal tapi mungkin Anda bisa melihat kegunaannya? Kami menggunakan ini untuk menegakkan metode publik / pribadi.


26
Ah, jika ini adalah penutupan, maka saya telah menggunakan penutupan tanpa menyadarinya! Saya sering memasukkan fungsi ke dalam yang lain seperti itu, dan kemudian memaparkan apa pun yang saya butuhkan publik dengan mengembalikan objek literal seperti dalam contoh Anda.
alex

1
Ya, seperti yang Anda lihat, Anda menyimpan konteks fungsi karena objek yang Anda kembalikan variabel referensi (dan fungsi) di dalamnya. Jadi Anda sudah menggunakannya, Anda tidak tahu itu.
Francisco Soto

9
Secara teknis setiap fungsi yang Anda buat dalam Javascript pada browser adalah penutupan karena objek jendela terikat padanya.
Adam Gent

9
Saya tahu ini adalah pertanyaan lama, tetapi bagi saya ini masih belum memberikan jawaban yang memadai. Mengapa tidak langsung memanggil fungsi saja? Mengapa Anda memerlukan fungsi pribadi?
qodeninja

5
Karena walaupun contoh hanya memiliki fungsi, ia juga bisa memiliki variabel yang tidak dapat diakses dari luar. Katakan: var obj = (function () {var value = 0; return {get: function () {value return;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 dll.
Francisco Soto

212

Misalkan, Anda ingin menghitung berapa kali pengguna mengklik tombol pada halaman web.
Untuk ini, Anda memicu fungsi saat onclicktombol untuk memperbarui jumlah variabel

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

Sekarang mungkin ada banyak pendekatan seperti:

1) Anda bisa menggunakan variabel global , dan fungsi untuk menambah penghitung :

var counter = 0;

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

Namun, jebakannya adalah bahwa skrip apa pun pada laman dapat mengubah penghitung, tanpa meneleponupdateClickCount() .


2) Sekarang, Anda mungkin berpikir untuk mendeklarasikan variabel di dalam fungsi:

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

Tapi hey! Setiap updateClickCount()fungsi waktu dipanggil, penghitung diatur ke 1 lagi.


3) Memikirkan fungsi Nested ?

Fungsi bersarang memiliki akses ke ruang lingkup "di atas" mereka.
Dalam contoh ini, fungsi dalam updateClickCount()memiliki akses ke variabel counter di fungsi indukcountWrapper()

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

Ini bisa memecahkan dilema kontra, jika Anda dapat mencapai updateClickCount()fungsi dari luar dan Anda juga perlu menemukan cara untuk mengeksekusi counter = 0hanya sekali tidak setiap kali.


4) Penutupan untuk penyelamatan! (fungsi memohon sendiri) :

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

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

Fungsi memohon sendiri hanya berjalan sekali. Ini mengatur counterke nol (0), dan mengembalikan ekspresi fungsi.

Cara ini updateClickCountmenjadi fungsi. Bagian "luar biasa" adalah dapat mengakses konter dalam lingkup induk.

Ini disebut penutupan JavaScript . Itu memungkinkan suatu fungsi memiliki variabel " pribadi ".

Ini counterdilindungi oleh ruang lingkup fungsi anonim, dan hanya dapat diubah menggunakan fungsi add!

Contoh yang lebih hidup tentang Penutupan:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


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


50
Ini adalah jawaban pertama yang membuat saya berkata, "Oh, itu sebabnya saya akan menggunakan penutupan!"
Pengacara Setan

6
u membuat hari saya :)
JerryGoyal

15
Saya baru saja membaca halaman w3schools pada penutupan dan kemudian datang ke sini untuk info lebih lanjut. Ini sama dengan halaman w3schools
tyelford

1
@ JerryGoyal, bisakah Anda membuatnya bekerja dengan 2 tombol terpisah? Saya tidak tahu bagaimana tanpa menggunakan 2 variabel (salinan fungsi), yang tampaknya menghapus beberapa manfaat / kenyamanan utama.
Tyler Collier

2
Jawaban yang bagus. Catat bahwa penutupan tidak harus berupa fungsi yang bisa digunakan sendiri, tetapi bisa juga. Ketika penutupan adalah pemanggilan diri (yaitu segera dipanggil dengan menambahkan () setelah fungsi), ini berarti nilai kembali segera dihitung, alih-alih fungsi dikembalikan dan nilai kembali dihitung nanti setelah fungsi dipanggil. Penutupan sebenarnya dapat berupa fungsi apa pun di dalam fungsi lain, dan karakteristik utamanya adalah bahwa ia memiliki akses ke ruang lingkup fungsi induk termasuk variabel dan metode itu.
Chris Halcrow

69

Contoh yang Anda berikan sangat bagus. Penutupan adalah mekanisme abstraksi yang memungkinkan Anda untuk memisahkan masalah dengan sangat bersih. Contoh Anda adalah kasus pemisahan instrumentasi (penghitungan panggilan) dari semantik (API pelaporan kesalahan). Kegunaan lain termasuk:

  1. Melewati perilaku parameterisasi ke dalam algoritma (pemrograman klasik tingkat tinggi):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
    
  2. Mensimulasikan pemrograman berorientasi objek:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
    
  3. Menerapkan kontrol aliran yang eksotis, seperti penanganan Acara jQuery dan API AJAX.


3
( int?) Terakhir saya periksa, JavaScript adalah bahasa yang diketik bebek. Mungkin Anda memikirkan Jawa?
Hello71

1
@ Hello71: Saya sedang memikirkan JavaScript, tetapi kebiasaan lama sulit. Tangkapan yang bagus.
Marcelo Cantos

2
@MarceloCantos sepertinya Anda lupa koma dalam implementasi penghitung. Saya mengedit posting Anda untuk memperbaikinya. Semoga tidak apa-apa :)
Natan Streppel

2
@ Streppel: Tangkapan bagus! Saya sangat senang Anda membuat kode saya lebih baik. :-)
Marcelo Cantos

mencoba memahami # 1 ... Bagaimana Anda memanggil proximity_sort?
Dave2081

26

Saya tahu saya sangat telat dalam menjawab pertanyaan ini, tetapi mungkin bisa membantu siapa saja yang mencari jawabannya di 2018.

Penutupan Javascript dapat digunakan untuk mengimplementasikan fungsi throttle dan debounce di aplikasi Anda.

Pelambatan :

Throttling memberi batasan sebagai jumlah kali maksimum suatu fungsi dapat dipanggil dari waktu ke waktu. Seperti pada "jalankan fungsi ini paling banyak sekali setiap 100 milidetik."

Kode:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Debouncing :

Debouncing memberi batasan pada fungsi yang tidak dapat dipanggil lagi sampai sejumlah waktu telah berlalu tanpa dipanggil. Seperti dalam "jalankan fungsi ini hanya jika 100 milidetik telah berlalu tanpa dipanggil."

Kode:

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

Seperti yang Anda lihat, penutupan membantu dalam mengimplementasikan dua fitur cantik yang harus dimiliki setiap aplikasi web untuk menyediakan fungsionalitas pengalaman UI yang lancar.

Saya harap ini akan membantu seseorang.


18

Ya, itu adalah contoh bagus dari penutupan yang bermanfaat. Panggilan ke warnUser membuat calledCountvariabel dalam cakupannya dan mengembalikan fungsi anonim yang disimpan dalam warnForTampervariabel. Karena masih ada penutupan menggunakan variabel dipanggilCount, itu tidak dihapus pada saat keluar fungsi, sehingga setiap panggilan ke warnForTamper()akan meningkatkan variabel cakupan dan mengingatkan nilai.

Masalah paling umum yang saya lihat di StackOverflow adalah di mana seseorang ingin "menunda" penggunaan variabel yang meningkat pada setiap loop, tetapi karena variabel tersebut dicakup maka setiap referensi ke variabel akan setelah loop berakhir, menghasilkan status akhir variabel:

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

Ini akan menghasilkan setiap peringatan yang menunjukkan nilai yang sama i, nilai yang dinaikkan menjadi ketika loop berakhir. Solusinya adalah membuat penutupan baru, ruang lingkup terpisah untuk variabel. Ini dapat dilakukan dengan menggunakan fungsi anonim yang dieksekusi langsung, yang menerima variabel dan menyimpan statusnya sebagai argumen:

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

-1 Menarik, saya kira ini bukan "penggunaan praktis untuk penutupan di javascript"?
Andy E

1
Saya menemukan beberapa kegunaan dalam membacanya jadi saya memberi +1 sebelum downvote.
alex

1
@alex: terima kasih, saya memang memperhatikan upvote. Saya sudah hampir terbiasa dengan downvotes anonim di SO. Itu hanya mengganggu saya karena saya benar-benar ingin tahu apakah saya telah mengatakan sesuatu yang tidak akurat atau salah, dan mereka cenderung membuat Anda berpikir Anda baru saja dikalahkan oleh penjawab lain yang menginginkan visibilitas yang lebih baik untuk jawaban mereka sendiri. Untungnya, saya bukan tipe pendendam ;-)
Andy E

1
Saya pikir ini lebih merupakan pekerjaan sekitar untuk ruang lingkup blok JavaScripts rusak. Anda seharusnya bisa menambahkan var j = i; sebelum setTimeout pertama dan dapatkan lansiran untuk menggunakannya j. Pekerjaan lain di sekitar adalah menggunakan 'with' seperti: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Nilai dari i was "+ i +" ketika timer ini disetel ")}, 100);}}
davidbuttar

1
@AndyE Lucu mungkin bukan kata yang tepat. Saya hanya memperhatikan bahwa sering orang menggunakan fungsi yang dapat dipanggil sendiri untuk menjelaskan penutupan, seperti banyak jawaban di halaman ini. Tetapi fungsi panggilan balik di setTimeout juga merupakan penutupan; mungkin dianggap "penggunaan praktis" karena Anda dapat mengakses beberapa variabel lokal lainnya dari panggilan balik. Ketika saya belajar tentang penutupan, menyadari ini berguna bagi saya - bahwa penutupan ada di mana-mana, tidak hanya dalam pola JavaScript arcade.
antoine

14

Dalam bahasa JavaScript (atau ECMAScript), khususnya, penutupan berguna untuk menyembunyikan implementasi fungsi sambil tetap mengungkapkan antarmuka.

Sebagai contoh, bayangkan Anda sedang menulis kelas metode utilitas tanggal dan Anda ingin memungkinkan pengguna untuk mencari nama hari kerja dengan indeks tetapi Anda tidak ingin mereka dapat memodifikasi array nama yang Anda gunakan di bawah tenda.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Perhatikan bahwa daysarray hanya dapat disimpan sebagai properti dateUtilobjek tetapi kemudian akan terlihat oleh pengguna skrip dan mereka bahkan dapat mengubahnya jika mereka mau, tanpa perlu kode sumber Anda. Namun, karena ini dilingkupi oleh fungsi anonim yang mengembalikan fungsi pencarian tanggal itu hanya dapat diakses oleh fungsi pencarian sehingga sekarang rusak-bukti.


2
Ini mungkin terdengar bodoh, tetapi tidak bisakah mereka hanya membuka file JavaScript itu sendiri dan melihat implementasi Anda?
itsmichaelwang

1
@Zapurdead: ya, mereka tentu saja dapat melihat implementasi tetapi mereka tidak dapat mengubah implementasi (secara tidak sengaja atau sengaja) tanpa secara langsung memodifikasi kode sumber Anda. Saya kira Anda dapat membandingkannya dengan anggota yang dilindungi di Jawa.
maerics

6

Melihat melalui ini, saya tidak melihat bagaimana itu "praktis" seolah-olah saya menghapus seluruh return function ()...kode masih berfungsi dengan baik. Penutupan itu tidak perlu
earwig

@James_Parsons Kemudian Anda tidak bisa menugaskan mereka ke penangan acara seperti yang telah mereka lakukan dalam contoh itu.
alex

5

Penggunaan lain yang umum untuk penutupan adalah untuk mengikat thissuatu metode ke objek tertentu, yang memungkinkannya dipanggil di tempat lain (seperti event handler).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Setiap kali acara mousemove kebakaran, watcher.follow(evt)disebut.

Penutupan juga merupakan bagian penting dari fungsi tingkat tinggi, memungkinkan pola yang sangat umum menulis ulang beberapa fungsi yang sama sebagai fungsi urutan lebih tinggi tunggal dengan parameterisasi bagian yang berbeda. Sebagai contoh abstrak,

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

menjadi

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

di mana A dan B bukan unit sintaksis tetapi string kode sumber (bukan string literal).

Lihat " Merampingkan javascript saya dengan suatu fungsi " untuk contoh konkret.


5

Di sini, saya punya salam yang ingin saya katakan beberapa kali. Jika saya membuat penutupan, saya cukup memanggil fungsi itu untuk merekam salam. Jika saya tidak membuat penutupan, saya harus memberikan nama saya setiap saat.

Tanpa penutupan ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

Dengan penutup ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
Saya tidak yakin tetapi masih tanpa penutupan Anda dapat memanggil sebagai var grretBilly = ucapan ("Billy", "Bob"); dan hubungi grretBilly (); Masih akan melakukan hal yang sama ?? Meskipun Anda membuat penutupan atau bukan itu masalah yang berbeda tetapi berlalunya nama setiap kali bukan masalah di sini.
user2906608

4

Jika Anda nyaman dengan konsep instantiating kelas dalam arti berorientasi objek (yaitu untuk membuat objek kelas itu) maka Anda sudah dekat dengan pemahaman penutupan.

Pikirkan seperti ini: ketika Anda instantiate dua objek Person Anda tahu bahwa variabel anggota kelas "Nama" tidak dibagi antara instance; setiap objek memiliki 'salinan' sendiri. Demikian pula, ketika Anda membuat penutupan, variabel bebas ('dipanggilCount' dalam contoh Anda di atas) terikat ke 'instance' dari fungsi.

Saya pikir lompatan konseptual Anda sedikit terhambat oleh kenyataan bahwa setiap fungsi / penutupan dikembalikan oleh fungsi warnUser (selain: itu adalah fungsi tingkat tinggi ) penutupan mengikat 'disebutCount' dengan nilai awal yang sama (0), sedangkan sering ketika membuat penutupan lebih berguna untuk melewatkan inisialisasi yang berbeda ke dalam fungsi orde tinggi, seperti memberikan nilai yang berbeda ke konstruktor suatu kelas.

Jadi, anggaplah ketika 'dipanggilCount' mencapai nilai tertentu yang Anda ingin mengakhiri sesi pengguna; Anda mungkin menginginkan nilai yang berbeda untuk itu tergantung pada apakah permintaan datang dari jaringan lokal atau internet buruk yang besar (ya, itu adalah contoh yang dibuat-buat). Untuk mencapai ini, Anda bisa meneruskan nilai awal yang berbeda untuk dipanggilCount ke warnUser (yaitu -3, atau 0?).

Bagian dari masalah dengan literatur adalah nomenklatur yang digunakan untuk menggambarkan mereka ("ruang lingkup leksikal", "variabel bebas"). Jangan biarkan itu membodohi Anda, penutupan lebih sederhana daripada yang akan muncul ... prima facie ;-)


3

Di sini saya punya satu contoh sederhana konsep penutupan yang dapat kita gunakan di situs E-commerce kami atau banyak lainnya juga. Saya menambahkan tautan jsfiddle saya dengan contoh. itu berisi daftar produk kecil 3 item dan satu gerobak gerobak.

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

Penggunaan Penutupan:

Penutupan adalah salah satu fitur paling kuat dari JavaScript. JavaScript memungkinkan untuk fungsi bersarang dan memberikan fungsi penuh akses penuh ke fungsi dalam semua variabel dan fungsi yang didefinisikan di dalam fungsi luar (dan semua variabel dan fungsi lain yang memiliki fungsi akses luar). Namun, fungsi luar tidak memiliki akses ke variabel dan fungsi yang didefinisikan di dalam fungsi dalam. Ini memberikan semacam keamanan untuk variabel fungsi dalam. Juga, karena fungsi dalam memiliki akses ke ruang lingkup fungsi luar, variabel dan fungsi yang didefinisikan dalam fungsi luar akan hidup lebih lama daripada fungsi luar itu sendiri, jika fungsi dalam berhasil bertahan hidup di luar kehidupan fungsi luar.

Contoh:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

Dalam kode di atas, variabel nama dari fungsi luar dapat diakses oleh fungsi dalam, dan tidak ada cara lain untuk mengakses variabel dalam kecuali melalui fungsi dalam. Variabel-variabel dalam dari fungsi dalam bertindak sebagai simpanan yang aman untuk fungsi-fungsi dalam. Mereka menyimpan "persisten", namun aman, data untuk fungsi internal untuk bekerja dengannya. Fungsi-fungsinya bahkan tidak harus ditugaskan ke variabel, atau memiliki nama. baca di sini untuk detailnya


2

Saya suka contoh fungsi pabrik Mozilla .

function makeAdder(x) {

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

var addFive = makeAdder(5);

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

11
Ini adalah jenis contoh yang tidak membantu orang memahami penutupan atau apa gunanya, menurut saya. Berapa kali Anda pernah menulis penutupan untuk mengembalikan fungsi untuk menambahkan angka, selain sebagai contoh?
Mohamad

2

Pola modul JavaScript menggunakan penutupan. Pola yang bagus memungkinkan Anda untuk memiliki sesuatu yang sama "publik" dan "pribadi" vars.

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

Utas ini sangat membantu saya dalam mendapatkan pemahaman yang lebih baik tentang bagaimana penutupan bekerja. Sejak itu saya telah melakukan beberapa eksperimen sendiri dan menghasilkan kode yang cukup sederhana ini yang dapat membantu beberapa orang melihat bagaimana penutupan dapat digunakan dengan cara yang praktis dan bagaimana menggunakan penutupan pada tingkat yang berbeda untuk mempertahankan variabel yang mirip dengan statis dan / atau variabel global tanpa risiko mereka ditimpa atau dikacaukan dengan variabel global. Apa yang dilakukan adalah melacak klik tombol, baik di tingkat lokal untuk setiap tombol individu dan tingkat global, menghitung setiap klik tombol, berkontribusi terhadap satu angka. Catatan Saya belum pernah menggunakan variabel global untuk melakukan ini, yang merupakan inti dari latihan - memiliki penangan yang dapat diterapkan pada tombol apa pun yang juga berkontribusi pada sesuatu secara global.

Tolong para ahli, beri tahu saya jika saya melakukan praktik buruk di sini! Saya sendiri masih mempelajari hal ini.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

Referensi: Penggunaan praktis penutupan

Dalam praktiknya, penutupan dapat membuat desain yang elegan, memungkinkan penyesuaian berbagai perhitungan, panggilan yang ditangguhkan, panggilan balik, membuat lingkup yang dienkapsulasi dll.

Contoh metode pengurutan array yang menerima sebagai argumen fungsi pengurutan kondisi:

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

Memetakan fungsional sebagai metode peta array yang memetakan array baru dengan kondisi argumen fungsional:

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

Seringkali lebih mudah untuk mengimplementasikan fungsi pencarian dengan menggunakan argumen fungsional yang mendefinisikan kondisi pencarian yang hampir tidak terbatas:

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

Selain itu, kami dapat mencatat penerapan fungsi sebagai, misalnya, metode forEach yang menerapkan fungsi ke array elemen:

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

Fungsi diterapkan pada argumen (ke daftar argumen - berlaku, dan argumen diposisikan - dalam panggilan):

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

Panggilan yang ditangguhkan:

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

Fungsi panggilan balik:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Pembuatan lingkup enkapsulasi untuk tujuan menyembunyikan objek tambahan:

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

0

Sebagian besar kode yang kami tulis di JavaScript front-end berbasis acara - kami mendefinisikan beberapa perilaku, lalu melampirkannya ke acara yang dipicu oleh pengguna (seperti klik atau penekanan tombol). Kode kami umumnya dilampirkan sebagai panggilan balik: satu fungsi yang dijalankan sebagai respons terhadap peristiwa tersebut. size12, size14, dan size16 sekarang fungsi yang akan mengubah ukuran teks tubuh menjadi 12, 14, dan 16 piksel, masing-masing. Kami dapat melampirkannya ke tombol (dalam hal ini tautan) sebagai berikut:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

Biola


Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa memecahkan masalah akan meningkatkan nilai jangka panjang jawaban.
Donald Duck

1
contoh ini, menurut saya, dapat diimplementasikan tanpa penutupan melalui fungsi standar. Saya mencoba menemukan contoh sesuatu yang TIDAK BISA diimplementasikan tanpa penutupan
Zach Smith

0

Penutupan adalah cara yang berguna untuk membuat , urutan yang bertambah saat diminta:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

Perbedaannya diringkas sebagai berikut:

Fungsi anonim Fungsi yang ditentukan

Tidak dapat digunakan sebagai metode. Dapat digunakan sebagai metode suatu objek

Hanya ada dalam ruang lingkup yang didefinisikan Ada dalam objek itu didefinisikan

Hanya dapat dipanggil dalam lingkup yang didefinisikan Dapat dipanggil pada titik mana pun dalam kode

Dapat ditugaskan ulang nilai baru atau dihapus Tidak dapat dihapus atau diubah

Referensi


0

Dalam sampel yang diberikan nilai 'counter' variabel terlampir dilindungi dan hanya dapat diubah dengan menggunakan fungsi yang diberikan (increment, decrement). karena itu dalam penutupan,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

Menjelaskan penggunaan praktis untuk Penutupan dalam JavaScript ---

Saat kami membuat fungsi di dalam fungsi lain, kami membuat penutupan. Penutupan sangat kuat karena mereka mampu membaca dan memanipulasi data fungsi luarnya. Setiap kali fungsi dipanggil, ruang lingkup baru dibuat untuk panggilan itu. Variabel lokal yang dideklarasikan di dalam fungsi milik lingkup itu dan mereka hanya dapat diakses dari fungsi itu. Ketika fungsi telah menyelesaikan eksekusi, ruang lingkup biasanya dihancurkan.

Contoh sederhana dari fungsi tersebut adalah ini:

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

Dalam contoh di atas, fungsi buildName () mendeklarasikan salam variabel lokal dan mengembalikannya. Setiap panggilan fungsi menciptakan ruang lingkup baru dengan variabel lokal baru. Setelah fungsi selesai dieksekusi, kami tidak memiliki cara untuk merujuk ke lingkup itu lagi, jadi itu mengumpulkan sampah.

Tapi bagaimana kalau kita memiliki tautan ke ruang lingkup itu?

Mari kita lihat fungsi selanjutnya:

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

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

Fungsi sayName () dari contoh ini adalah penutupan. Fungsi sayName () memiliki cakupan lokalnya sendiri (dengan variabel sambutan) dan juga memiliki akses ke lingkup fungsi luar (melampirkan). Dalam hal ini, salam variabel dari buildName ().

Setelah eksekusi buildName dilakukan, ruang lingkup tidak dihancurkan dalam kasus ini. Fungsi sayMyName () masih memiliki akses ke sana, jadi itu tidak akan menjadi sampah yang dikumpulkan. Namun, tidak ada cara lain untuk mengakses data dari lingkup luar kecuali penutupan. Penutupan berfungsi sebagai pintu gerbang antara konteks global dan lingkup luar.


0

Saya mencoba mempelajari clousures dan saya pikir contoh yang saya buat adalah use case yang praktis. Anda dapat menjalankan cuplikan dan melihat hasilnya di konsol. kami memiliki dua pengguna terpisah yang memiliki data terpisah. Masing-masing dari mereka dapat melihat keadaan aktual dan memperbaruinya.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.