JavaScript漂流记(下)__jQuery封装(简易版)

在学习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课程" ,一个值得推荐的"渡一教育"。

猜你喜欢

转载自blog.csdn.net/AquamanTrident/article/details/93141925