我们在学习其它语言时,在调用一个函数时,往往是如下这样的:
~~~
// 定义
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>
~~~
测试:
![](https://box.kancloud.cn/2016-07-20_578f39333dfd4.png)
没错,在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');
});
~~~
执行结果:
![](https://box.kancloud.cn/2016-07-20_578f39335db2f.png)
## 为什么要用回调
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
- 第十节:完善手机详情页
- 第十一节:自定义过滤器
- 第十二节:行为处理
- 第十三节:封装请求
- 第十四节:应用动画
- 第十五节:总结
- 第三章:菜谱管理示例
- 第四章:总结