Czy mogę nazwać funkcję JavaScript i wykonać ją natychmiast?


90

Mam kilka z nich:

function addEventsAndStuff() {
  // bla bla
}
addEventsAndStuff();

function sendStuffToServer() {
  // send stuff
  // get HTML in response
  // replace DOM
  // add events:
  addEventsAndStuff();
}

Ponowne dodanie wydarzeń jest konieczne, ponieważ DOM się zmienił, więc wcześniej dołączone zdarzenia zniknęły. Ponieważ muszą być również przymocowane na początku (duh), są w fajnej funkcji, aby być SUCHE.

Nie ma nic złego w tej konfiguracji (czy jest?), Ale czy mogę trochę wygładzić? Chciałbym stworzyć addEventsAndStuff()funkcję i od razu ją nazwać, żeby nie wyglądała tak amatorsko.

Oba poniższe odpowiedzi odpowiadają błędem składni:

function addEventsAndStuff() {
  alert('oele');
}();

(function addEventsAndStuff() {
  alert('oele');
})();

Jacyś chętni?



2
Nie, @CristianSanchez.
Máxima Alekz

Imho twój drugi blok kodu jest trudny do odczytania. W pierwszym bloku kodu dla każdego czytelnika jest jasne, że wywołujesz funkcję po init. Czytelnicy mają tendencję do ignorowania zamykających i zastępujących nawiasy na końcu.
kaiser

Odpowiedzi:


123

Nie ma nic złego w przykładzie, który zamieściłeś w swoim pytaniu. Inna metoda może wyglądać dziwnie, ale:

var addEventsAndStuff;
(addEventsAndStuff = function(){
    // add events, and ... stuff
})();

Istnieją dwa sposoby definiowania funkcji w JavaScript. Deklaracja funkcji:

function foo(){ ... }

oraz wyrażenie funkcyjne, które jest dowolnym sposobem zdefiniowania funkcji innej niż powyższe:

var foo = function(){};
(function(){})();
var foo = {bar : function(){}};

...itp

wyrażeniom funkcyjnym można nadawać nazwy, ale ich nazwa nie jest propagowana do zakresu zawierającego. Znaczenie tego kodu jest ważne:

(function foo(){
   foo(); // recursion for some reason
}());

ale to nie jest:

(function foo(){
    ...
}());
foo(); // foo does not exist

Aby więc nazwać swoją funkcję i natychmiast ją wywołać, musisz zdefiniować zmienną lokalną, przypisać jej funkcję jako wyrażenie, a następnie ją wywołać.


1
„Wyrażenia funkcyjne można nazwać„ Uważaj na nazwane wyrażenia funkcyjne. Oni powinni pracować jak opisujesz, ale nie na IE przed IE9 - masz dwie funkcje, a przecieki nazwa funkcji. Szczegóły: kangax.github.com/nfe (zostało to ostatecznie naprawione w IE9).
TJ Crowder,

@TJ - dzięki za odniesienie. Nie zdawałem sobie z tego sprawy, ponieważ nigdy nie używam IE z wyjątkiem testowania produktów końcowych :) Zakładam, że IE9 nie emuluje tego błędu, ustawiając go w trybie IE7 / 8? Myślę, że nadal używa silnika IE9 JS ... Meh
Mark Kahn

Jeśli, tak jak ja, napotkasz problemy z jshint z tą metodą, możesz użyć call()metody z tego miejsca: stackoverflow.com/a/12359154/1231001
cfx

To podejście może wydawać się przydatne w pewnych okolicznościach, ale oprócz tego, że jest trochę trudniejsze do odczytania, wydaje się, że zawiera praktycznie taką samą ilość tekstu.
Vladimir

16

Jest na to dobry skrót (nie trzeba deklarować żadnych zmiennych poza przypisaniem funkcji):

var func = (function f(a) { console.log(a); return f; })('Blammo')

Która rfunkcja zwróci? Czy function rlub var r? Po prostu ciekawy. Dobre rozwiązanie. Nie musiał to być jednak jeden loc =)
Rudie

Zmieniłem nazwę, aby była bardziej przejrzysta, ale return rbyłby taki, function rponieważ był to jego zakres. Pisałem Scalę tak nieustannie, próbując znaleźć coś na linii.
James Gorrie

@JamesGorrie, genialny skrót, jednak próbowałem dwa razy w konsoli, wygląda na to, że func, func (), func () () wszystkie zwracają deklarację funkcji f (), ale czy możemy wyświetlić 'Blammo' przez func () zgodnie z oczekiwaniami, proszę :)?
mike652638

