为什么你必须用匿名函数包装回调?
Why do you have to wrap a callback with an anonymous function?
我的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.
,但为什么不起作用?
那是因为调用函数作为对象的 属性 是一种特殊的语法,它以对象作为上下文调用函数。
当您调用这样的函数时:
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));
};
Fiddle: http://jsfiddle.net/jmj8tLfm/
调用 addForm.fadeIn
和 addForm.fadeOut
时未指定调用 addForm.fadeIn()
时通常会传递的 this
上下文。尝试 .bind()
-ing this 变量,如下所示:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut.bind(addForm));
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn.bind(addForm));
};
我怀疑问题是当 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
最多接受两个参数:值,还有 radix/base 进行解析。(例如,基数 2 将字符串解析为二进制)所以实际发生的本质上是:
[
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
]
根据错误消息,我怀疑这里发生了同样的事情。函数的 "arity" 是传递给它的参数的数量,所以这里的错误消息说提供了 4 个参数,而预期只有一个。
一般来说,对于带有可选参数的函数,在将它们直接传递给其他函数之前需要小心,否则你无法控制调用它的参数。
如果你正在用 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);
回调函数将在第一个函数完成后调用(就像上面一样)在回调获取的第一个函数内提供called 正在调用该函数。即 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();
});
});
我的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.
,但为什么不起作用?
那是因为调用函数作为对象的 属性 是一种特殊的语法,它以对象作为上下文调用函数。
当您调用这样的函数时:
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));
};
Fiddle: http://jsfiddle.net/jmj8tLfm/
调用addForm.fadeIn
和 addForm.fadeOut
时未指定调用 addForm.fadeIn()
时通常会传递的 this
上下文。尝试 .bind()
-ing this 变量,如下所示:
var showEditForm = function() {
editForm.fadeIn(addForm.fadeOut.bind(addForm));
};
var showAddForm = function() {
editForm.fadeOut(addForm.fadeIn.bind(addForm));
};
我怀疑问题是当 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
最多接受两个参数:值,还有 radix/base 进行解析。(例如,基数 2 将字符串解析为二进制)所以实际发生的本质上是:
[
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
]
根据错误消息,我怀疑这里发生了同样的事情。函数的 "arity" 是传递给它的参数的数量,所以这里的错误消息说提供了 4 个参数,而预期只有一个。
一般来说,对于带有可选参数的函数,在将它们直接传递给其他函数之前需要小心,否则你无法控制调用它的参数。
如果你正在用 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);
回调函数将在第一个函数完成后调用(就像上面一样)在回调获取的第一个函数内提供called 正在调用该函数。即 callback()
,() 是重要的部分。
尝试像这样省略第一个函数中的括号,callback
并将第二个函数作为不带括号的回调传递 firstFunction(secondFunction)
注意你是如何传递回调函数的,但它永远不会被传递调用。你的 console.log() 应该是这样的。
> yes
() => console.log('great')
那么为什么这很重要...
如果您使用第一个代码片段中的设置将函数调用作为 firstFunc(secondFunc())
传递。您会注意到第二个函数先打印,然后 3 秒后调用第一个函数。
> great
yes
鉴于Javascript是事件驱动的,可以找到第二个函数的调用const secondFunc = () => console.log('great');
,它会立即调用该函数而不等待第一个函数的响应。这就是当您在 firstFunc.
通过传递一个匿名函数,该函数在到达 callback() 调用部分之前永远不会被调用。再次使用第一个代码片段中的设置,尝试。
firstFunc(function(){
secondFunc();
});
firstFunc(function(){
secondFunc();
}())
查看第二个调用如何立即调用 secondFunc。发生的事情是匿名函数是一个包装器,不会立即调用您的函数。
为什么这有用? 如果 secondFunc 接受回调,则在第二个函数完成执行之前不会调用回调函数。您需要在第二个函数中调用该回调函数。
firstFunc(function(){
secondFunc(function(){
thirdFunc();
});
});