106

I have quite a few of these:

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

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

Re-adding the events is necessary because the DOM has changed, so previously attached events are gone. Since they have to be attached initially as well (duh), they're in a nice function to be DRY.

There's nothing wrong with this set up (or is there?), but can I smooth it a little bit? I'd like to create the addEventsAndStuff() function and immediately call it, so it doesn't look so amateuristic.

Both following respond with a syntax error:

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

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

Any takers?

informatik01
  • 16,038
  • 10
  • 74
  • 104
Rudie
  • 52,220
  • 42
  • 131
  • 173
  • possible duplicate of [Javascript self executing function](http://stackoverflow.com/questions/6090912/javascript-self-executing-function) – Cristian Sanchez Jun 19 '11 at 18:48
  • 2
    No, @CristianSanchez. – Máxima Alekz Nov 12 '16 at 01:15
  • Imho your second code block is hard to read. In the first code block it is clear for every reader, that you call the function after init. Readers tend to ignore closing and overread brackets at the end. – kaiser Nov 19 '18 at 01:41

9 Answers9

147

There's nothing wrong with the example you posted in your question.. The other way of doing it may look odd, but:

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

There are two ways to define a function in JavaScript. A function declaration:

function foo(){ ... }

and a function expression, which is any way of defining a function other than the above:

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

...etc

function expressions can be named, but their name is not propagated to the containing scope. Meaning this code is valid:

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

but this isn't:

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

So in order to name your function and immediately call it, you need to define a local variable, assign your function to it as an expression, then call it.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    *"function expressions can be named"* Beware of named function expressions. They *should* work as you describe, but they don't on IE prior to IE9 -- you get *two* functions, and the function name leaks. Details: http://kangax.github.com/nfe/ (This is **finally** fixed in IE9.) – T.J. Crowder Jun 19 '11 at 18:57
  • @TJ - thanks for the reference. I didn't realize that as I never use IE except to test final products :) I'm assuming that IE9 doesn't emulate this bug when setting it to IE7/8 mode? I think it still uses the IE9 JS engine... Meh –  Jun 19 '11 at 19:00
  • If, like me, you run into jshint issues with this method, you can use the `call()` method from here: http://stackoverflow.com/a/12359154/1231001 – cfx Feb 26 '15 at 17:43
  • This approach may seem useful in certain circumstances, but besides being a little harder to read, it seems to have virtually the same amount of text. – Vladimir Mar 26 '19 at 16:37
20

There is a good shorthand to this (not needing to declare any variables bar the assignment of the function):

var func = (function f(a) { console.log(a); return f; })('Blammo')
James Gorrie
  • 351
  • 2
  • 6
  • Which `r` will the function return? The `function r` or the `var r`? Just curious. Good solution. Didn't have to be one loc though =) – Rudie May 08 '14 at 18:07
  • I've renamed it to be more clear, but the `return r` would have been the `function r` as that's was it's scope. Been writting Scala so permanently trying to get something on oneline. – James Gorrie May 09 '14 at 13:37
  • @JamesGorrie, Brilliant shorthand, however, I tried twice in console, it seems func, func(), func()() all return function declaration of f(), but can we output 'Blammo' by func() as expected please :) ? – mike652638 Sep 04 '19 at 11:18
  • @mike652638 I've run that in my console and it console logs blammo? – James Gorrie Sep 10 '19 at 20:19
7

There's nothing wrong with this set up (or is there?), but can I smooth it a little bit?

Look at using event delegation instead. That's where you actually watch for the event on a container that doesn't go away, and then use event.target (or event.srcElement on IE) to figure out where the event actually occurred and handle it correctly.

That way, you only attach the handler(s) once, and they just keep working even when you swap out content.

Here's an example of event delegation without using any helper libs:

(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);
  }

})();

Live example

Note how if you click the messages that get dynamically added to the page, your click gets registered and handled even though there's no code to hook events on the new paragraphs being added. Also note how your handlers are just entries in a map, and you have one handler on the document.body that does all the dispatching. Now, you probably root this in something more targeted than document.body, but you get the idea. Also, in the above we're basically dispatching by id, but you can do matching as complex or simple as you like.

Modern JavaScript libraries like jQuery, Prototype, YUI, Closure, or any of several others should offer event delegation features to smooth over browser differences and handle edge cases cleanly. jQuery certainly does, with both its live and delegate functions, which allow you to specify handlers using a full range of CSS3 selectors (and then some).