@ mike652638 Uruchomiłem to w mojej konsoli i konsola rejestruje blammo?
James Gorrie,

5

Nie ma nic złego w tej konfiguracji (czy jest?), Ale czy mogę trochę wygładzić?

Zamiast tego spójrz na użycie delegowania zdarzeń. W tym miejscu faktycznie obserwujesz zdarzenie w kontenerze, który nie znika, a następnie używasz event.target(lub event.srcElementw IE), aby dowiedzieć się, gdzie zdarzenie faktycznie miało miejsce i poprawnie je obsłużyć.

W ten sposób dołączasz moduły obsługi tylko raz, a one po prostu działają nawet po wymianie treści.

Oto przykład delegowania zdarzeń bez użycia bibliotek pomocniczych:

(function() {
  var handlers = {};

  if (document.body.addEventListener) {
    document.body.addEventListener('click', handleBodyClick, false);
  }
  else if (document.body.attachEvent) {
    document.body.attachEvent('onclick', handleBodyClick);
  }
  else {
    document.body.onclick = handleBodyClick;
  }

  handlers.button1 = function() {
    display("Button One clicked");
    return false;
  };
  handlers.button2 = function() {
    display("Button Two clicked");
    return false;
  };
  handlers.outerDiv = function() {
    display("Outer div clicked");
    return false;
  };
  handlers.innerDiv1 = function() {
    display("Inner div 1 clicked, not cancelling event");
  };
  handlers.innerDiv2 = function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  };

  function handleBodyClick(event) {
    var target, handler;

    event = event || window.event;
    target = event.target || event.srcElement;

    while (target && target !== this) {
      if (target.id) {
        handler = handlers[target.id];
        if (handler) {
          if (handler.call(this, event) === false) {
            if (event.preventDefault) {
              event.preventDefault();
            }
            return false;
          }
        }
      }
      else if (target.tagName === "P") {
        display("You clicked the message '" + target.innerHTML + "'");
      }
      target = target.parentNode;
    }
  }

  function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
  }

})();

Przykład na żywo

Zwróć uwagę, że jeśli klikniesz wiadomości, które są dynamicznie dodawane do strony, kliknięcie zostanie zarejestrowane i obsługiwane, nawet jeśli nie ma kodu, który przechwyciłby zdarzenia na dodawanych nowych akapitach. Zwróć również uwagę, że twoje programy obsługi są tylko wpisami na mapie, a masz jeden moduł obsługujący, document.bodyktóry wykonuje całą wysyłkę. Prawdopodobnie zakorzenisz to w czymś bardziej ukierunkowanym niż document.body, ale masz pomysł. Ponadto w powyższym zasadniczo wysyłamy id, ale możesz dopasować tak złożone lub proste, jak chcesz.

Nowoczesne biblioteki JavaScript, takie jak jQuery , Prototype , YUI , Closure lub dowolne inne, powinny oferować funkcje delegowania zdarzeń, aby wygładzić różnice w przeglądarkach i czysto obsługiwać skrajne przypadki. jQuery z pewnością to robi, zarówno z jego funkcjami, jak livei delegate, które pozwalają określić moduły obsługi przy użyciu pełnego zakresu selektorów CSS3 (a także niektórych).

Na przykład, oto równoważny kod używający jQuery (z wyjątkiem tego, że jestem pewien, że jQuery obsługuje przypadki krawędziowe, które nie są dostępne w surowej wersji powyżej):

(function($) {

  $("#button1").live('click', function() {
    display("Button One clicked");
    return false;
  });
  $("#button2").live('click', function() {
    display("Button Two clicked");
    return false;
  });
  $("#outerDiv").live('click', function() {
    display("Outer div clicked");
    return false;
  });
  $("#innerDiv1").live('click', function() {
    display("Inner div 1 clicked, not cancelling event");
  });
  $("#innerDiv2").live('click', function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  });
  $("p").live('click', function() {
    display("You clicked the message '" + this.innerHTML + "'");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

})(jQuery);

Kopia na żywo


Nie chcę używać Event.target, ponieważ to nie zawsze jest właściwy cel. Zgadza się. Jeśli klikniesz IMGwewnątrz a, DIVa DIVma zdarzenie, Event.targetzostanie wyświetlone IMGi nie ma sposobu, aby DIVładnie uzyskać obiekt. edytuj Btw I hate jQuery's .live'event'
Rudie

@Rudie: Re event.target: To, co opisujesz, nie jest złe, jest poprawne. Jeśli klikniesz imgwewnątrz a divi oglądasz div, event.targetto jest img(i thisjest div). Spójrz ponownie na powyższe. Możesz dołączyć moduł obsługi w dowolnym punkcie łańcucha między klikniętym elementem ( imgna przykład) a elementem, który obserwujesz (powyżej, na samym dole document.body). Re live: Zdecydowanie wolę delegate, bardziej zamkniętą wersję live. Użyłem livepowyżej, ponieważ było to najbliższe temu, co zrobiłem w surowym przykładzie. Pozdrawiam
TJ Crowder

4

Możesz chcieć utworzyć taką funkcję pomocniczą:

function defineAndRun(name, func) {
    window[name] = func;
    func();
}

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});

