Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is it necessary to wrap function call in a function body

Tags:

javascript

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!

like image 598
Jaja Harris Avatar asked Jan 06 '13 22:01

Jaja Harris


2 Answers

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
like image 73
Evan Trimboli Avatar answered Nov 14 '22 23:11

Evan Trimboli


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.

like image 33
Mihai Danila Avatar answered Nov 15 '22 00:11

Mihai Danila