For example, here's the equivalent code using jQuery (except I'm sure jQuery handles edge cases the off-the-cuff raw version above doesn't):

(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);

Live copy

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I don't want to use `Event.target`, because that's not alwaus the right target. That's right. If you click on a `IMG` inside a `DIV` and the `DIV` has the event, `Event.target` will be the `IMG` and there's no way to get the `DIV` object nicely. **edit** Btw I hate jQuery's `.live` 'event' – Rudie Jun 19 '11 at 22:03
  • @Rudie: Re `event.target`: What you describe isn't wrong, it's correct. If you click an `img` inside a `div` and you're watching the `div`, `event.target` is the `img` (and `this` is the `div`). Take another look at the above. You can attach a handler at any point in the chain between the element that was clicked (the `img`, for instance) and the element you're watching (in the above, all the way down at `document.body`). Re `live`: I vastly prefer `delegate`, the more contained version of `live`. I used `live` above because it was the closest to what I'd done in the raw example. Best, – T.J. Crowder Jun 19 '11 at 23:45
6

Your code contains a typo:

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

so

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

works. Cheers!

[edit] based on comment: and this should run and return the function in one go:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);
KooiInc
  • 119,216
  • 31
  • 141
  • 177
  • Stupid brackets all look alike!! Thanks! Cheers indeed! – Rudie Jun 19 '11 at 18:43
  • Yeah... So eh... Actually that doesn't work. The function is executed immediately, yes, but it's not registered anywhere, so calling it again -> `ReferenceError` – Rudie Jun 19 '11 at 18:45
  • @Rudie: just discovered that KomodoEdit finally does everything that I always wanted Aptana Studio to do (but breaked all the time), and on top of that it's highly configurable, fast and packed with useful addons. And it highlights matching brackets: you can even give those matching brackets a firm red warning color! Give it a try, it's free: http://www.activestate.com/komodo-edit – KooiInc Jun 19 '11 at 19:10
  • Dude... (And obviously I typed it in the SO web editor, not in a desktop editor.) – Rudie Jun 19 '11 at 22:01
  • And that's a lot of extra typing and remembering what and how to type. I liked the original (not working) solution, but the new one is too difficult =) – Rudie Jun 19 '11 at 22:07
4

Even simpler with 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"
Alberto
  • 1,423
  • 18
  • 32
  • So how would I call it again? What's its name, to call it by? You're only keeping the result, not the function. – Rudie Oct 24 '18 at 11:18
  • This method is used in some cases only if you need to call it inmediately and you don't want to rerun it. Anyways I will edit the answer to show how to be able to call it again, what do you think? @Rudie – Alberto Oct 24 '18 at 21:23
  • 1
    My question was about naming and rerunning it. `concat_two` works perfectly, BUT it always defines it in the global scope, so it won't work with `"use strict"`. That's not important for me, but it's a tiny downside. – Rudie Oct 25 '18 at 11:23
4

You might want to create a helper function like this:

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

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
pimvdb
  • 151,816
  • 78
  • 307
  • 352
2

Try to do like that:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Alexander Ruliov
  • 3,785
  • 3
  • 17
  • 18
  • 1
    This is close, but `var foo = (function() {...})();` calls the function before the assignment. The parenthesis needs to include the assignment. (You want to assign *then* call.) – David Jun 19 '11 at 18:25
  • That's also a lot of typing AND remembering what to type. Then I'd just rather use my current calls. – Rudie Jun 19 '11 at 22:04
2

If you want to create a function and execute immediately -

// 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();
Praveen Lobo
  • 6,956
  • 2
  • 28
  • 40
  • 1
    Beware, when you use a named function as a right-hand value (as you do above), you're using a *named function expression* which while it *should* be valid unfortunately has issues in various implementations (mostly -- quelle shock -- IE). Details: http://kangax.github.com/nfe/ – T.J. Crowder Jun 19 '11 at 18:53
0

For my application I went for the easiest way. I just need to fire a function immediately when the page load and use it again also in several other code sections.

function doMyFunctionNow(){
    //for example change the color of a div
}

var flag = true;
if(flag){
    doMyFunctionNow();
}
Pietro
  • 127
  • 6