Studiuję THREE.js i zauważyłem wzorzec, w którym funkcje są zdefiniowane w następujący sposób:
var foo = ( function () {
var bar = new Bar();
return function ( ) {
//actual logic using bar from above.
//return result;
};
}());
(Przykład patrz metoda raycast tutaj ).
Normalna zmienność w taki sposób będzie wyglądać następująco:
var foo = function () {
var bar = new Bar();
//actual logic.
//return result;
};
Porównując pierwszą wersję z normalną odmianą, pierwsza wydaje się różnić tym:
- Przypisuje wynik samowykonującej się funkcji.
- Definiuje zmienną lokalną w ramach tej funkcji.
- Zwraca rzeczywistą funkcję zawierającą logikę, która korzysta ze zmiennej lokalnej.
Zatem główna różnica polega na tym, że w pierwszej wariacji słupek jest przypisywany tylko raz, podczas inicjalizacji, podczas gdy druga zmiana tworzy tę zmienną tymczasową za każdym razem, gdy jest wywoływana.
Moje najlepsze przypuszczenie, dlaczego jest to używane, jest takie, że ogranicza liczbę wystąpień paska (będzie tylko jeden), a tym samym oszczędza obciążenie związane z zarządzaniem pamięcią.
Moje pytania:
- Czy to założenie jest słuszne?
- Czy jest jakaś nazwa tego wzoru?
- Dlaczego to jest używane?
javascript
closures
iife
Patrick Klug
źródło
źródło
Odpowiedzi:
Twoje założenia są prawie poprawne. Przejrzyjmy najpierw te.
Nazywa się to wyrażeniem funkcyjnym wywołanym natychmiast lub IIFE
Jest to sposób na posiadanie prywatnych pól obiektów w JavaScript, ponieważ w
private
inny sposób nie zapewnia słowa kluczowego ani funkcji.Ponownie, głównym punktem jest to, że ta zmienna lokalna jest prywatna .
AFAIK, możesz nazwać ten wzorzec wzorca modułu . Cytowanie:
Porównując te dwa przykłady, moje najlepsze przypuszczenia, dlaczego używany jest pierwszy z nich, to:
Ale jeśli za każdym razem potrzebujesz obiektu waniliowego, ten wzór prawdopodobnie nie doda żadnej wartości.
źródło
Ogranicza to koszty inicjalizacji obiektu i dodatkowo zapewnia, że wszystkie wywołania funkcji używają tego samego obiektu. Pozwala to na przykład na przechowywanie stanu w obiekcie do wykorzystania w przyszłości.
Chociaż możliwe jest, że ogranicza to użycie pamięci, zwykle GC i tak zbiera nieużywane obiekty, więc ten wzorzec raczej nie pomoże.
Ten wzór jest specyficzną formą zamknięcia .
źródło
Nie jestem pewien, czy ten wzorzec ma bardziej poprawną nazwę, ale dla mnie wygląda to na moduł, a powodem jego użycia jest zarówno hermetyzacja, jak i utrzymanie stanu.
Zamknięcie (identyfikowane przez funkcję w funkcji) zapewnia, że funkcja wewnętrzna ma dostęp do zmiennych w funkcji zewnętrznej.
W podanym przykładzie funkcja wewnętrzna jest zwracana (i przypisywana do
foo
) przez wykonanie funkcji zewnętrznej, co oznacza, żetmpObject
nadal istnieje w zamknięciu, a wiele wywołań funkcji wewnętrznejfoo()
będzie działać na tym samym wystąpieniutmpObject
.źródło
Kluczowa różnica między twoim kodem a kodem Three.js polega na tym, że w kodzie Three.js zmienna
tmpObject
jest inicjalizowana tylko raz, a następnie współdzielona przez każde wywołanie zwróconej funkcji.Byłoby to przydatne do utrzymywania stanu między wywołaniami, podobnie jak w
static
przypadku używania zmiennych w językach C-podobnych.tmpObject
jest zmienną prywatną, widoczną tylko dla funkcji wewnętrznej.Zmienia użycie pamięci, ale nie jest przeznaczony do oszczędzania pamięci.
źródło
Chciałbym wnieść swój wkład w ten interesujący wątek, rozszerzając koncepcję ujawniającego wzorca modułu, który zapewnia, że wszystkie metody i zmienne pozostają prywatne, dopóki nie zostaną jawnie ujawnione.
W tym drugim przypadku metoda dodawania nosiłaby nazwę Calculator.add ();
źródło
W podanym przykładzie pierwszy fragment kodu będzie używał tej samej instancji tmpObject dla każdego wywołania funkcji foo (), gdzie tak jak w drugim fragmencie, tmpObject będzie za każdym razem nową instancją.
Jednym z powodów, dla których mógł zostać użyty pierwszy fragment kodu, jest to, że zmienna tmpObject może być współużytkowana między wywołaniami funkcji foo (), bez wycieku jej wartości do zakresu, w którym jest zadeklarowana foo ().
Wersja funkcji pierwszego fragmentu kodu, która nie została natychmiast wykonana, wyglądałaby tak:
Zauważ jednak, że ta wersja ma tmpObject w tym samym zakresie co foo (), więc można nim później manipulować.
Lepszym sposobem na osiągnięcie tej samej funkcjonalności byłoby użycie osobnego modułu:
Moduł „foo.js”:
Moduł 2:
Porównanie wydajności IEF i nazwanej funkcji twórcy foo: http://jsperf.com/ief-vs-named-function
źródło