function counter () {
var num = 0;
return {
add_one: function () {
num += 1;
return num;
},
get_num: function () {
return num;
}
};
}
var my_counter = counter();
console.log( my_counter.get_num() );
console.log( my_counter.add_one() );
Proste i zrozumiałe. Dwie ciekawe rzeczy dotyczące tego rozwiązania są takie:
- pomimo tego, że funkcja counter kończy swoje działanie po utworzeniu i zwróceniu nowego obiektu, zmienna num przechowywana jest w pamięci, jako że obie metody nowopowstałego obiektu mają do niej dostęp (to właśnie jest domknięcie)
- zmienna num dostępna jest jedynie za pomocą dwóch metod nowopowstałego obiektu, co czyni ją prywatną. W ten sposób zwracany obiekt staje się bardzo czytelnym i stuprocentowo szczelnym interfejsem dla stanu przechowywanego w domknięciu.
Jest to wzorzec asynchronicznego dostępu do danych, który pojawił się w Raw Salad. Rzeczy mają się tak. Jest moduł _gui, który odpowiedzialny jest za obsługę zdarzeń w... GUI, moduł _store, który przechowuje aktualny stan aplikacji oraz moduł _db, który w razie potrzeby dociąga dane z bazy. Komunikacja wygląda zatem tak: na klik wyciągam dane ze store'a i wyświetlam je w określony sposób. Jeśli danych nie ma w storze, zaciągam je z bazy, dodaję do store'a i wtedy wyświetlam. W drugim przypadku nie wiadomo, co stanie się na łączach i ile przyjdzie nam czekać na odpowiedź z bazy. Co więcej moduł _gui nie jest w stanie określić z góry, co w storze jest a czego jeszcze nie ma. Zresztą, czy go to powinno interesować? W konsekwencji zatem _gui komunikuje się ze _storem za pomocą takiego callbacka:
_gui.js
$('#more-data-please').click( function () {
var destination = $(this).parent();
var render_data = function ( from_store ) {
var new_data = $('<h1>'+from_store+'</h2>');
destination.append( new_data );
});
_store.get_new_data( render_data );
});
_store.js
function get_new_data( callback ) {
if( data_in_store() ) {
callback( data_from_store );
}
else {
_db.get_more_data( function( new_data ) {
store_new_data( new_data );
callback( new_data );
});
}
}
_db.js
function get_more_data( callback ) {
$.get( '/more_data/', {}, callback );
}
Oczywiście pewne rzeczy zostały mocno uproszczone - chodzi o wzorzec, a nie konkretny przypadek. Ważne jest to, że dzięki domknięciu callback wysłany z _gui do modułu _store posiada referencję do obiektu destination, dzięki czemu jak tylko dostanie odpowiednie dane, będzie w stanie je wyświetlić. Ten prosty przykład pokazuje, że dzięki domknięciu udało się uzyskać sensowaną modularyzację kodu. Moduł _gui wie, że chce danych, wie jak je wyświetlić i myśli, że są one w storze. Moduł store nie ma pojęcia kto i po co prosi go o dane, wie natomiast, że wraz z zapytaniem przyszła czarna skrzynka z napisem "Do the magic" oraz że jeśli zabraknie mu danych to moduł _db wie skąd je wziąć. Moduł _db natomiast nie wie, kto i po co prosi go o wyjęcie danych z bazy, wie natomiast, jak zagadać do serwera, żeby te dane dostać oraz ma skrzynkę z napisem "All the magic here". Innymi słowy: w drodze do swojego gabinetu Staszek prosi Janka o kilka faktur; nie ma czasu czekać, bo zaczyna spotkanie, więc zostawia Franka, któremu Janek przekaże te dokumenty, a on już wie, co z nimi zrobić. Ponieważ dwóch faktur nie było, więc Janek prosi Panią Gabrysię o dodatkowe dokumenty. Ostatecznie, kiedy ma już wszystko daje je Frankowi, który idzie do komputera i robi na podstawie tych faktur wykresy potrzebne Staszkowi w dalszej części spotkania, bo ten zostawił mu na swoim biurku pendrive'a z resztą prezentacji. Życiowa sprawa te domknięcia i callbacki...