函数是第一类对象,这是javascript中的一个重要的概念,意味着函数可以像对象一样按照第一类管理被使用,所以在javascript中的函数:

能"存储"在变量中,能作为函数的实参被传递,能在函数中被创建,能从函数中返回

jquery中回调的设计

异步回调:

事件句柄回调:

$(document).ready(callback);

$(document).on('click',callback);

Ajax异步请求成功失败回调

$.ajax({

  url:"",

  context:document

}).done(function(){

  //成功执行

}).fail(function(){

  //失败执行

});

动画执行完毕回调

$("#a").click(function(){

  $("#b").animate({

    left:'+=50',

    opacity:0.25,

    height:'toggle'

  },5000,function(){

  这里是回调

});

});

同步回调

一个同步(阻塞)中使用回调的例子,目的是在test代码执行完成后执行回调callback

var test1 = function(callback){

  //执行长时间操作

  callback();

}

test1(function(){

  //执行回调中的方法

});

所以理解回调函数最重要的2点:

1,一个回调函数作为参数传递给另一个函数时,我们仅仅传递了函数定义,我们并没有在参数中执行函数,我们并不传递我们平时执行函数一样带有一对执行小括号的函数。

2,回调函数并不会马上被执行,他会在包含它的函数内的某个特定的时间点被"回调"。

回调的运用

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

时间触发通知,资源加载通知,定时器延时,ajax,动画通知等等。

例1:

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

1,参数的传递可以是html字符串,DOM元素,元素数组或者jquery对象。

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.partentNode.insertBefore(elem,this.nextSibling);

  }

});

}

底层实现

domMainp:function(args,callback){

  args = concat.apply([],args);

  if(isFunction || self.doManip(args,callback));  

  if(l){

    //生成文档碎片

    fragment = jquery.buildFragment(args,this[0].ownerDocument,false,this);

    callback.call(this[i],node,i);

  }

  return this;

}

例子2

  1. function Aaron(List, callback) {
  2.     setTimeout(function() {
  3.         var task;
  4.         if (task = List.shift()) {
  5.             task(); //执行函数
  6.         }
  7.         if (List.length > 0) { //递归分解
  8.             arguments.callee(List)
  9.         } else {
  10.             callback()
  11.         }
  12.     }, 25)
  13. }
  14.  
  15. //调用
  16. Aaron([
  17.     function() {
  18.         alert('a')
  19.     },
  20.     function() {
  21.         alert('b')
  22.     },
  23.     function() {
  24.         alert('c')
  25.     }
  26. ], function() {
  27.     alert('callback')
  28. })
  29.  
  30. // 分别弹出 ‘a’ , ‘b’ ,'c',’callback
    传入一组函数参数,靠递归解析,分个执行,其实就时靠setTimeout可以把函数加入到队列末尾才执行的原理,聚合对象完全是一个整体,无法再次细分出来,所以我们需要一种方案,用来管理分离每一个独立的对象。
    换成jquery提供的方式
    var callbacks = $.Callbacks();
    callbacks.add(function(){alert(1)};);
  1. callbacks.add(function(){alert(2)};);
    callbacks.fire();//1 2
  2.  
  3. 模式的实际运用
    $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      $("").html(data.a);
    });
    优化
    $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      pocessData();
      pocessHtml();
      pocessOther();
    });
    function pocessData(){
      //处理数据
    }
    function pocessHtml(){
      $().html(data.a);
    }
    function pocessOther(){
      //处理其他逻辑
    }
    优化的好处是 分离出各种的业务函数,从而降低了代码之间的耦合度, 但是这样代码写法达不到代码复用的作用
    用观察者模式
    Observable.add(function(){
      //pocessdata
    });
    Observable.add(function(){
      $().html(data.a);
    })
    Observable.add(function(){
      //pocessOther
    })
  4.  
  5. $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      Observable.fire(data);
    })
    jquery回调对象
    jquery.Callbacks是在1.7版本后加入的,主要用来进行函数队列的addremovefirelock等操作, 并提供oncemomoryunique,stopOnFalse四个option进行一些特殊的控制
  6.  
  7. $.Callbacks是一个工厂函数,使用函数调用(非new,他不是一个类),他又一个可选参数flags用来设置回调函数的行为,对外的接口也就是self的返回。
    callbacks.add():回调列表中添加一个回调或回调的集合。
    callbacks.disable():禁用回调列表中的回调
    callbacks.disabled():确定回调列表是否已被禁用
    callbacks.empty():从列表中删除所有的回调
    callbacks.fire():用给定的参数调用所有的回调
    callbacks.fired():访问给定的上下文和参数列表中的所有回调
    callbacks.fireWith():访问给定的上下文和参数列表中的所有回调
    callbacks.has():确定列表中是否提供一个回调
    callbacks.lock():锁定当前状态的回调列表
    callbacks.locked():确定回调列表是否已被锁定
    callbacks.remove():从回调列表中的删除一个回调或回调集合。
  8.  
  9. 源码结构
    jQuery.Callbacks = function(options){
  1.   options = typeof options === "string" ? (optionsCache[options] || createOptions(options)): jQuery.extend({},option);
      //实现代码
      fire = function(){}
      self = {
        add : function(){},
        remove : function(){},
        has : function(){},
        empty : function(){},
        disable : funciton(){},
        disabled : function(){},
        lock:function(){},
        locked:function(){},
        fireWith:function(){},
        fire:function(){}
      };
      return self;
    };
    整个结构要分为三个部分:options参数缓存,内部fire触发器的设计,外部。
    参数的缓存设计:Callbacks是可以接受字符串的组合参数,可以使用空格分割
  1. var opts = 'unique memory';
  2. var object = {}
  3. jQuery.each(opts.match(/\S+/g) || [], function(_, flag) {
  4.   object[flag] = true;
  5. });

