函数就是对象,所以他们可以作为一个参数传递给其它函数;
当你将introduceBugs()作为一个参数传递给writeCode(),然后在某个时间点,writeCode()有可能执行(调用)introduceBugs();
这种情况下,introduceBugs()被称为回调函数(callback function)或简称为回调(callback:):
function writeCode(callback) { // do something... callback(); // ... } function introduceBugs() { // ... make bugs } writeCode(introduceBugs);
注意introduceBugs()作为一参数传递给writeCode()是没有使用括号的;
使用括号会立即执行函数,然而在这种情况下,我们希望的是只传递一个指向函数的引用,让writeCode()在适当的时候去执行;
一个回调的例子(A Callback Example)
var findNodes = function() { var i = 100000, // big, heavy loop nodes = [], // stores the result found; // the next node found while (i) { i -= 1; // complex logic here... nodes.push(found); } return nodes; };
将这个函数保持通用性并让它返回一个DOM节点(node)的数组是个好主意,但没有对实际的元素做任何事情;
var hide = function(nodes) { var i = 0, max = nodes.length; for (; i < max; i += 1) { nodes[i].style.display = "none"; } }; // executing the functions hide(findNodes());
这种实现是没有效率的,因为hide()不得不再遍历一次findNodes()返回的的数组;
// refactored findNodes() to accept a callback var findNodes = function(callback) { var i = 100000, nodes = [], found; // check if callback is callable if (typeof callback !== "function") { callback = false; } while (i) { i -= 1; // complex logic here... // now callback: if (callback) { callback(found); } nodes.push(found); } return nodes; };
这样的实现是简单明确的,唯一增加的工作就是findNodes()检查了可选的回调函数是否有被提供,如果有,就执行它;
// a callback function var hide = function(node) { node.style.display = "none"; }; // find the nodes and hide them as you go findNodes(hide);
回调函数可以是一个在代码中已经存在的函数,也可以是一个匿名函数(当你调用主函数的时候才会创建);
// passing an anonymous callback findNodes(function (node) { node.style.display = "block"; });
回调和作用域(Callbacks and Scope)
在前面这个例子中,回调函数执行的部分可能像:
callback(parameters);
虽然这样很简单并且在很多情况下都已经足够了;
var myapp = {}; myapp.color = "green"; myapp.paint = function(node) { node.style.color = this.color; };
findNodes()函数做了类似下面的事:
var findNodes = function(callback) { // ... if (typeof callback === "function") { callback(found); } // ... };
如果你调用了findNodes(myapp.paint),它并不能按照预期的那样工作,因为this.color将会是undefined;
findNodes(myapp.paint, myapp);
紧跟着,我们需要去修改findNodes()去绑定(bind)传递进来的对象:
var findNodes = function(callback, callback_obj) { //... if (typeof callback === "function") { callback.call(callback_obj, found); } // ... };
findNodes(myapp.paint, myapp);
会变成:
findNodes("paint", myapp);
那么findNodes()可能会做一些事,就像下面几行:
var findNodes = function(callback, callback_obj) { if (typeof callback === "string") { callback = callback_obj[callback]; } //... if (typeof callback === "function") { callback.call(callback_obj, found); } // ... };
匿名的事件监听器(Asynchronous Event Listeners)
回调模式在日常中被经常使用,比如,当你附加一个事件监听器给页面上的某个元素时,你实际上提供了一个指向了回调函数的引用,并且在事件发生时被调用;
document.addEventListener("click", console.log, false);
绝大部分客户端浏览器都是事件驱动的(event-driven);
因为回调模式,JavaScript特别适合事件驱动编程,能让你的程序异步的工作,换言之,就是不受顺序限制。
Timeouts
var thePlotThickens = function () { console.log('500ms later...'); }; setTimeout(thePlotThickens, 500);
再次注意一下,thePlotThickens是如何被作为一个参数传递的,没有使用括号;