JavaScript中,函数是第一流(first-class)对象;也就是说,函数是 Object
类型并且可以像其他顶级对象(String,Array,Number等)一样使用。它们可以“保存在变量中,作为参数传递给函数,在函数内创建,以及让函数返回”。

是因为函数是五星级对象,我们可以把一个函数作为参数传递给其它一个函数,然后以生函数内执行,甚至为可以被死函数返回,然后又实践。这即是
JavaScript 中回调函数(callback
functions)的原形。在本文的结余部分,我们拿上到有关 JavaScript
回调函数的享有知识。回调函数可能是 JavaScript
中使最常见的函数式编程技术了,你得当其余一样段落 JavaScript或jQuery
代码发现其,但是,它对众多 JavaScript
开发者来说仍是暧昧的。直到你读书了本文,就再也不会对其感到神秘了。

扭转调函数
是来源于函数式编程的同种技术。从底层来说,函数式编程把函数用作参数。函数式编程过去凡是
——
现在依旧是,(尽管如今不绝盛)被起经验的、高级开发者视作难懂的技巧。

万幸的凡,函数式编程已经让发明到像咱这么的底小人物还能够便于理解的境地。其重要技术有就是是回调函数。下面你就是会视,实现回调函数很容易,就比如传递一个一般变量参数一样。这个技能这样概括以至于自己生想得到为什么大部分课程都拿它们归类为高级主题中。

回调是呀?

扭动调函数,也给高阶(higher-order)函数,是一个用作参数传递到旁函数的函数,然后回调函数在其他函数未遭让调用。回调函数本质上是一个模式,因此采取回调函数被称为回调模式。

要看下的代码,这是 jQuery 中广的回调函数的施用:

$("#btn_1").click(function(){
    alert("Btn 1 Clicked");
});

此,传递了一个匿名函数给 click 方法。click
方法将调用或执行这回调函数。

重新拘留一个事例:

var friend = ["Mike", "Stacy", "Andy", "Rick"];

friend.forEach(function(eachName, index){
    console.log(index + 1 + "." + eachName); // 1.Mike,2.Stacy,3.Andy,4.Rick
});

此间,传递了一个匿名函数给 forEach 方法。

掉调函数如何行事?

当我们传递一个回调函数给任何函数时不时,我们只是传递了函数定义。我们连不曾以参数中施行函数。换句话说,传递函数时不可知在部数名为背后加括号“()”,而推行函数时那样需要。

由于任何函数以参数中发生欠回调函数的概念,所以她好于旁时候实施该函数。

小心到回调函数不是就施行的,所谓“回调”就是靠于另函数遇之一特定的时让改过调用。所以还看率先独例子,click
函数中的匿名函数将以click函数体内被调用。即使该函数匿名,也可以通过
arguments 对象看到。

转调函数都是闭包
当传递一个回调函数给另函数不时,回调函数在旁函数函数体内某处执行,就接近回调函数是当其它函数中定义的。这证明回调函数是一个闭包(closure)。众所周知,闭包可以拜外层包含函数的作用域,所以回调函数也克顾另函数的变量,甚至全局作用域中之变量。

心想事成回调函数所依的基本准则

尽管未复杂,但仍然发生一对值得注意的地方。

动命名的或者匿名的函数作为回调函数

仲单例中我们以了匿名函数作为回调函数,这是同样栽常见的做法。另一样栽普遍做法是概念一个来名字的函数,然后传递给其它一个函数。

// 全局变量
var allUserData = [];

// logStuff 函数,用于打印参数的值
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, calllback){
    allUserData.push(options);
    calllback(options);
}

// 调用 getInput 函数时传递了 logStuff,所以,logStuff 将在 getInput 函数中被调用(或执行)
getInput({name:"Rich",speciality:"JavaScript"},logStuff);
// name:Rich
// speciality:JavaScript

传递参数为回调函数

出于回调函数在实践时也唯有是一个日常函数,所以我们得以传递参数为它们。可以传递外层函数的特性或者全局变量。上例被,传递了
options 作为回调函数的参数。然后,让咱们传递一个全局变量和片变量。

// 全局变量
var generalLastName = "Clinton";

function getInput(options, calllback){
    allUserData.push(options);
    // 传递全局变量
    calllback(generalLastName, options);
}

履前保证回调是一个函数

每当调用之前检查所盛传的回调函数是否确实是一个函数是一个好习惯。让咱们重构一下达例被getInput函数:

function getInput(options, calllback){
    allUserData.push(options);

    // 确保 calllback 是一个函数
    if(typeof calllback === "function"){
        // 调用之,因为已经确保它是函数了
        calllback(options);
    }
}

假如未检测其项目,当传入的参数不是函数时,就见面导致运行时左。

转调函数与this相关的问题

当回调函数中之所以到了 this
对象时,我们不得不改成执行回调函数的章程来保障原有的 this
对象。否则,this 对象或借助于全局的 window
对象,如果回调函数被流传了大局函数中的话。或者对外层包含方法的靶子。

下在代码中示范:

var clientData = {
    id: 012334,
    fullName: "Not Set",
    // setUserName 是 clientData 对象的方法
    setUserName: function(firstName, lastName){
        this.fullName = firstName + " " + lastName;
    }
}

