在JavaScript中更好地理解回调函数

164

我理解把一个函数作为回调传递给另一个函数,并使其执行,但我不太明白最佳实现方式。我正在寻找一个非常基本的示例,如下:

var myCallBackExample = {
    myFirstFunction : function( param1, param2, callback ) {
        // Do something with param1 and param2.
        if ( arguments.length == 3 ) {
            // Execute callback function.
            // What is the "best" way to do this?
        }
    },
    mySecondFunction : function() {
        myFirstFunction( false, true, function() {
            // When this anonymous function is called, execute it.
        });
    }
};

在myFirstFunction中,如果我执行return new callback(),那么它会执行匿名函数,但这似乎不是正确的方法。


“Correct in what sense?” 这句话的意思是什么?通常回调函数用来处理事件,尤其是处理异步请求,比如 Ajax 调用,基本上你不知道响应会在什么时候(或者是否会)到来。 - cletus
2
顺便提一下,参数类似于数组但不是数组,因此您不能使用argument.length,但可以使用slice方法将其转换为数组... - paul
1
@paul,虽然你说的“arguments”不是一个数组没错,但你仍然可以用它的长度来引用“arguments.length”--试一下吧。这个属性指的是实际传入的参数数量,而不一定是函数签名中的参数数量。 - hotshot309
8个回答

133
你可以直接说
callback();

如果你想在回调函数中调整 this 的值,你可以使用 call 方法。

callback.call( newValueForThis);
在函数内部,this 将是 newValueForThis 的值。

91

你应该检查回调函数是否存在,且是否为可执行函数:

if (callback && typeof(callback) === "function") {
    // execute the callback, passing parameters as necessary
    callback();
}
很多库(jQuery,dojo等)在其异步函数中使用类似的模式,以及node.js对于所有异步函数(通常将errordata传递给回调)。查看它们的源代码会有所帮助!

你为什么要将 callback 转换成字符串然后再检查它的类型?这样会提高性能吗?这就像检查类型,检查转换后的布尔值是否返回 true,然后再次检查其类型并将其与字符串进行比较... 你能解释一下为什么吗? - headacheCoder
我很好奇为什么你需要第一个断言来进行回调...是为了检查 null 或 undefined 吗?使用 typeof(callback) 不是可以实现这个功能吗?typeof(null) === "Object"typeof("undefined") === "undefined" - PJH
1
短路 AND。如果回调函数不存在,则无需计算其类型。虽然你说得没错。用 typeof() 不需要它,但我会进行 jsperf 测试,看看短路是否值得。 - arunjitsingh
@headacheCoder - 在调用之前,callback并没有被转换为字符串,而是被检查其类型是否为函数。代码可能将callback作为参数接受,并且不确定该参数是否为可调用类型,或者也许参数具有不同的类型,以提供一种多态形式,使得代码可以对不同的typeof参数做出不同的反应。 - Lee Goddard

34

执行一个函数有三种主要的可能性:

var callback = function(x, y) {
    // "this" may be different depending how you call the function
    alert(this);
};
  1. 回调函数(callback)使用方式为callback(argument_1,argument_2);
  2. 使用callback.call(some_object, argument_1, argument_2)表示想在某个对象的上下文(context)中调用该函数。在这种情况下,在回调函数中使用"this"关键字将引用作为call()或apply()参数传递的对象。如果不想传递对象上下文,请使用null或undefined。在后一种情况下,全局对象将用于"this"。
  3. 使用callback.apply(some_object, [argument_1, argument_2])同样可以在指定对象上下文中调用该函数,但是要注意参数必须存储在数组中。

根据具体情况选择使用哪种方法:

  1. 如果参数已经存储在数组中或者已经作为单独的变量存在,则选择适当的方法。
  2. 如果需要在特定的对象上下文中调用该函数,则使用call()或apply()方法来传递该对象。如果不需要传递对象上下文,请使用null或undefined。在后一种情况下,全局对象将作为"this"的值。

有关Function.callFunction.apply的文档。


6

回调是关于信号的,"new" 是关于创建对象实例的。

在这种情况下,仅执行 "callback();" 要比 "return new callback()" 更合适,因为你无论如何都不会使用返回值。

(此外,arguments.length==3 的测试真的很笨拙,最好检查回调参数是否存在并且是一个函数。)


6
正确的实现方式应该是:
if( callback ) callback();

这使得回调参数变为可选项。

如果回调参数不是一个函数怎么办? - Yaki Klein

2

您可以使用:

if (callback && typeof(callback) === "function") {
    callback();
}

下面的例子更加全面:

function mySandwich(param1, param2, callback) {
  alert('Started eating my sandwich.\n\nIt has: ' + param1 + ', ' + param2);
  var sandwich = {
      toppings: [param1, param2]
    },
    madeCorrectly = (typeof(param1) === "string" && typeof(param2) === "string") ? true : false;
  if (callback && typeof(callback) === "function") {
    callback.apply(sandwich, [madeCorrectly]);
  }
}

mySandwich('ham', 'cheese', function(correct) {
  if (correct) {
    alert("Finished eating my " + this.toppings[0] + " and " + this.toppings[1] + " sandwich.");
  } else {
    alert("Gross!  Why would I eat a " + this.toppings[0] + " and " + this.toppings[1] + " sandwich?");
  }
});


1
这是一个基本示例,解释了JavaScript中的callback()函数:

var x = 0;

function testCallBack(param1, param2, callback) {
  alert('param1= ' + param1 + ', param2= ' + param2 + ' X=' + x);
  if (callback && typeof(callback) === "function") {
    x += 1;
    alert("Calla Back x= " + x);
    x += 1;
    callback();
  }
}

testCallBack('ham', 'cheese', function() {
  alert("Function X= " + x);
});

JSFiddle

可以翻译为:

{{链接1:JSFiddle}}


1

function checkCallback(cb) {
  if (cb || cb != '') {
    if (typeof window[cb] === 'undefined') alert('Callback function not found.');
    else window[cb].call(this, Arg1, Arg2);
  }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接