在学习jQuery的过程中,我们不仅需要掌握使用方法,更重要的是理解方法实现原理,在学习过程中,跟着老师整理了jQuery中常用方法的实现(仅仅实现了部分功能),有助于我们更灵活、更全面的掌握jQuery。
(function(){
function jQuery(selector){
return new jQuery.prototype.init(selector);//返回以init为构造函数new出来的对象
}
jQuery.prototype.init = function(selector){
// var this = {}
// 根据选择器选出dom元素, 并且包装成jQuery对象并返回
// null undefined
// id class
// dom
this.length = 0;
if(selector == null){
return this;
}
if(typeof selector == 'string' && selector.indexOf('.') != -1){
var dom = document.getElementsByClassName(selector.slice(1));
}else if(typeof selector == 'string' && selector.indexOf('#') != -1){
var dom = document.getElementById(selector.slice(1));
}
// if(selector instanceof Element){//如果selector是dom对象
// this[0] = selector;
// this.length++;
// }
if(selector instanceof Element || dom.length == undefined){//这儿揉了两种情况
this[0] = dom || selector;
this.length++;
}else{
for(var i = 0; i < dom.length; i++){
this[i] = dom[i];
this.length++;
}
}
//return this;
}
jQuery.prototype.css = function(config){//jQuery对象调用css方法,this指向jQuery对象
//循环操作选出来的每一个dom
for(var i = 0; i < this.length; i++){
for(var attr in config){
this[i].style[attr] = config[attr];//这儿没写 +'px', 使用的时候需要写上
}
}
return this;//链式操作精髓
}
jQuery.prototype.pushStack = function(dom){//给jQuery对象加上prevObject
if(dom.constructor != jQuery){
dom = jQuery(dom);
}
dom.pervObject = this; //prevObject指向的是之前的jQuery对象, 不太懂的话看end()就懂了
return dom;
}
jQuery.prototype.get = function(num){//从jQuery对象中获取原生的dom对象
// if(!num){
// 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){//在get()的基础上包装为jQuery对象
var dom = num != null ? (num >= 0 ? this[num] : this[num + this.length]) : null;
// return jQuery(dom);
return this.pushStack(dom);
}
jQuery.prototype.add = function(selector){
var curObj = jQuery(selector);//add()选中的jQuery对象
var baseObj = this;//this是之前的jQuery对象
var newObj = jQuery();//空jQuery对象
for(var i = 0; i < curObj.length; i++){
newObj[newObj.length++] = curObj[i];
}
for(var i = 0; i < baseObj.length; i++){
newObj[newObj.length++] = baseObj[i];
}
//newObj.pervObject = this;
this.pushStack(newObj);
return newObj;//返回add()操作过的jQuery对象
}
jQuery.prototype.end = function(){
return this.pervObject;//返回之前的jQuery对象
}
jQuery.prototype.myOn = function(type, handle){//只处理自定义事件
for(var i = 0; i < this.length; i++){
if(!this[i].cacheEvent){
this[i].cacheEvent = {};
}
if(!this[i].cacheEvent[type]){
this[i].cacheEvent[type] = [handle];//把事件缓存到里面, type : []
}else{
this[i].cacheEvent[type].push(handle);
}
}
}
jQuery.prototype.myTrigger = function(type){
var self = this;
var params = arguments.length > 1 ? [].slice.call(arguments, 1): [];//处理trigger的第二个参数
for(var i = 0; i < this.length; i++){
if(this[i].cacheEvent[type]){
this[i].cacheEvent[type].forEach(function(ele, index){
ele.apply(self, params);//ele就是type数组中的事件函数
});
}
}
}
jQuery.prototype.myQueue = function(){
var queueObj = this;
var queueName = arguments[0] || 'fx';//animate在使用队列时默认名称'fx'
var addFunc = arguments[1] || null;
var len = arguments.length;
//传一个参数,获取队列
if(len == 1){
return queueObj[0][queueName];
}
//添加队列或往已有队列中添加内容。为什么取零索引呢?如果是多个dom呢?
queueObj[0][queueName] == undefined ? queueObj[0][queueName] = [addFunc] : queueObj[0][queueName].push(addFunc);
console.log(queueObj);
return this;
}
jQuery.prototype.myDequeue = function(type){
var self = this;
var queueName = arguments[0] || 'fx';
var queueArr = this.myQueue(queueName);//取队列数组
var currFunc = queueArr.shift();
if(currFunc == undefined){
return;
}
var next = function(){
self.myDequeue(queueName);//递归调用
}
currFunc(next);
return this;
}
jQuery.prototype.myDelay = function(duration){
var queueArr = this[0]['fx'];
queueArr.push(function(next){
setTimeout(function(){
next();
}, duration)
});
return this;
}
jQuery.prototype.myAnimate = function(attrObj, callback){
var len = this.length;//有多个dom
var self = this;
//添加到队列里的内容函数
var baseFunc = function(next){
var times = 0;
for(var i = 0; i < len; i++){
startMove(self[i], attrObj, function(){
times++;
if(times == len){//判断每一个dom到达目标点
callback && callback();
next();
}
})
}
}
this.myQueue('fx', baseFunc);//入队
if(this.myQueue('fx').length == 1){//外界调用animate,上一行入队执行,然后这儿出队
this.myDequeue('fx');
}
function getStyle(dom, attr){
if(window.getComputedStyle){
return window.getComputedStyle(dom, null)[attr];
}else{
return dom.currentStyle[attr];
}
}
function startMove(dom, attrObj, callback){
clearInterval(dom.timer);
var iSpeed = null, iCur = null;
dom.timer = setInterval(function(){
var bStop = true;
for(var attr in attrObj){
if(attr == 'opacity'){
iCur = parseFloat(getStyle(dom, attr)) * 100;
}else{
iCur = parseInt(getStyle(dom, attr));
}
iSpeed = (attrObj[attr] - iCur) / 10;
iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
if(attr == 'opacity'){
dom.style.opacity = (iCur + iSpeed) / 100;
}else{
dom.style[attr] = iCur + iSpeed + 'px';
}
if(iCur != attrObj[attr]){
bStop = false;
}
}
if(bStop){
clearInterval(dom.timer);
typeof callback == 'function' && callback();
}
},30);
}
return this;
}
jQuery.myCallbacks = function(){
//once memory
//存储参数
var options = arguments[0] || '';
//存储通过add加入的方法
var list = [];
//记录当前要执行函数的索引
var fireIndex = 0;
//记录是否被fire过
var fired = false;
//实际参数列表
var args = [];
var fire = function(){
for(; fireIndex < list.length; fireIndex++){
list[fireIndex].apply(window, args);
}
if(options.indexOf('once') != -1){
list = [];
fireIndex = 0;
}
}
return {
add : function(func){
list.push(func);
if(options.indexOf('memory') != -1 && fired){
fire();
}
return this;
},
fire : function(){
fireIndex = 0;//每次执行后游标归零
args = arguments;
fired = true;
fire();
}
}
}
jQuery.myDeferred = function(){
//3个callback
var arr = [
[jQuery.myCallbacks('once memory'), 'done', 'resolve'],
[jQuery.myCallbacks('once memory'), 'fail', 'reject'],
[jQuery.myCallbacks('memory'), 'progress', 'notify']
];
var pendding = true;
var deferred = {};
for(var i = 0; i < arr.length; i++){
//注册
//deferred['done'] = function(){}
deferred[ arr[i][1] ] = (function(index){
return function(func){
arr[index][0].add(func);//注册时执行的是add方法
}
}(i));
//触发
//deferred['resolve'] = function(){}
deferred[ arr[i][2] ] = (function(index){
return function(){
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;
}
jQuery.prototype.init.prototype = jQuery.prototype;//init的原型等于jQuery的原型
window.$ = window.jQuery = jQuery;//把jQuery保存到全局
}());
以上内容属二哥原创,整理自 "渡一教育Javascript课程" ,一个值得推荐的"渡一教育"。