Po co definiować anonimową funkcję i przekazywać ją jako argument jQuery?

98

Przeglądam doskonały kod demonstracyjny peepcode z screencastów backbone.js. W nim cały kod szkieletu jest zawarty w anonimowej funkcji, do której jest przekazywany obiekt jQuery:

(function($) {
  // Backbone code in here
})(jQuery);

W moim własnym kodzie szkieletowym właśnie opakowałem cały mój kod w zdarzenie „ready” jQuery DOM:

$(function(){
  // Backbone code in here
});

Jaki jest sens / zaleta pierwszego podejścia? Robiąc to w ten sposób, tworzymy anonimową funkcję, która jest następnie wykonywana natychmiastowo z przekazaniem obiektu jQuery jako argumentu funkcji, skutecznie zapewniając, że $ jest obiektem jQuery. Czy to jedyny punkt - aby zagwarantować, że jQuery jest powiązany z '$', czy są inne powody, aby to zrobić?

Matt Roberts
źródło
4
Zamiast tego powinieneś najpierw przejrzeć SO.
Alexander,
Interoperacyjność z innymi bibliotekami - jeśli autor strony musi skorzystać $.noConflict(), pierwszy przykład nadal będzie działał.
DCoder
Możliwy duplikat: jQuery i $ questions
Alexander
Zobacz możliwy zduplikowany dokument jQuery
Bergi.
@Alexander, ale wtedy inni ludzie najpierw znajdą to pytanie na SO. :-)
caiosm1005

Odpowiedzi:

182

Dwa bloki kodu, które pokazałeś, różnią się diametralnie, kiedy i dlaczego są wykonywane. Nie wykluczają się wzajemnie. Nie służą temu samemu celowi.

Moduły JavaScript


(function($) {
  // Backbone code in here
})(jQuery);

Jest to wzorzec „modułu JavaScript”, zaimplementowany z funkcją natychmiastowego wywołania.

Celem tego kodu jest zapewnienie „modułowości”, prywatności i hermetyzacji kodu.

Implementacja tego jest funkcją, która jest natychmiast wywoływana przez (jQuery)nawias wywołujący . Celem przekazania jQuery w nawiasie jest zapewnienie lokalnego zakresu dla zmiennej globalnej. Pomaga to zmniejszyć obciążenie związane z wyszukiwaniem $zmiennej i pozwala w niektórych przypadkach na lepszą kompresję / optymalizację dla minifier.

Natychmiastowe wywołanie funkcji jest wykonywane, no cóż, natychmiast. Jak tylko definicja funkcji jest kompletna, funkcja jest wykonywana.

Funkcja „DOMReady” jQuery

To jest alias funkcji „DOMReady” jQuery: http://api.jquery.com/ready/


$(function(){
  // Backbone code in here
});

Funkcja „DOMReady” jQuery jest wykonywana, gdy DOM jest gotowy do manipulowania przez kod JavaScript.

Moduły vs DOMReady w kodzie szkieletowym

Definiowanie kodu Backbone wewnątrz funkcji DOMReady jQuery jest złą formą i może potencjalnie szkodzić wydajności aplikacji. Ta funkcja nie jest wywoływana, dopóki DOM nie zostanie załadowany i nie będzie gotowy do manipulacji. Oznacza to, że czekasz, aż przeglądarka co najmniej raz przeanalizuje model DOM, zanim zdefiniujesz swoje obiekty.

Lepszym pomysłem jest zdefiniowanie obiektów Backbone poza funkcją DOMReady. Między innymi ja wolę to robić wewnątrz wzorca modułu JavaScript, aby zapewnić hermetyzację i prywatność mojego kodu. Zwykle używam wzorca „Revealing Module” (zobacz pierwszy link powyżej), aby zapewnić dostęp do potrzebnych mi bitów poza moim modułem.

Definiując swoje obiekty poza funkcją DOMReady i udostępniając sposób odniesienia się do nich, pozwalasz przeglądarce uzyskać przewagę w przetwarzaniu JavaScript, potencjalnie przyspieszając pracę użytkownika. Dzięki temu kod jest bardziej elastyczny, ponieważ można przenosić rzeczy bez martwienia się o tworzenie większej liczby funkcji DOMREady podczas przenoszenia rzeczy.

Prawdopodobnie użyjesz funkcji DOMReady, nawet jeśli zdefiniujesz swoje obiekty Backbone gdzie indziej. Powodem jest to, że wiele aplikacji Backbone musi w jakiś sposób manipulować DOM. Aby to zrobić, musisz poczekać, aż DOM będzie gotowy, dlatego musisz użyć funkcji DOMReady, aby uruchomić aplikację po jej zdefiniowaniu.

W Internecie można znaleźć wiele przykładów takiej sytuacji, ale oto bardzo podstawowa implementacja, wykorzystująca zarówno moduł, jak i funkcję DOMReady:



// Define "MyApp" as a revealing module

MyApp = (function(Backbone, $){

  var View = Backbone.View.extend({
    // do stuff here  
  });

  return {
    init: function(){
      var view = new View();
      $("#some-div").html(view.render().el);
    }
  };

})(Backbone, jQuery);



// Run "MyApp" in DOMReady

$(function(){
  MyApp.init();
});
Derick Bailey
źródło
1
Dzięki za tę szczegółową odpowiedź. Wiedziałem o funkcji DOMReady wywołującej tylko wtedy, gdy uruchomiono zdarzenie DOM ready, ale nigdy tak naprawdę nie myślałem, że to może być problem. Podział kodu na zdefiniowanie bitów szkieletu wewnątrz modułu, a następnie interakcja z nimi w gotowym dom rzeczywiście wydaje się najlepszym podejściem
Matt Roberts
2
Co ciekawe, przykładowa aplikacja „todo” z plikiem backbone src ustawia wszystko w dom.
Matt Roberts,
2
Nie zapomnij, że wzorzec modułu javascript jest również nazywany IIFE.
Jess
1
Funkcje anonimowe są zasadniczo wykonywane w tym samym czasie co DOM ready, więc w jaki sposób czyni je bardziej wydajnymi?
bhavya_w
14

Na marginesie, wysłanie $ jako argumentu do funkcji anonimowej powoduje, że $ jest lokalna dla tej funkcji, co ma niewielki pozytywny wpływ na wydajność, jeśli funkcja $ jest często wywoływana. Dzieje się tak, ponieważ javascript najpierw przeszukuje lokalny zasięg w poszukiwaniu zmiennych, a następnie przechodzi w dół aż do zakresu okna (gdzie zwykle znajduje się $).

joidegn
źródło
9

Zapewnia, że zawsze możesz użyć $tego zamknięcia, nawet jeśli $.noConflict()zostało użyte.

Bez tego zamknięcia należałoby używać jQueryzamiast $przez cały czas.

ThiefMaster
źródło
2

Użyj obu.

Funkcja auto-wywołująca, w której przekazujesz jQuery, aby zapobiec konfliktom bibliotek i po prostu upewnić się, że jQuery jest dostępne zgodnie z oczekiwaniami z $.

I metoda skrótu .ready () wymagana do uruchomienia javascript tylko po załadowaniu DOM:

(function($) {
    $(function(){
          //add code here that needs to wait for page to be loaded
    });

    //and rest of code here
})(jQuery);
Andrzej
źródło
Krótsza wersja, którą znalazłem gdzie indziej na SO (również chroni niezdefiniowane) :jQuery(function ($, undefined) { /* Code */ });
Jared Gotte