jQuery 源码解析代码及更多学习干货: 猛戳GitHub前端
本篇代码为git
my-jQuery 1.0.2.jsgithub
my-jQuery 1.0.3.jsajax
建议下载源码而后据文章思路学习,最好本身边思考边多敲几遍。设计模式
1.$.callbacks
用于管理函数队列。
2.经过add()
添加处理函数到队列中,经过fire去执行这些函数。
3.$.callbacks
是在jQuery内部使用的,如为.ajax,$.Deffed等组件提供基础功能函数。它也能够在相似功能的一些组件中,如本身开发的插件数组
$.callbacks
,获取实例add()
向内部队列添加函数fire()
依次找到并执行队列里的函数var cb = $.callbacks();
cb.add(function(){
console.log('add one');
});
cb.add(function(){
console.log('add two');
});
cb.add(function(){
console.log('add three');
});
cb.fire()
//依次输出
add one
add two
add three
复制代码
callbacks经过字符串参数的形式,支持四种特定的功能bash
once
// once关键字
var cbOne = $.Callbacks('once');
cbOne.add(function(){
console.log("this is cbOne1");
});
cbOne.add(function(){
console.log("this is a cbOne2");
});
// 只输出执行一次,后面调用都不生效
cbOne.fire();
cbOne.fire();
执行结果:
this is cbOne1
this is a cbOne2
复制代码
unique
// unique
var cbUnique = $.Callbacks('unique');
function demo(){
console.log("this is a cbUnique");
}
cbUnique.add(demo,demo);
cbUnique.fire();
// 输出了一次
this is a cbUnique
复制代码
stopOnFalse
// stopOnFalse 关键字
// 不加关键字的状况
var cbDemo = $.Callbacks();
cbDemo.add(function(){
console.log("this is cbDemo 1");
return false;
},function(){
console.log("this is cbDemo 2");
})
cbDemo.fire();
输出:
this is cbDemo 1
this is cbDemo 2
// 加关键字的状况
var cbStopOnFalse = $.Callbacks('stopOnFalse');
cbStopOnFalse.add(function(){
console.log("this is a cbStopOnFalse 1");
return false;
},function(){
console.log("this is a cbStopOnFalse 2");
});
cbStopOnFalse.fire();
输出:
this is a cbStopOnFalse 1
复制代码
memory
// 参数 memory
// 不加参数的状况
var cbNoMemory = $.Callbacks();
cbNoMemory.add(function(){
console.log("this is a cbNoMemory 1");
});
cbNoMemory.fire();
输出:
this is a cbNoMemory 1
cbNoMemory.add(function (){
console.log("this is a cbNoMemory 2");
});
// 添加参数的状况
var cbMemory = $.Callbacks('memory');
cbMemory.add(function(){
console.log("this is a cbMemory 1");
});
cbMemory.fire();
输出:
this is a cbMemory 1
this is a cbMemory 2
cbMemory.add(function(){
console.log("this is a cbMemory 2");
})
复制代码
1.事件一般与函数配合使用,这样就能够经过发生的事件来驱动函数的执行.app
原则:一个事件对应一个事件函数 在一个事件对应多个函数的状况下,后者会覆盖掉前者。dom
问题: 那么咱们可否有一种方案来改变一对一的事件模型呢?函数
解决方案: 把事件放到一个数组里,而后经过遍历数组依次执行的方式来达到一对多的事件模型。
// 一对多事件模型
function one(){
console.log("one");
};
function two(){
console.log("two");
};
function three(){
console.log("three");
};
function four(){
console.log("four");
};
var clickCallBack = [one,two,three,four];
// 在body中定义一个button <button id="btn">按钮</button>
$("#btn").click(function(){
var _this = this;
clickCallBack.forEach(function(fn){
fn.call(_this);
})
});
// 输出结果:
one
three
three
four
复制代码
2.Callbacks 不只仅是一个数组,能够把它当作一个容器。
上面咱们已经经过jQuery来调用Callbacks的API并输出了内容,根据两个方法,add(),fire()
及四个参数,"once","unique","menory","stopOnfalse"
,的相关特性咱们开始反推实现过程.
首先是add()
方法:将穿过来的options先把他们转为真数组,而后将数组遍历出来挑选出类型为"Function"的数据,将数据添加到一个空数组中,等待执行。
仅是代码片断,完整代码下载地址: 源码下载
核心代码片断:
add:function(){
// Array.prototype.slice.call(arguments 伪数组转真数组
var args = Array.prototype.slice.call(arguments);
start = list.length;
// 遍历args 找出里面的Function
args.forEach(function(fn){
// 检索fn是是不是Function
if (toString.call(fn) === "[object Function]") {
// unique 不存在 且fn在list中 那么能够把fn添加到队里中
list.push(fn);
}
}
});
复制代码
fire()
方法:fire其实就是把添加到队列中的方法依次按规则输出执行,须要一个中间件fireWith提供上下文。
核心代码:
var fire = function(data){
index = 0;
length = list.length;
// 遍历循环list
for(; index < length; index++){
// 经过遍历查找list[index]的值为false 且options有stopOnfalse这个参数时遍历终止返回
if (list[index].apply(data[0],data[1]) == false){
break;
}
}
}
复制代码
// 定义一个上下文绑定函数
fileWith:function(context,arguments){
var args = [context,arguments];
fire(args);
}
复制代码
fire:function(){
self.fileWith(this,arguments);
},
复制代码
到此以上代码能够实现 add()
方法和fire()
方法,其次咱们在考虑四种参数的状况,首先咱们先考虑 stopOnfalse
的状况.
stopOnfalse这个参数生效的阶段是在调用fire()方法后执行 add()
添加的队列函数中是否有返回false的状况,因此首先咱们应该想到在fire()这个方法里作文章.
思路:直接在遍历方法的时候来断定optionss是否有 stopOnfalse
参数若是有立马退出.
核心代码:
var fire = function(data){
// memory
memory = options.memory && data;
// 为了防止memory再次调用一次定义了starts
index = 0;
length = list.length;
// 遍历循环list
for(; index < length; index++){
// 经过遍历查找list[index]的值为false 且options有stopOnfalse这个参数时遍历终止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
复制代码
once 参数生效的状况是,当once
存在执行第一次完成后,若是还有fire()
方法,那么就直接退出不执行.
思路:首先明白受影响阶段是fire()
, 定义一个参数来记录第一次执行fire()
的方法,而后在调用执行fire()
这个方法判断是否传入有 once
参数若是有,那么就不会再去执行fire()
方法.
核心代码:
var fire = function(data){
index = 0;
length = list.length;
startAdd = true;// 用来记录fire()方式是否执行 便于"once"方法操做
// 遍历循环list
for(; index < length; index++){
// 经过遍历查找list[index]的值为false 且options有stopOnfalse这个参数时遍历终止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
}
复制代码
// 定义一个上下文绑定函数
fileWith:function(context,arguments){
var args = [context,arguments];
// 非fire作限制调用
if(!options.once || !startAdd) {
fire(args);
}
},
复制代码
memory这个参数生效的状况是,若是执行 fire()
方法后,还存在 add()
的方法,那么后面的 add()
方法依然有效。
思路: 首先要搞明白memory在哪一个阶段会受影响,在add()
阶段和fire(
)阶段都有影响,add()
阶段要记录传入的options是否有memory这个参数,其次在执行fire()
的阶段,主要是要记录住它的index值。
核心代码:
var fire = function(data){
// memory
memory = options.memory && data;
// 为了防止memory再次调用一次定义了memoryStarts
index = memoryStarts || 0;
start = 0;
length = list.length;
startAdd = true; // 用来记录fire()方式是否执行 便于"once"方法操做
// 遍历循环list
for(; index < length; index++){
// 经过遍历查找list[index]的值为false 且options有stopOnfalse这个参数时遍历终止返回
if (list[index].apply(data[0],data[1]) == false && options.stopOnfalse){
break;
}
}
}
复制代码
// memory
if (memory) {
memoryStarts = start;
fire(memory);
}
复制代码
最难的是 unique 这个参数.
unique
这个参数生效的状况是,同一个方法被add()
屡次,仅执行一次改方法。
思路: unique影响阶段是add()
时候,全部咱们在这里作拦截操做是最好的,所以咱们在add()
的时候作判断若是存在 unique
这个参数,那么咱们就不让一样的这个方法push到队里中,没有添加到队列,那么咱们就不会再次执行这个方法啦.
两种方法:
(1).经过数组的[].indexOf.call()
来查看是否存在于数组中,不存在返回-1.
(2).能够用ES6的set进行过滤重复值
咱们采用方法一来完成此操做。
核心代码:
// 添加 方法
add:function(){
// Array.prototype.slice.call(arguments 伪数组转真数组
var args = Array.prototype.slice.call(arguments);
start = list.length;
// 遍历args 找出里面的Function
args.forEach(function(fn){
// 检索fn是是不是Function
if (toString.call(fn) === "[object Function]") {
// unique 不存在 且fn在list中 那么能够把fn添加到队里中
// 处理 unique 参数
if(!options.unique || !self.has(fn,list)) {
list.push(fn);
}
}
});
复制代码
has:function(fn,array){
return arr = jQuery.inArray(fn,array) > -1;
}
复制代码
jQuery.inArray = function (elem,arr){
return arr == null?-1:[].indexOf.call(arr,elem);
}
复制代码
至此,大功告成!! 完成了Callbacks()
实现原理剖析,你是学到了呢?
jQuery 源码剖析 系列目录地址:猛戳GitHub
jQuery 源码剖析 系列预计写十篇左右,旨在加深对原生JavaScript 部分知识点的理解和深刻,重点讲解 jQuery核心功能函数、选择器、Callback 原理、延时对象原理、事件绑定、jQuery体系结构、委托设计模式、dom操做、动画队列等。 若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star⭐️,对做者也是一种鼓励。
关注公众号回复:学习 领取前端最新最全学习资料,也能够进群和大佬一块儿学习交流