Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do you have to wrap a callback with an anonymous function?

My html contains two forms overlapping each other, one used as add form and one as edit form. I use jQuery to show and hide them with the following code:

var editForm = $("#edit-form");
var addForm = $("#add-form");

var showEditForm = function() {
    editForm.fadeIn(function() {
        addForm.fadeOut();
    });
};
var showAddForm = function() {
    editForm.fadeOut(function() {
        addForm.fadeIn();
    });
};

I wanted to make the code more compact so I set the fadeOut() call directly on the fadeOut() callback by doing like this:

var showEditForm = function() {
    editForm.fadeIn(addForm.fadeOut);
};
var showAddForm = function() {
    editForm.fadeOut(addForm.fadeIn);
};

But this productes the following error Uncaught TypeError: Failed to execute 'animate' on 'Element': Valid arities are: [1], but 4 arguments provided. but why doesn't that work?

like image 555
mmmmm Avatar asked Dec 06 '22 22:12

mmmmm


2 Answers

That's because calling a function as a property of an object is a special syntax, that calls the function with the object as context.

When you call a function like this:

obj.func();

then this will be a reference to obj inside the function.

If you get the reference to the function, and then call it:

var f = obj.func;
f();

then this will be a reference to the global context, i.e. the window object.

By using editForm.fadeIn(addForm.fadeOut); you get the reference to addForm.fadeOut and send to the fadeIn method. It's no longer associated with the object, so it will be called with the global context instead of the object as context.

You can use the proxy method to associate the function with the object, so that it will be called with the correct context:

var showEditForm = function() {
  editForm.fadeIn($.proxy(addForm.fadeOut, addForm));
};
var showAddForm = function() {
  editForm.fadeOut($.proxy(addForm.fadeIn, addForm));
};
like image 80
Guffa Avatar answered Jan 18 '23 02:01

Guffa


I suspect the problem is that addForm.fadeOut is being called with a bad combination of arguments, when its passed to the fadeIn function (and vice versa).

The classic example of this pitfall seems to be:

["0", "1", "2", "3"].map(function(i) {return parseInt(i);})

This, works as expected and gives [1,2,3,4] as a result. You might expect that you could shorten this, much as you did above, and write

["0", "1", "2", "3"].map(parseInt);

Unfortunately; this evaluates to [0, NaN, NaN, NaN]. The problem, is that .map calls any function provided it with three arguments: the value, the index, and the array itself, and parseInt takes up to two arguments: the value, but also the radix/base to parse in. (e.g. radix 2 to parse a string as binary) So what actually happens is essentially:

[
    parseInt("0", 0), //0, radix is ignored
    parseInt("1", 1), //NaN, what is base 1?
    parseInt("2", 2), //NaN, 2 isn't valid binary
    parseInt("3", 3)  //NaN, 3 isn't valid ternary/base-3
]

I suspect, based on the error message, that the same thing is going on here. The "arity" of a function is the number of arguments passed to it, so the error message here says that 4 arguments were provided, when only one was expected.

In general with functions that take optional arguments, you need to be careful before passing them to other functions directly, or else you can't control what arguments it will be called with.

like image 39
Retsam Avatar answered Jan 18 '23 00:01

Retsam