这样的操作其实是不需要重复的,所以我们可以设计一个缓存池,用来储存重复的操作:

  1. var optionsCache = {};
  2. function createOptions(options) {
  3. var object = optionsCache[options] = {};
  4. jQuery.each(options.match(rnotwhite) || [], function(_, flag) {
  5. object[flag] = true;
  6. });
  7. return object;
  8. }

所以我们传递参数的时候,如果参数是字符串,我们可以直接从optionsCache缓存中去查找:

  1. options = typeof options === "string" ?
  2.         ( optionsCache[ options ] || createOptions( options ) ) :
  3.         jQuery.extend( {}, options );
  4.  
  5. 默认回调对象设计
    function Callbacks(){
      var list = [];
      var self;
      self = {
        add:function(fn){
          list.push(fn);  
          },
          fire:function(args){
            list.foreach(function(fn){
            fn(args);
          })
        }  
      }
      return self;
    }
  6.  
  7. once的设计
    once的作用确保回调列表只执行(.fire())一次(像一个递延Deferrend
    function fn1(val){
      console.info(1);
    }
    var cbs = $.Callbacks("once");
    cbs.add(fn1);
    cbs.fire("foo");
    cbs.fire("foo");
    cbs只会执行一次,once定义是很明确的,确保这个回调列表只执行(.fire())一次,所以针对这种once的处理可以有多种不同的途径实现。
    1add的时候抛弃
    2,在fire的时候抛弃多个
    但是jquery是在执行第一个fire的时候直接给清空list列表了,然后在add的地方给判断下list是否存在,从而达到这样的处理。
    function Callbacks(options){
      var list = [];
      var self;
      self = {
        add:function(){
          list,push(fn);
        },
        fire:function(args){
          if(list){
            list.forEach(function(fn){
              fn(args);
            })
            if(options === "once"){
              list = undefined;  
            }
          }
        }
      }
      return self;
    }
    fire之后,判断参数是否为once,直接把list给清理掉,所以之后的所有fire都被抛弃了,从而到达了once的效果。
    jQuery.Callbacks的处理
    fire中调用了self.disable()方法
    disable:function(){
      list = stack = memory = undefined;
      return this;
    }
  1. memory的设计
    memory:保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调。
    unique的设计
    unique:确保一次只能添加一个回调
    stopOnFalse
    stopOnFalse:当一个回调返回false时中断调用。
  1.  

JavaScipt 源码解析 回调函数的更多相关文章

  1. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

  2. JavaScipt 源码解析 异步

    我们常见的异步操作: 定时器setTimeout postmessage WebWorkor CSS3 动画 XMLHttpRequest HTML5的本地数据 等等- JavaScript要求在与服 ...

  3. JavaScipt 源码解析 数据缓存

    常见的内存泄露的几种情况: 循环引用 JavaScript闭包 DOM插入 一个DOM对象被一个JavaScript对象引用,同时又引用同一个或其他的JavaScript对象,这个DOM对象可能回引发 ...

  4. underscore.js源码解析【函数】

    // Function (ahem) Functions // ------------------ // Determines whether to execute a function as a ...

  5. JavaScipt 源码解析 Sizzle选择器

    jQuery的定位就是一个DOM的操作库,那么可想而知选择器是一个至关重要的模块.Sizzle,作为一个独立全新的选择器引擎,出现在jQuery 1.3版本之后,并被John Resig作为一个开源的 ...

  6. JavaScipt 源码解析 css选择器

    css1-css3提供了很多选择器,总得来说分为几大类: 群组选择器:逗号"," 简单选择器:ID,标签,类,属性,通配符 关系选择器:孩子,后代,兄弟,相邻 伪类选择器:动作伪类 ...

  7. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  8. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  9. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

随机推荐

  1. codis配置

    codis集群配置 Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表) ...

  2. 使用百度编辑器时,报错:从客户端("...)中检测到有潜在危险的 Request.Form 值

    ajax请求失败,提交不上去 起初没找到报错的这句话,可能由于我用了其他第三方插件的原因,后来改动了一下,出现了这个错误 然后百度得到答案:http://www.cnblogs.com/tearer/ ...

  3. JQuery兼容IE6问题汇总(不断更新)

    兼容IE6真是苦逼的差事,无奈中... 逗号的问题:IE6中要去掉最后的逗号 var o={ id:1, Name:"abc", //这里的逗号一定要去掉 } HTML的结构,由于 ...

  4. 湖大OJ-实验E----可判定的DFA的空问题

    实验E----可判定的DFA的空问题 Time Limit: 1000ms, Special Time Limit:2500ms, Memory Limit:32768KB Total submit ...

  5. React,js实现分页的案列

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  6. android项目的结构和布局

    一.res文件夹 1.res文件夹用于存放Android的资源.包括:动画.静态图片.字符串.菜单.布局.视频.文件等. 1.drawable-ldpi:低分辨率图形(120像素/英寸) 2.draw ...

  7. [课程设计]Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)

    Scrum 1.5 多鱼点餐系统开发进度(点餐页面框架修复及继续布置)  1.团队名称:重案组 2.团队目标:长期经营,积累客户充分准备,伺机而行 3.团队口号:矢志不渝,追求完美 4.团队选题:餐厅 ...

  8. 提高mysql插入性能

    参考地址:http://blog.jobbole.com/29432/ 参数优化: 1. SQL语句是有长度限制,在进行数据合并在同一SQL中务必不能超过SQL长度限制,通过max_allowed_p ...

  9. OpenCV安装配置的简单记录

    在ubuntu16.04下安装OpenCV 2.4.11的简单记录 1. 安装cmake,执行$apt-get install cmake即可,cmake -version验证 2. 下载OpenCV ...

  10. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序实现继承

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十一篇:为ASP.NET MVC应用程 ...