原文链接: segmentfault.com
本文章记录本人在学习 JavaScript 中看书理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习。
什么是回调函数
在js
里函数都是对象,这表示它们可以作为参数传递给其他的函数。举例:当函数b()
作为参数传递给函数a()
,那么在某一时刻函数a()
可能会执行或者调用函数b()
。这种情况下,函数b()
就被称为回调函数,也可以简称叫做回调(下面是栗子)。
'use strict'; function a(callback) { callback(); } function b() { console.log('hello callback'); } a(b); // 注意 b() 作为参数传递给 a() 的时候是不需要带括号的
回调函数示例
假设有一个通用的函数执行一些复杂的处理工作,并且返回结果为一个大块数据(下面是栗子)。
var findeNodes = function () { var i = 10000, node = [], found; while (i) { i -= 1; node.push(found); } return nodes; }
上面代码定义函数findNodes()
,它的任务是抓取页面的dom tree
,并返回一个你要的页面元素数组。
在定义一个函数hide()
,顾名思义它的作用是隐藏页面中的节点(下面是栗子)。
var hide = function (nodes) { for (var i = 0; i < nodes.length; i += 1) { nodes[i].style.display = 'block'; } } // 执行函数 hide(findeNodes());
好了,要的效果都实现了,但是实现却是低效的。因为hide()
必须再次循环由findNodes()
返回的数组节点。如果能避免这种循环,只需要在findNodes()
中选择便可隐藏节点,那么这将是更高效的方式。但是如果在findNodes()
中实现隐藏逻辑的话,由于检索和修改逻辑耦合,那么他不再是通用的函数。面对这种问题的解决方法是采用回调模式,可以将因此节点逻辑以回调函数方式传递给findNodes()
并委托执行(下面是栗子)。
// 重构 findeNodes() 函数,并接受一个回调函数 var findeNodes = function (callback) { var i = 10000, nodes = [], found; while (i) { i -= 1; // 现在运行回调函数 if (typof callback === 'function') { callback(); } nodes.push(found); } return found; }
上面的代码只是做了对callback
是否存在进行了判断,如果存在的话吗,那么就执行该函数。其中,回调函数是可选的,因此后续的findeNodes()
仍然可以想以前一样使用,而不会破坏以来旧API
的原始代码。
现在hide
的实现就简单多了,因为它不需要再去循环遍历所有的节点了(下面是栗子)。
// 回调函数 var hide = function (nodes) { nodes.style.display = 'none'; } // 找到指定的节点,并在后续执行中隐藏 findeNodes(hide);
回调函数与作用域
在上一个栗子中,回调函数执行的语句是这样的:callback(parameters)
。
虽然上面那句在大多数的场景上都是有效的,但是总会有一些场景,其回调函数并不是一次性的匿名函数或者全局函数,而是对象的方法。如果该函数使用this
来引用它所的属性,这可能有是一个坑了(下面是栗子)。
// 假设回调函数是 paint(),它是一个名为 myapp 的对象的方法 var myapp = {}; myapp.color = 'green'; myapp.paint = function (nodes) { nodes.style.color = this.color; } // 然后用到上一个栗子的 findeNodes() findeNodes(myapp.paint); //
坑:this.color
没有被定义,因为findeNods()
是一个全局函数,因此,对象的this
是指向window
的。
解决的方法有:
- 另外传递该回调函数的所属对象。这样就需要修改一下
findNodes()
函数(下面是栗子)。
var findNodes = function (callback, callback_obj) { // ... if (typof callback === 'function') { callback.call(callback_obj, found); } // ... } // 执行函数 findNodes(myapp.paint, myapp);
主要是通过call、apply
来修改函数运行时的this
指向。
- 然后接着修改上面的方法,将方法作为字符串传递,因此就不用两次输入该对象的名称(下面是栗子)。
var findNodes = function (callback, callback_obj) { // ... if (typof callback === 'string') { callback = callback_obj[callback]; } // ... if (typof callback === 'function') { callback.call(callback_obj, found); } // ... } // 执行函数 findNodes('paint', myapp);
最后,如果文章有什么错误和疑问的地方,请指出。与sf各位共勉!