3-2 回调的灵活运用
本节编程练习不计算学习进度,请电脑登录imooc.com操作

回调的灵活运用

我们经常会这样使用函数回调:

     事件触发通知

     资源加载通知

     定时器延时

     ajax、动画通知等等。

以上都是很单一的事件监听回调的处理方式,但是jQuery把回调函数的用法设计成一个更高的抽像,用于解耦与分离变化。

如何理解这个设计?我们看下面的例子。

例子一:

jQuery针对Dom的处理提供了append、prepend、before、after等方法的处理,这几个方法的特征:

1、参数的传递可以是HTML字符串、DOM元素、元素数组或者jQuery对象

2、为了优化性能针对节点的处理需要生成文档碎片

可见几个方法都是需要实现这2个特性的,那么我们应该如何处理?

高层接口:

before: function() {
    return this.domManip(arguments, function(elem) {
        if (this.parentNode) {
            this.parentNode.insertBefore(elem, this);
        }
    });
},

after: function() {
    return this.domManip(arguments, function(elem) {
        if (this.parentNode) {
            this.parentNode.insertBefore(elem, this.nextSibling);
        }
    });
},

底层实现:

domManip: function(args, callback) {
    // Flatten any nested arrays
    args = concat.apply([], args);
    // We can't cloneNode fragments that contain checked, in WebKit
    if (isFunction ||
        //多参数处理
        self.domManip(args, callback);
    }

    if (l) {
        //生成文档碎片
        fragment = jQuery.buildFragment(args, this[0].ownerDocument, false, this);
        callback.call(this[i], node, i);
    }
    return this;
}

我们观察下jQuery的实现,通过抽象出一个domManip方法,然后在这个方法中处理共性,合并多个参数的处理与生成文档碎片的处理,然后最终把结果通过回调函数返回给每一个调用者。

例子二:

在很多时候需要控制一系列的函数顺序执行。那么一般就需要一个队列函数来处理这个问题。

我们看一段代码:

function Aaron(List, callback) {
    setTimeout(function() {
        var task;
        if (task = List.shift()) {
            task(); //执行函数
        }
        if (List.length > 0) { //递归分解
            arguments.callee(List)
        } else {
            callback()
        }
    }, 25)
}

//调用
​Aaron([
    function() {
        alert('a')
    },
    function() {
        alert('b')
    },
    function() {
        alert('c')
    }
], function() {
    alert('callback')
})

// 分别弹出 ‘a’ , ‘b’ ,'c',’callback

传入一组函数参数,靠递归解析,分个执行,其实就是靠setTimeout可以把函数加入到队列末尾才执行的原理,这样的写法就有点就事论事了,聚合对象完全是一个整体,无法再次细分出来,所以我们需要一种方案,用来管理分离每一个独立的对象。

我们换成jQuery提供的方式:

var callbacks = $.Callbacks();
callbacks.add(function() {
    alert('a');
})
callbacks.add(function() {
    alert('b');
})
callbacks.fire(); //输出结果: 'a' 'b'

是不是便捷很多了,代码又很清晰,所以Callbacks它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列。

那么我们使用回调函数,总的来说弱化耦合,让调用者与被调用者分开,调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件的被调用函数。

 

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <script src="http://img1.sycdn.imooc.com//down/540812440001e40e00000000.js" type="text/javascript"></script>
  6. <title></title>
  7. </head>
  8. <body>
  9.  
  10. <script type="text/javascript">
  11.  
  12. function Aaron(List, callback) {
  13. setTimeout(function() {
  14. var task;
  15. if (task = List.shift()) {
  16. task(); //执行函数
  17. }
  18. if (List.length > 0) { //递归分解
  19. arguments.callee(List)
  20. } else {
  21. callback()
  22. }
  23. }, 25)
  24. }
  25.  
  26. function show(data){
  27. $("body").append('<li>'+ data +'</li>')
  28. }
  29.  
  30. Aaron([
  31. function() {
  32. show('a')
  33. },
  34. function() {
  35. show('b')
  36. },
  37. function() {
  38. show('c')
  39. }
  40. ], function() {
  41. show('callback')
  42. })
  43.  
  44.  
  45. var callbacks = $.Callbacks();
  46. callbacks.add(function() {
  47. show('callbacksA');
  48. })
  49. callbacks.add(function() {
  50. show('callbacksB');
  51. })
  52. callbacks.fire();
  53.  
  54.  
  55.  
  56. </script>
  57.  
  58. </body>
  59. </html>
下一节