我們在學習其它語言時,在調用一個函數時,往往是如下這樣的:
~~~
// 定義
function yunzhi(string name, int sex, array friends)
{
.....
}
// 調用
yunzhi('zhangsan', 1, array('lisi', 'wangwu'));
~~~
而很少見過下面這種調用形式:
~~~
yunzhi('hello', function(){});
~~~
> 在JavaScript中,作為參數傳入的函數,我們稱之為回調函數。
我們看下JavaScript中對回調函數是這樣定義的:
> A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
簡單翻一下,回調函數就是回頭再調用的函數。
舉個簡單例子:
假設現在有函數a()和函數b():
**C語言中**:
a(b()); //b()被作為a()的參數傳入,先執行b(),再執行a()。
**JavaScript中**:
a(b()); //b()被作為a()的參數(回調函數)傳入,先執行a(),由a()決定b()的執行情況(b()可能被執行一次或多次,也可能一次也不執行,可能一開始就被執行,也可能最后被執行,一切全憑a()的心情)。
這里看不懂沒關系,我們下面用代碼的方式詳細說明一下。
# 傳函數
以前,我們只將普通的變量傳到另一個函數中,變量的類型可能是int、string、array,還可能是object,但還沒有把函數看做一個變量作為參數傳過去。
下面,我們來共同看看在JS中函數是怎么作為參數被傳過去,并且在函數中被正確執行的。
為此,我們在根文件夾下新建一個test.html文件。
測試代碼如下:
`test.html`
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
// 定義yunzhi
var yunzhi = function(callback) {
// 執行傳入的函數 callback
callback();
}
// 定義hello,控制臺輸出hello
var hello = function(){
console.log('hello');
};
// 將hello做為參數傳入yunzhi
yunzhi(hello);
</script>
</body>
</html>
~~~
測試:

沒錯,在JS中,函數做為參數傳入后就這么被執行了。
下面,我們再簡單改寫下。
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
// 定義yunzhi
var yunzhi = function(callback) {
// 執行傳入的函數 callback
callback();
}
// 由于yunzhi接收的參數類型為function
// 我們在這直接寫下一個function也是可以的
yunzhi(function(){});
</script>
</body>
</html>
~~~
再改寫:
~~~
...
// 由于yunzhi接收的參數類型為function
// 我們在這直接寫下一個function也是可以的
yunzhi(function(){
console.log('hello');
});
</script>
...
~~~
刷新面頁,我們發現實現的效果相同。
## 引入對象
為了更好的理解在angularjs中定義模塊和控制器的寫法。我們再給出另一個示例:定義一個yunzhi對象,對象中有3個屬性:name、sex、fn,類型分別為string、int和function。
~~~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<script type="text/javascript">
// 定義一個對象
var yunzhi = {
name:'hello',
sex:1,
fn:function(){}
}
</script>
</body>
</html>
~~~
既然fn的類型是function,那么當然是可以這樣定義:
~~~
fn:function(){
console.log('I am a function');
}
~~~
然后我們這樣來調用:
~~~
yunzhi.fn();
~~~
當然了,name和sex也是可以定義為function的。我們再改寫:
~~~
var yunzhi = {
name:'hello',
sex:1,
fn:function(){
console.log('I am function');
},
setName: function(){},
setSex: function(){}
}
~~~
再改寫:
~~~
setName: function(name, callback){
this.name = name; // 設置當前對象的name
callback; // 執行回調
return this; // 返回當前對象
},
~~~
繼續改寫setSex:
~~~
var yunzhi = {
name:'hello',
sex:1,
fn:function(){
console.log('I am function');
},
setName: function(name, callback){
this.name = name; // 設置當前對象的name
callback; // 執行回調
return this; // 返回當前對象
},
setSex: function(sex, callback){
this.sex = sex;
callback;
return this;
}
}
~~~
最后,我們如下調用:
~~~
var yunzhi = {
name:'hello',
sex:1,
fn:function(){
console.log('I am function');
},
setName: function(name, callback){
this.name = name; // 設置當前對象的name
callback(); // 執行回調
return this; // 返回當前對象
},
setSex: function(sex, callback){
this.sex = sex;
callback();
return this;
}
}
// 調用yunzhi中的setName方法,得到一個對象。
var zhangsan = yunzhi.setName('zhangsan', function(){});
// 調用對象中的sexSex方法
zhangsan.setSex(0, function(){});
~~~
最后,我們向function中加入我們想執行的回調代碼。
> 沒錯,一個好的書寫習慣,不僅可以提升代碼的效率,還可以提升我們寫代碼的準確性。更重要的是,我們可以更好的理解代碼之間互相調用的關系。當我們在接解一門新的語言時,就顯得更加重要了。
~~~
// 調用yunzhi中的setName方法,得到一個對象。
var zhangsan = yunzhi.setName('zhangsan', function(){
console.log('set name zhangsan success');
});
// 調用對象中的sexSex方法
zhangsan.setSex(0, function(){
console.log('set sex 0 success');
});
~~~
執行結果:

## 為什么要用回調
1、回調函數的執行需要依賴前面函數的執行結果。
基于此,我們再改寫,讓其更貼近我們前面看到的angularjs初始化模塊及控制器的代碼。
~~~
setName: function(name, callback){
this.name = name; // 設置當前對象的name
var message = 'set name ' + name + 'success';
callback(message); // 執行回調
return this; // 返回當前對象
},
...
// 調用yunzhi中的setName方法,得到一個對象。
var zhangsan = yunzhi.setName('zhangsan', function(message){
console.log(message);
});
~~~
最終的執行結果與未改寫前是一致的。
現在,在讓我們回想一下angularjs的寫法:
~~~
// 調用 angular對象的module方法,該方法接收兩個參數。第一個是模塊名,第二個數組,返回值是一個對象。
var module = angular.module('modulename', []);
// 調用上面返回對象的controller方法,該方法接收兩個參數,第一個參數類型是字符串,第二個類型是function。
module.controller('controllerName', function(){});
// 我們還可以在function中加入參數及代碼實現
module.controller('controllerName', function($scope){
....
});
~~~
如果看懂了,那么恭喜你。如果沒看懂,那么就再看一遍,跟著寫一下測試代碼。當然了,如果你看了幾遍還沒有懂,我想應該是我教程的問題了。那就不要再糾結,直接看下一節好了。
- 前言
- 第一章:準備知識
- 第一節:GIT
- 第二節:Node.js
- 第三節:http-server
- 第四節:bower
- 第五節:firefox+chrome
- 第二章:官方示例教程
- 第零節:Hello Yunzhier
- 第一節:靜態模板
- 第二節:MVC
- 回調函數
- 第三節:組件
- 第四節:重構組件
- 2.4.1 調用組件
- 2.4.2 規劃目錄結構
- 2.4.3 剝離V層
- 2.4.4 大話測試
- 第五節:循環過濾器
- 第六節:雙向數據綁定
- 第七節:XHR與依賴注入
- 第八節:添加縮略圖
- 第九節:模擬頁面跳轉
- 2.9.1 使用bower
- 2.9.2 使用grunt
- 第十節:完善手機詳情頁
- 第十一節:自定義過濾器
- 第十二節:行為處理
- 第十三節:封裝請求
- 第十四節:應用動畫
- 第十五節:總結
- 第三章:菜譜管理示例
- 第四章:總結