function getUserInput(firstName, lastName, calllback){
    calllback(firstName,lastName);
}

脚的代码中,当 clientData.setUserName 执行时,this.fullName 将非会见设置
clientData 对象的 fullName 属性,而是设置为 window 对象的 fullName
属性。这是坐全局函数中的 this 对象指于 window 对象

getUserInput("Barack", "Obama", clientData.setUserName);

console.log(clientData.fullName); // Not Set

console.log(window.fullName); // Barack Obama

使用 CallApply 函数来维持 this

咱得以经过 CallApply
函数来缓解者的题材。目前吧,JavaScript
中之每个函数都产生点儿独方法:Call 和 Apply。这片个法子用来安装函数内的
this 对象。

Call 把第一只参数作为函数内的 this
对象,其他的参数分别传递让函数。Apply 也是将第一只参数作为函数内的
this 对象,而第二单参数是一个数组(或者argument对象)。

咱们当底下的代码中利用 Apply 来缓解者题材:

function getUserInput(firstName, lastName, calllback, calllbackObj){
    calllback.apply(calllbackObj, [firstName,lastName]);
}

apply 正确安装了 this 对象,现在得是实施回调,并设置 clientData
上之 fullName 属性了。

getUserInput("Barack", "Obama", clientData.setUserName, clientData);

console.log(clientData.fullName); // Barack Obama

兴下多单回调函数

咱俩传递多只回调函数到此外函数,就像传递多单变量一样,下面时一个经典的jQuery
AJAX函数的事例:

function successCallBack(){

}

function failCallBack(){

}

function completeCallback(){

}

function errorCallback(){

}

$.ajax({
    url:"http://www.91ymb.com/favicon.png",
    success: successCallBack,
    complete: completeCallback,
    error: errorCallback
});

“回调地狱”问题以及缓解

于异步代码执行中,代码可能为其它顺序执行,有时见面视有诸多交汇回调函数,比如下例:

var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);
​
                        // Let's close the db​
                        p_client.close();
                    });
                });
            });
        });
    });
});

上述杂乱之代码被誉为回调地狱,回调太多以至于很麻烦知晓。你可能无见面蒙这问题,但是如果碰到了,这有少数种植艺术解决之题目。

  1. 取名函数,定义函数,然后传递函数名当回调,而未是定义匿名函数。
  2. 模块化:把代码分模块,这有就足以导出某个特定任务的代码。然后导入好特定模块。

形容你协调的回调函数

及今天,你该知道了关于 JavaScript
回调函数的有情节,你意识以回调函数不仅简单而非常强大,你应当看自己之代码,寻找有机遇动回调函数,它会于您做这些工作:

  1. 非重复代码(DRY)
  2. 实现再好之抽象。
  3. 再度好的可维护性
  4. 再次好之可读性
  5. 重新多特别的函数

形容好的回调函数也要命简单。下面的例子中,我拿创设一个函数用来:取回用户数据,使用数据变动诗句,然后告诉用户。这听起好像是一个乱之函数,有那么些if/else语句,并且可能被界定而休克就此用户数量做些其它的作业。

可,我拿具体成效的落实交给回调函数,这样主函数用于取回用户数据,然后简短地传递用户全名和性别为回调函数,然后实施回调函数就实施了。

简言之,getUserInput
函数是通用的:它实施有回调函数来实现具体的功能。

// 首先,建立一个诗句生成函数,它将作为回调函数传递
function genericPoemMaker(name, gender){
    console.log(name + " is finer than fine wine.");
    console.log("ALltrustic and noble for the modern time.");
    console.log("Always admirably adorned with the latest style.");
    console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile");
}

// 
function getUserInput(firstName, lastName, gender, callback){
    var fullName = firstName + " " + lastName;

    // 确保 callback 是函数
    if(typeof callback === "function"){
        callback(fullName, callback);
    }
}

// 调用 getUserInput,并传递回调函数
getUserInput("Michael", "Fassbender", "Man", genericPoemMaker);
// 输出
​/* Michael Fassbender is finer than fine wine.
Altruistic and noble for the modern time.
Always admirably adorned with the latest style.
A Man of unfortunate tragedies who still manages a perpetual smile.
*/

由 getUserInput
函数只是处理数量,我们好传递任何回调函数。例如,传递一个 greetUser
函数:

function greetUser(customerName, sex){
    var salutation = sex & sex == "Man" ? "Mr." : "Ms.";
    console.log("Hello, " + salutation + " " + customerName);
}

getUserInput("Bill", "Gates", "Man", greetUser);

// 输出
// Hello, Mr. Bill Gates

我们一致调用 getUserInput 函数星星糟,但推行了不同之天职。

注意到,下列场景是咱一再使用到回调函数的地方,尤其是当代 web
应用开发,库和框架开发:

  1. 异步执行(如读取文件,HTTP请求)
  2. 事件监听器/处理器
  3. setTimeout 函数和 setInterval 函数
  4. 通用标准:代码简洁性

最后

JavaScript
回调函数异常好用,有诸多利益。现在即令从头使用回调函数来重构代码以加强抽象、可维护性、可读性吧。

原稿链接:
http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them/

相关文章

网站地图xml地图