2
Dlaczego jest to złe rozwiązanie? Ponieważ funkcje są zarejestrowane w globalnej przestrzeni nazw? Kto za tym głosował?
Rudie

Świetny pomysł
:)

4

Twój kod zawiera literówkę:

(function addEventsAndStuff() {
  alert('oele');
)/*typo here, should be }*/)();

więc

(function addEventsAndStuff() {
  alert('oele');
 })();

Pracuje. Twoje zdrowie!

[ edytuj ] na podstawie komentarza: a to powinno uruchomić i zwrócić funkcję za jednym razem:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);

Wszystkie głupie nawiasy wyglądają podobnie! Dzięki! Doprawdy!
Rudie

Tak ... Więc eh ... Właściwie to nie działa. Funkcja jest wykonywana natychmiastowo, tak, ale nie jest nigdzie zarejestrowana, więc wywołuję ją ponownie ->ReferenceError
Rudie

@Rudie: właśnie odkryłem, że KomodoEdit w końcu robi wszystko, co zawsze chciałem zrobić Aptana Studio (ale cały czas się zepsuło), a do tego jest wysoce konfigurowalny, szybki i pełen przydatnych dodatków. I podkreśla pasujące nawiasy: możesz nawet nadać tym pasującym nawiasom mocny czerwony kolor ostrzegawczy! Spróbuj, to nic nie kosztuje: activestate.com/komodo-edit
KooiInc

Koleś ... (I oczywiście wpisałem to w edytorze internetowym SO, a nie w edytorze desktopowym.)
Rudie

A to dużo dodatkowego pisania i pamiętania, co i jak pisać. Podobało mi się oryginalne (nie działające) rozwiązanie, ale nowe jest zbyt trudne =)
Rudie

2

Jeszcze prostsze z ES6:

var result = ((a, b) => `${a} ${b}`)('Hello','World')
// result = "Hello World"
var result2 = (a => a*2)(5)
// result2 = 10
var result3 = (concat_two = (a, b) => `${a} ${b}`)('Hello','World')
// result3 = "Hello World"
concat_two("My name", "is Foo")
// "My name is Foo"

Więc jak mam to ponownie nazwać? Jak to się nazywa? Zachowujesz tylko wynik, a nie funkcję.
Rudie

Ta metoda jest używana w niektórych przypadkach tylko wtedy, gdy musisz wywołać ją natychmiast i nie chcesz jej ponownie uruchamiać. W każdym razie zredaguję odpowiedź, aby pokazać, jak mogę ją ponownie wywołać, co o tym myślisz? @Rudie
Alberto

1
Moje pytanie dotyczyło nazwania go i ponownego uruchomienia. concat_twodziała idealnie, ALE zawsze definiuje to w zakresie globalnym, więc nie będzie działać "use strict". To nie jest dla mnie ważne, ale to mały minus.
Rudie

1

Jeśli chcesz utworzyć funkcję i wykonać ją natychmiast -

// this will create as well as execute the function a()
(a=function a() {alert("test");})();

// this will execute the function a() i.e. alert("test")
a();

1
Uważaj, kiedy używasz nazwanej funkcji jako wartości po prawej stronie (tak jak powyżej), używasz nazwanego wyrażenia funkcji, które chociaż powinno być poprawne, niestety ma problemy w różnych implementacjach (głównie - quelle shock - IE ). Szczegóły: kangax.github.com/nfe
TJ Crowder

0

Spróbuj zrobić tak:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();

1
To jest blisko, ale var foo = (function() {...})();wywołuje funkcję przed przypisaniem. Nawias musi zawierać przypisanie. (Chcesz przydzielić, a następnie zadzwoń.)
David

To także dużo pisania ORAZ pamiętania, co wpisać. W takim razie wolałbym po prostu skorzystać z moich bieżących połączeń.
Rudie
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.