6

I often see something like the following in JavaScript:

$("#sendButton").click(function() {
    sendForm();
}

Why is it necessary to wrap the call to sendForm() inside a function? I would think that doing it like this would be more readable and less typing.

$("#sendButton").click(sendForm);

What are the advantages/disadvantages to each approach? thanks!

Alexander
  • 22,498
  • 10
  • 56
  • 73
Jaja Harris
  • 3,766
  • 3
  • 21
  • 22
  • 2
    It isn't necessary, Its probably done out of habit. – Musa Jan 06 '13 at 22:55
  • 2
    Keep in mind that the value of `this` in your two versions will differ (unless `sendForm` is a bound function), though the `this` in the first version isn't very useful. – the system Jan 06 '13 at 23:02

6 Answers6

6

There's typically two cases where you'd want to use the former over the latter:

  1. If you need to do any post-processing to the arguments before calling your function.

  2. If you're calling a method on an object, the scope (this reference) will be different if you use the second form

For example:

MyClass = function(){
    this.baz = 1;    
};

MyClass.prototype.handle = function(){
    console.log(this.baz);    
};

var o = new MyClass();

$('#foo').click(o.handle);
$('#foo').click(function(){
    o.handle();
});

Console output:

undefined
1
hakre
  • 178,314
  • 47
  • 389
  • 754
Evan Trimboli
  • 29,097
  • 5
  • 41
  • 64
1

Here you are defining an anonymous event handler that could call multiple functions inline. It's dirty and tough to debug, but people do it because they are lazy and they can.

It would also work like your second example (how I define event handlers):

$("#sendButton").click(sendForm)

Something you get by defining your event handlers inline is the ability to pass event data to multiple functions and you get this scoped to the event object:

$("#sendButton").click(function(event) {
    sendForm();
    doSomethingElse(event);
    andAnotherThing(event);
    // say #sendButton is an image or has some data attributes
    var myButtonSrc = $(this).attr("src");
    var myData = $(this).data("someData");
});
Patrick Gunderson
  • 3,217
  • 14
  • 26
1

If all you are doing is calling sendForm, then there isn't much difference, in the end, between the two examples you included.

$("#sendButton").click(function(event) {
    if(event.someProperty) { /* ... */ }
    else { sendForm({data: event.target, important: 'yes'}); }
}

However, in the above case, we could handle arguments passed to the callback from click(), but if the sendForm function is already equipped to handle this, then there's no reason why you wouldn't place sendForm as the callback argument if that is truly all you are doing.

function sendForm(event) {
    // Do something meaningful here.
}

$("#sendButton").click(sendForm);

Note that it is up to you where you handle the differing layers of logic in your program; you may have encapsulated certain generic functionality in a sendForm function then have a sendFormCallback which you pass to these sorts of function which handle the interim business of event/callback processing before calling sendForm itself.

If you are working in a callback-heavy environment, it would be wise to separate significant functionality from the callback triggers themselves to avoid callback hell and promote maintainability and readability in your source code.

Ryan Stein
  • 7,730
  • 3
  • 21
  • 38
1

It's just to lock scope. When you wrap that sendForm() in the anonymous function that closes over the current scope. In other words, the this will be kept with it. If you just pass sendForm then any calls to this will come from the calling scope.
This is a good question for learning about scope in javascript, and questioning conventions.

Hasen
  • 280
  • 1
  • 3
  • 12
1

Probably one too many answers by now, but the difference between the two is the value of this, namely the scope, entering sendForm. (Also different will be the arguments.) Let me explain.

According to the JavaScript specification, calling a function like this: sendForm(); invokes the function with no context object. This is a JavaScript given.

However, when you pass a function as an argument, like this: $(...).click(sendForm), you simply pass a reference to the function for later invocation. You are not invoking that function just yet, but simply passing it around just like an object reference. You only invoke functions if the () follows them (with the exception of call and apply, discussed later). In any case, if and when someone eventually calls this function, that someone can choose what scope to call the function with.

In our case, that someone is jQuery. When you pass your function into $(...).click(), jQuery will later invoke the function and set the scope (this) to the HTML element target of the click event. You can try it: $(...).click(function() { alert(this); });, will get you a string representing a HTML element.

So if you give jQuery a reference to an anonymous function that says sendForm(), jQuery will set the scope when calling that function, and that function will then call sendForm without scope. In essence, it will clear the this. Try it: $(...).click(function() { (function() { alert(this); })(); });. Here, we have an anonymous function calling an anonymous function. We need the parentheses around the inner anonymous function so that the () applies to the function.

If instead you give jQuery a reference to the named function sendForm, jQuery will invoke this function directly and give it the scope that it promises to always give.

So the answer to your question becomes more obvious now: if you need this to point to the element target of the click when you start work in sendForm, use .click(sendForm). Otherwise, both work just as well. You probably don't need this, so skip the anonymous function.

For those curious, scope can be forced by using the JavaScript standard apply or call (see this for differences between the two). Scope is also assigned when using the dot operator, like in: obj.func, which asks of JavaScript to call a function with this pointing to obj. (So in theory you could force obj to be the scope when calling a function by doing something like: obj.foo = (reference to function); obj.foo(); delete obj.foo; but this is a pretty ugly way of using apply.

Function apply, used by jQuery to call your click handler with scope, can also force arguments on the function call, and in fact jQuery does pass arguments to its click handlers. Therefore, there is another difference between the two cases: arguments, not only scope, get lost when you call sendForm from an anonymous function and pass no parameters.

Community
  • 1
  • 1
Mihai Danila
  • 2,029
  • 1
  • 18
  • 24
0

Nope, that second example is perfectly valid.

99.9% of jQuery examples use the first notation, that doesn't mean you need to forget basic JavaScript syntax.

Madara's Ghost
  • 158,961
  • 49
  • 244
  • 292