函数是第一类对象,这是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

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(1)};);
callbacks.add(function(){alert(2)};);
callbacks.fire();//1 2 模式的实际运用
$.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
}) $.ajax({
  url:"",
  context:document.body
}).done(function(data){
  Observable.fire(data);
})
jquery回调对象
jquery.Callbacks是在1.7版本后加入的,主要用来进行函数队列的add,remove,fire,lock等操作, 并提供once,momory,unique,stopOnFalse四个option进行一些特殊的控制 $.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():从回调列表中的删除一个回调或回调集合。 源码结构
jQuery.Callbacks = function(options){
  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是可以接受字符串的组合参数,可以使用空格分割
var opts = 'unique memory';
var object = {}
jQuery.each(opts.match(/\S+/g) || [], function(_, flag) {
  object[flag] = true;
});

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

var optionsCache = {};
function createOptions(options) {
var object = optionsCache[options] = {};
jQuery.each(options.match(rnotwhite) || [], function(_, flag) {
object[flag] = true;
});
return object;
}

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

options = typeof options === "string" ?
        ( optionsCache[ options ] || createOptions( options ) ) :
        jQuery.extend( {}, options ); 默认回调对象设计
function Callbacks(){
  var list = [];
  var self;
  self = {
    add:function(fn){
      list.push(fn);  
      },
      fire:function(args){
        list.foreach(function(fn){
        fn(args);
      })
    }  
  }
  return self;
} 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的处理可以有多种不同的途径实现。
1,add的时候抛弃
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;
}
memory的设计
memory:保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调。
unique的设计
unique:确保一次只能添加一个回调
stopOnFalse
stopOnFalse:当一个回调返回false时中断调用。
												

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. iis 重新注册 .net 方法

    dhl:IIS注册ASP.NET 1.1.2.0.4.0_在win7下如果先安装vs2010 后安装iis7的话,必须注册iis才可以用.~~~!!鄙视微软   IIS中ASP.NET的版本号此时可选 ...

  2. ubuntu 常用命令集合版(二)【大侠勿喷,菜鸟欢迎】(转)

    原文:http://page.renren.com/600759338/note/729595757 1.shutdown: 关闭系统,如果停留在TTY,请改用halt, poweroff等命令常用参 ...

  3. <<卸甲笔记>>-基础语法对比

    以Oracle中sottt用户下的数据为例,PPAS 中scott用户下面的数据由Oracle迁移而来 1 查询emp表中的数据 Oracle [root@test03 ~]# su -  oracl ...

  4. A New Effect About My Plugin render

  5. 初试“七牛云”--零基础运用七牛云配合UEditor实现图片的上传和浏览(.NET篇)

    (注册和建立存储空间就不介绍了,网上一把一把的资料,自己试着点点也能明白) 作为一个成熟的菜鸟,如果遇到一个新问题,第一步当然是先百度一下... 看了N个关于七牛云的使用的帖子,表示还是蒙圈的,看懂了 ...

  6. Cookie与Session的区别

    cookie机制 Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器.IETF RFC 2965 HTTP State Management Mechanism 是通用c ...

  7. Going from u to v or from v to u?_POJ2762强连通+并查集缩点+拓扑排序

         Going from u to v or from v to u? Time Limit: 2000MS   Memory Limit: 65536K       Description I ...

  8. mfc通过消息传递参数进行程序间通信

    这样的程序主要实现windows平台下两个应用程序间的通信. 最简单的单元分为两个部分,一个是引用的程序,一个是被引用的程序. 一.如果引用他人,需要传递参数找到运行的程序,并且传递消息过去 void ...

  9. Unix网络编程--卷二:FAQ

    1.编译unpipc库. 执行./configure时报错: checking host system type... Invalid configuration `x86_64-pc-linux-g ...

  10. TCP/IP协议(二)

    2014-09-04 11:03:27   注:关于seq 和 ack 的理解,seq为发送的字节的第一个序号,一直累加,ack接收字节的最后一个序号+1,建立连接和结束连接时的SYN.FIN标志位占 ...