何为回调函数
1 setTimeout(function () { 2 console.log('callback...'); 3 }, 1000);
此延时定时器中的function就是我们常说的回调函数,回调函数常常满足三个特征
- 我们自己定义的
- 我们自己没去执行
- 最终被其它人(浏览器的ajax模块,定时器模块...)执行了
何为回调地狱
1 setTimeout(function (name) { 2 var catList = name + ','; 3 4 setTimeout(function (name) { 5 catList += name + ','; 6 7 setTimeout(function (name) { 8 catList += name + ','; 9 10 setTimeout(function (name) { 11 catList += name + ','; 12 13 setTimeout(function (name) { 14 catList += name; 15 16 console.log(catList); 17 18 }, 1, 'Lion'); 19 20 }, 1, 'Snow Leopard'); 21 22 }, 1, 'Lynx'); 23 24 }, 1, 'Jaguar');}, 1, 'Panther');
由于回调函数是异步的,在上面的代码中每一层的回调函数都需要依赖上一层的回调执行完,所以形成了层层嵌套的关系最终形成类似上面的回调地狱,但代码以此种形式展现时无疑是不利于我们阅读与维护的,此时就要想办法改进这种代码形式。
初次改进
var catList = ''; setTimeout(getPanther, 1, 'Panther'); function getPanther(name) { catList += name + ','; setTimeout(getJanguar, 1, 'Janguar'); } function getJanguar(name) { catList += name + ','; setTimeout(getLynx, 1, 'Lynx'); } function getLynx(name) { catList += name + ','; setTimeout(getSnowLeopard, 1, 'Snow Leopard'); } function getSnowLeopard(name) { catList += name + ','; setTimeout(getLion, 1, 'Lion'); } function getLion(name) { catList += name; setTimeout(print, 1); } function print() { console.log(catList); }
将匿名函数命名
虽然匿名函数使用方便,但是不利于我们阅读与书写,所以我们选择将回调函数单独的拿出来并命名,写成如上形式,这样看上去就要比第一次的代码优雅清晰很多。需要注意的是上面的代码相比之前有了几点改变。
1. catList变成了全局变量
2. 函数的作用域发生了变化,从局部作用域变成了全局作用域,所以才需要将catList变成全局变量以保证每个函数都可以访问的到
通常情况下我们不希望有全局变量污染环境,并且上面的代码还有一些可以复用的空间,可以看到我们在每个函数中都重复的调用定时器,所以我们可以再来改进一下上面的代码。
再次改进
1 function buildCatList(list, returnVal, fn) { 2 setTimeout(function (name) { 3 var catList = list === '' ? name : list + ',' + name; 4 fn(catList); 5 }, 1, returnVal); 6 } 7 8 9 buildCatList('', 'Panther', getJanguar); 10 11 function getJanguar(list) { 12 buildCatList(list, 'Janguar', getLynx); 13 } 14 15 function getLynx(list) { 16 buildCatList(list, 'Lynx', getSnowLeopard); 17 } 18 19 function getSnowLeopard(list) { 20 buildCatList(list, 'Snow Leopard', getLion); 21 } 22 23 function getLion(list) { 24 buildCatList(list, 'Lion',print); 25 } 26 27 function print(list) { 28 console.log(list); 29 }
定时器中的回调函数处在外层函数中的作用域内,并通过参数传入,没有产生全局变量,没有重复代码。