jQuery源码简析
jQuery是一个js库 通过js封装一些方法让dom操作更简便提高开发效率
jQuery可以分为3大部分来看
第一部分jQuery的封装
在jQuery里 想要实现调用而不去污染其他变量封闭作用域当然用到了闭包配合立即执行函数来实现
在jQuery中 通过window.$ = window.jquery = jquery来把jQuery对象保存到全局上使其在别的模块中可以调用
在jQuery中 通过new jQuery.prototype.function来包装成了jQuery对象来方便方法调用
(function(){
function jQuery (select){
return new jQuery.prototype.init(select); //把构造函数保存出来 包装成实例化对象
}
());
window.$ = window.jquery = jquery //把jQuery保存到全局上方便调用
第二部分jQuery的方法封装
在jQuery方法的封装上就是在jQuery原型上写对dom的操作方法 以下是我写的一些比较典型的方法 还有比较注意的地方就是链式调用在方法最后return this来实现 在源码中方法都写在了实参里 这样可以减少在原型链上频繁调用
(function(){
function jQuery (select){
return new jQuery.prototype.init(select); //把构造函数保存出来 包装成实例化对象
}
jQuery.prototype.css = function(config){
for(var i = 0;i < this.length; i++){ //把类数组里的标签拿出来
for(var attr in config){ //把每个config拿出来
this[i].style[attr] = config[attr]; //把config 设在this[i]上
}
}
return this; //链式操作 拿到谁把谁再返回出去方便下次操作
}
jQuery.prototype.get = function(num){
// if(num == null){
// return [].slice.call(this,0);
// }else{
// if(num >= 0){
// return this[num];
// }else{
// return this.[num + this.length]
// }
// }
//简写 双重判断
return num != null ? (num >= 0 ? this[num] : this[num + this.length]) : [].slice.call(this,0);
}
jQuery.prototype.eq = function(num){
var dom = num != null ? (num >= 0 ? this[num] : this[num + this.length]): null;
return jQuery(dom) //包裹成jQuery对象
}
jQuery.prototype.add = function(select){
var curObj = jQuery(select);
var baseObj = this;
var newObj = jQuery();
for(var i = 0; i < curObj.length; i ++){
newObj[this.length++] = curObj[i]; //把curObj 放入nweObj
}
for(var i = 0; i < baseObj.length; i ++){
newObj[this.length++] = baseObj[i]; //把baseObj 放入newObj
}
return newObj;
}
jQuery.prototype.on = function(type,handle){
for(var i = 0;i < this.length;i ++){
if(!this[i].cacheEvent){
this[i].cacheEvent = {}; //创建缓存对象
}
if(!this[i].cacheEvent[type]){ //缓存中是否有type类型
this[i].cacheEvent[type] = [handle]
}else{
this[i].cacheEvent[type].push(handle); //把执行函数放进去
}
}
}
jQuery.prototype.trigger = function(type){
var params = arguments.length > 1 ? [].slice.call(arguments,1) : [];
var self = this;
for(var i = 0; i <this.length;i ++){
this[i].cacheEvent[type].foreach(function(ele,index){
ele.apply(self,params)
})
}
}
jQuery.prototype.Queue = function(){ //type 起的队列名字 handle 队列内容 type,handle
var queueObj = this; //jQuery对象
var queueName = arguments[0] || 'fx'; //传参第一位 队列起名
var addFunc = arguments[1] || null; //传参第二位 队列内容
var len = arguments.length;
if(len == 1){
return queueObj[0][queueName]; //有名字的话把名字传进来
}
//判断 dome对象有么有名字 没有名字填入名字有名字把内容填进去
queueObj[0][queueName] == undefined ? queueObj[0][queueName] = [addFunc] : queueObj[0][queueName].push(addFunc);
return this;
}
jQuery.prototype.Dequeue = function(type){
var self = this; //外部jq对象
var queueName = arguments[0] || 'fx'; //传进来的队列名
var queueArr = this.Queue(queueName); //把这个队列拿出来
var currFunc = queueArr.shift(); //把队列里的值删掉
//判断队列里有没有值了 没有值就成为递归出口
if(currFunc == undefined){
return;
}
var next = function() { //巧妙用next参数进行下一次出队
self.Dequeuee(queueName);
}
currFunc(next);
return this;
}
jQuery.prototype.Deferred = function(){
var arr = [
[
jQuery.callbacks('once memory'),'done','resolve' //done 已完成的 memory 内存 once 一次 resolve解决 决定
],[
jQuery.callbacks('once meomry'),'fail','reject' //fail 失败的 regect 拒绝的
],[
jQuery.callbacks('memory'),'progress','notify' //progress 前进中的 notify 通知
]
];
var pendding = true;
var deferred = {};
for(var i = 0 ; i < arr.length;i++){
deferred[arr[i][1]] = (function (index){
return function(func){
arr[index][1].add(func)
}
})(i);
deferred[arr[i][2]] = (function (index){
return function(func){
var args = arguments;
if(pendding){
arr[index][0].fire.apply(window,args);
arr[index][2] == 'resolve' || arr[index][2] == 'reject' ? pendding = false : '';
}
}
})(i)
}
return deferred;
}
());
window.$ = window.jquery = jquery //把jQuery保存到全局上方便调用
第三部分init()选择器的封装 也是最重要的部分
在init里是对$()选择出来的的参数进行一系列的处理这一部分比较复杂 有一部分原生js选择器 有通过正则来匹配选择内容的 还有很多