Can I name a JavaScript function and execute it immediately?
Asked Answered
I

9

108

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?

Invagination answered 19/6, 2011 at 18:7 Comment(3)
possible duplicate of Javascript self executing functionBirdseed
No, @CristianSanchez.Shoeblack
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.Epistemology
S
149

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.

Schell answered 19/6, 2011 at 18:30 Comment(4)
"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: kangax.github.com/nfe (This is finally fixed in IE9.)Tolentino
@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... MehSchell
If, like me, you run into jshint issues with this method, you can use the call() method from here: https://mcmap.net/q/205040/-how-to-concisely-assign-and-immediately-invoke-a-function-variableBcd
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.Leith
L
21

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')
Lobworm answered 8/5, 2014 at 16:27 Comment(4)
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 =)Invagination
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.Lobworm
@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 :) ?Froissart
@Froissart I've run that in my console and it console logs blammo?Lobworm
T
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

Tolentino answered 19/6, 2011 at 18:15 Comment(2)
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'Invagination
@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,Tolentino
U
7

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;
 }()
);
Unaunabated answered 19/6, 2011 at 18:31 Comment(5)
Stupid brackets all look alike!! Thanks! Cheers indeed!Invagination
Yeah... So eh... Actually that doesn't work. The function is executed immediately, yes, but it's not registered anywhere, so calling it again -> ReferenceErrorInvagination
@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: activestate.com/komodo-editUnaunabated
Dude... (And obviously I typed it in the SO web editor, not in a desktop editor.)Invagination
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 =)Invagination
L
4

You might want to create a helper function like this:

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

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
Lopez answered 19/6, 2011 at 18:11 Comment(2)
Why is this a bad solution? Because the functions are registered in the global namespace? Who voted down for this?Invagination
Great idea :))-Shoeblack
F
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"
Flitter answered 23/10, 2018 at 11:56 Comment(3)
So how would I call it again? What's its name, to call it by? You're only keeping the result, not the function.Invagination
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? @InvaginationFlitter
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.Invagination
B
2

Try to do like that:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Bolen answered 19/6, 2011 at 18:13 Comment(2)
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.)Blowup
That's also a lot of typing AND remembering what to type. Then I'd just rather use my current calls.Invagination
B
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();
Bouchier answered 19/6, 2011 at 18:16 Comment(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: kangax.github.com/nfeTolentino
S
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();
}
Seisin answered 7/3, 2022 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.