函数是第一类对象,这是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时中断调用。