为什么必须使用匿名函数包装回调?

时间:2015-05-03 21:13:21

标签: javascript jquery callback fadein fadeout

我的html包含两个彼此重叠的表单,一个用作添加表单,另一个用作编辑表单。我使用jQuery用以下代码显示和隐藏它们:

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

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

我想让代码更紧凑,所以我直接在fadeOut()回调上设置fadeOut()调用,方法如下:

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

但这产生了以下错误Uncaught TypeError: Failed to execute 'animate' on 'Element': Valid arities are: [1], but 4 arguments provided.,但为什么不起作用?

4 个答案:

答案 0 :(得分:8)

这是因为将一个函数作为一个对象的属性调用是一种特殊的语法,它以对象作为上下文来调用该函数。

当你调用这样的函数时:

obj.func();

然后this将成为函数内obj的引用。

如果您获得对该函数的引用,然后调用它:

var f = obj.func;
f();

然后this将引用全局上下文,即window对象。

使用editForm.fadeIn(addForm.fadeOut);,您可以获得addForm.fadeOut的引用并发送到fadeIn方法。它不再与对象相关联,因此将使用全局上下文而不是对象作为上下文来调用它。

您可以使用proxy方法将函数与对象相关联,以便使用正确的上下文调用它:

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

答案 1 :(得分:3)

我怀疑问题是当addForm.fadeOut被传递给fadeIn函数(反之亦然)时,会使用错误的参数组合调用["0", "1", "2", "3"].map(function(i) {return parseInt(i);})

这个陷阱的典型例子似乎是:

[1,2,3,4]

这样,按预期工作,结果为["0", "1", "2", "3"].map(parseInt); 。你可能希望你可以像上面那样缩短它,然后写

[0, NaN, NaN, NaN]

不幸的是;这评估为.map。问题是,parseInt调用任何函数为它提供三个参数:值,索引和数组本身,而[ 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 ] 最多需要两个参数:值,但也是基数/要解析的基数。(例如,基数2将字符串解析为二进制)所以实际发生的事实上是:

while (count < 100) {
    std::ostringstream name;  
    name << "Process" << count;
    browser->add(name.str().c_str());
    count++;
}

我怀疑,根据错误信息,这里发生了同样的事情。函数的“arity”是传递给它的参数的数量,因此这里的错误消息表明,只提供了一个参数,提供了4个参数。

通常,对于带有可选参数的函数,在将它们直接传递给其他函数之前需要小心,否则你无法控制它将被调用的参数。

答案 2 :(得分:2)

小提琴:http://jsfiddle.net/jmj8tLfm/

调用

addForm.fadeInaddForm.fadeOut时未指定通常在您致电this时通过的addForm.fadeIn()上下文。请按以下方式尝试.bind() - 正确使用此变量:

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

答案 3 :(得分:2)

如果您使用Vanilla JS编写。关于为什么您还需要在匿名函数中传递回调函数的原因,与函数调用有关。

看看这个例子:

const firstFunc = (callback) => {
 setTimeout(function() {
  console.log('yes');
  console.log(callback())
 }, 3000);
}

const secondFunc = () => console.log('great');

firstFunc(function(){
 secondFunc();
});

// prints 'yes' 'great' after 3 seconds

> yes
  great

在调用函数时,如果您传递不带括号的回调参数,即firstFunc(secondFunc);,则在第一个函数内部(如上)提供的第一个函数完成调用后(如上述),将调用回调函数。调用该功能。即callback()()是重要部分。

尝试在第一个函数callback中省略括号,并在不带括号firstFunction(secondFunction)的情况下将第二个函数作为回调传递,请注意您是如何传递回调函数的,但永远不会被调用。您的console.log()应该看起来像这样。

> yes
  () => console.log('great')

那为什么如此重要...

如果使用第一个代码段中的设置将函数调用作为firstFunc(secondFunc())传递。您会注意到第二个函数先打印,然后3秒后第一个函数被调用。

> great
  yes

鉴于Javascript是事件驱动的,因此可以const secondFunc = () => console.log('great');找到第二个函数的调用,它将立即调用该函数,而无需等待第一个函数的响应。这就是()在firstFunc内调用secondFunc()时所做的。

通过传递一个匿名函数,该函数直到到达callback()调用部分时才被调用。再次使用第一个代码段中的设置,尝试。

firstFunc(function(){
 secondFunc();
});

firstFunc(function(){
 secondFunc();
}())

查看第二个调用如何立即调用secondFunc。发生的情况是匿名函数是一个包装程序,无法立即调用您的函数。

这为什么有用? 如果secondFunc接受回调,则在第二个函数完成执行之前,不会调用该回调函数。您需要在第二个函数中调用该回调函数。

firstFunc(function(){
 secondFunc(function(){
  thirdFunc();
 });
});
相关问题