function foo(a) {
if (/* Some condition */) {
// perform task 1
// perform task 3
}
else {
// perform task 2
// perform task 3
}
}
Mam funkcję, której struktura jest podobna do powyższej. Chcę wyodrębnić zadanie 3 do funkcji, bar()
ale chcę ograniczyć dostęp do tej funkcji tylko do zakresu foo(a)
.
Aby osiągnąć to, czego chcę, czy słuszne jest przejście na następujące?
function foo(a) {
function bar() {
// Perform task 3
}
if (/* Some condition */) {
// Perform task 1
bar();
}
else {
// Perform task 2
bar();
}
}
Jeśli powyższe jest poprawne, czy za bar()
każdym foo(a)
wywołaniem jest zmieniane? (Martwię się tutaj o marnowanie zasobów procesora).
javascript
tamakisquare
źródło
źródło
Odpowiedzi:
Tak, to, co tam masz, jest właściwe. Kilka uwag:
bar
jest tworzony przy każdym wywołaniu funkcjifoo
, ale:bar
, co się dzieje, niektóre silniki mogą określić, że mogą to „wbudować”, całkowicie eliminując wywołanie funkcji. V8 to robi i jestem pewien, że to nie jedyny silnik, który to robi. Oczywiście mogą to zrobić tylko wtedy, gdy nie zmienia to zachowania kodu.bar
tworzenia za każdym razem będzie się znacznie różnić w zależności od silnika JavaScript. Jeślibar
jest trywialny, będzie się różnić od niewykrywalnego do dość małego. Jeśli nie dzwoniszfoo
tysiące razy z rzędu (na przykład odmousemove
obsługi), nie martwiłbym się tym. Nawet jeśli tak, martwiłbym się tylko, gdybym zobaczył problem z wolniejszymi silnikami. Oto przypadek testowy obejmujący operacje DOM , który sugeruje, że jest wpływ, ale trywialny (prawdopodobnie wyparty przez rzeczy DOM). Oto przypadek testowy wykonujący czyste obliczenia, które pokazują znacznie większy wpływ, ale szczerze mówiąc nawet, mówimy o różnicy mikro sekund, ponieważ nawet 92% wzrost w przypadku czegoś, co wymaga mikrosekundy, które mają nastąpić, są nadal bardzo, bardzo szybkie. Dopóki nie zobaczysz rzeczywistego wpływu, nie ma się czym martwić.bar
będzie dostępny tylko z poziomu funkcji i będzie miał dostęp do wszystkich zmiennych i argumentów dla tego wywołania funkcji. To sprawia, że jest to bardzo poręczny wzór.źródło
bar
jest tworzona przy każdym wywołaniufoo
)foo
. Jeśli nie dzwoniszfoo
tysiące razy z rzędu (na przykład nie w programiemousemove
obsługi), nie martwiłbym się tym w ogóle. Zwróć uwagę, że niektóre silniki (na przykład V8) i tak wbudują kod, całkowicie eliminując wywołanie funkcji, pod warunkiem, że nie zmieni to tego, co się dzieje w sposób, który można wykryć zewnętrznie.bar()
się każdego połączenia? również, czy użyciefoo.prototype.bar
do zdefiniowania funkcji pomogłoby każdemu?bar
dostęp do zmiennych i argumentów dla wywołaniafoo
(cokolwiek chcesz, żeby działało, musisz to przekazać), co może trochę skomplikować sprawę, ale w krytycznej dla wydajności sytuacji, w której masz Widziałeś rzeczywisty problem, możesz dokonać takiej zmiany, aby zobaczyć, czy to rozwiązuje problem. Nie, używaniefoo.prototype
nie pomogłoby (po pierwsze,bar
nie byłoby już prywatne).Po to są zamknięcia.
var foo = (function () { function bar() { // perform task 3 }; function innerfoo (a) { if (/* some cond */ ) { // perform task 1 bar(); } else { // perform task 2 bar(); } } return innerfoo; })();
Innerfoo (zamknięcie) przechowuje odniesienie do baru, a anonimowa funkcja, która jest wywoływana tylko raz, aby utworzyć zamknięcie, zwraca tylko odwołanie do innerfoo.
W ten sposób bar nie jest dostępny z zewnątrz.
źródło
var foo = (function () { var bar = function () { // perform task 3 } return function (a) { if (/*some condition*/) { // perform task 1 bar(); } else { // perform task 2 bar(); } }; }());
Zamknięcie zachowuje zakres
bar()
zawarty, zwracając nową funkcję z samowykonującej się funkcji anonimowej ustawia bardziej widoczny zakres nafoo()
. Anonimowa funkcja samowykonująca się jest uruchamiana dokładnie raz, więc jest tylko jednabar()
instancja i każde wykonaniefoo()
będzie jej używać.źródło
Tak, to działa dobrze.
Funkcja wewnętrzna nie jest odtwarzana za każdym razem, gdy wchodzisz do funkcji zewnętrznej, ale jest ponownie przypisywana.
Jeśli przetestujesz ten kod:
function test() { function demo() { alert('1'); } demo(); demo = function() { alert('2'); }; demo(); } test(); test();
pokaże
1
,2
,1
,2
, nie1
,2
,2
,2
.źródło
demo()
każdym razemtest()
powinna być spowodowana wydajnością? Czy to zależy od złożonościdemo()
?Utworzyłem jsperf, aby przetestować zagnieżdżone i niezagnieżdżone wyrażenia funkcyjne i deklaracje funkcji i byłem zaskoczony, widząc, że zagnieżdżone przypadki testowe działały 20 razy szybciej niż niezagnieżdżone. (Spodziewałem się albo przeciwnych, albo nieistotnych różnic).
https://jsperf.com/nested-functions-vs-not-nested-2/1
To jest w Chrome 76, macOS.
źródło