什么是回调或高级函数
在JavaScrip中,function是内置对象,它可以存储在变量中,通过参数传递给另一个函数,延迟到函数中执行,甚至执行后将它返回。
回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数(在这称作"otherFunction")的高级函数,回调函数会在otherFunction内被调用(或执行)。回调函数的本质是一种模式(一种解决常见问题的模式),因此回调函数也被称为回调模式。
jQuery中常用的回调函数:
//Note that the item in the click method's parameter is a function, not a variable.
//The item is a callback function
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
前面的例子中,我们传递了一个函数给click方法的形参,click方法将会调用(或执行)我们传递给它的回调函数。这个例子就给出了JavaScript中使用回调函数的一个典型方式,并广泛应用于jQuery中。
JavaScript的典型例子:
var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index){
console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});
forEach() 方法:遍历数组的每个元素,并将元素传递给回调函数,然后调用回调函数。
回调函数的原理
我们可以像使用变量一样使用函数,作为另一个函数的参数,在另一个函数中作为返回结果,在另一个函数中调用它。当我们作为参数传递一个回调函数给另一个函数时,我们只传递了这个函数的定义,并没有在参数中执行它。
当包含(调用)函数拥有了在参数中定义的回调函数后,它可以在任何时候调用(也就是回调)它。
这说明回调函数并不是立即执行,而是在包含函数的函数体内指定的位置“回调”它(形如其名)。所以,即使第一个jQuery的例子看起来是这样:
//The anonymous function is not being executed there in the parameter.
//The item is a callback function
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
匿名函数将延迟在click函数的函数体内被调用,即使没有名称,也可以被包含函数通过 arguments对象访问。
回调函数是闭包的。当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。这意味着回调函数是闭包的。闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。
例子
前面的例子都是使用命名函数或匿名函数作为回调,这是使用回调函数的通用形式之一。
另一个经常被使用的形式是定义一个带名称的函数,并将函数名作为参数传递给另一个函数,例如:
// 全局变量
var allUserData = [];
// 回调函数
function logStuff (userData) {
if ( typeof userData === "string")
{
console.log(userData);
}
else if ( typeof userData === "object")
{
for (var item in userData) {
console.log(item + ": " + userData[item]);
}
}
}
// 另一个函数
function getInput (options, callback) {
allUserData.push (options);
callback (options);
}
//调用函数,传入回调函数名logStuff
getInput ({name:"Rich", speciality:"JavaScript"}, logStuff);
// name: Rich
// speciality: JavaScript
在执行之前确保回调是一个函数
在调用之前,确保通过参数传递进来的回调是一个需要的函数通常是明智的。此外,让回调函数是可选的也是一个好的实践。
让我们重构一下上面例子中的getInput函数,确保回调函数做了适当的检查。
function getInput(options, callback) {
allUserData.push(options);
// Make sure the callback is a function
if (typeof callback === "function") {
// Call it, since we have confirmed it is callable
callback(options);
}
}
如果getInput函数没有做适当的检查(检查callback是否是函数,或是否通过参数传递进来了),我们的代码将会导致运行时错误。
使用含有this对象的回调函数的问题
当回调函数是一个含有this对象的方法时,我们必须修改执行回调函数的方法以保护this对象的内容。否则this对象将会指向全局的window对象(如果回调函数传递给了全局函数),或指向包含函数。让我们看看下面的代码:
// Define an object with some properties and a method
// We will later pass the method as a callback function to another function
var clientData = {
id: 094545,
fullName: "Not Set",
// setUserName is a method on the clientData object
setUserName: function (firstName, lastName) {
// this refers to the fullName property in this object
this.fullName = firstName + " " + lastName;
}
}
function getUserInput(firstName, lastName, callback) {
// Do other stuff to validate firstName/lastName here
// Now save the names
callback (firstName, lastName);
}
在下面的示例代码中,当clientData.setUserName被执行时,this.fullName不会设置clientData 对象中的属性fullName,而是设置window 对象中的fullName,因为getUserInput是一个全局函数。出现这种现象是因为在全局函数中this对象指向了window对象。
getUserInput ("Barack", "Obama", clientData.setUserName);
console.log (clientData.fullName);// Not Set
// The fullName property was initialized on the window object
console.log (window.fullName); // Barack Obama
解决方法:使用Call或Apply函数保护this对象
//Note that we have added an extra parameter for the callback object, called "callbackObj"
function getUserInput(firstName, lastName, callback, callbackObj) {
// Do other stuff to validate name here
// The use of the Apply function below will set the this object to be callbackObj
callback.apply (callbackObj, [firstName, lastName]);
}
通过Apply函数正确地设置this对象,现在我们可以正确地执行回调函数并它正确地设置clientData对象中的fullName属性。
// We pass the clientData.setUserName method and the clientData object as parameters. The clientData object will be used by the Apply function to set the this object
getUserInput ("Barack", "Obama", clientData.setUserName, clientData);
// the fullName property on the clientData was correctly set
console.log (clientData.fullName); // Barack Obama
参考自这位博主的文章来源