实现一个自己的类jq函数库

前言

jq功能强大,是每个前端人的开发利器。本文用意不在实现一个像jq那样的强大函数库,只是对jq中常用功能做一个简单实现,构建一个自己的微型库。

正文

一、对外提供接口

//为了避免污染全局变量,采用闭包来处理
(function(){
    function $(){}
    window.$ = $;
})()

二、选择器功能的实现

//思考:在jq中$()返回一个对象,假如使用简单工厂模式即可实现,但是这样做显然有十分大的弊端。参考jq源码做如下处理
function $(selector,context){
    return new $.fn.init(selector,context);
}

$.fn = $.prototype;

$.fn.init = function(selector,context){
    if(selector.nodeType === 1){         // 如果是DOM节点直接返回
        this[0] = selector;
        this.length = 1;
        return this;
    }   

    var context = context || document;
    var nodeList = context.querySelectorAll(selector);
    this.length = nodeList.length;

    for(var i=0; i<this.length; i++){
        this[i] = nodeList[i];  
    }
}

$.fn.init.prototype = $.fn;

三、extend的实现

//主要实现方法的拷贝,jq中主要使用了拷贝继承,比较简单好理解
$.fn.extend = $.extend = function(destination,source){
    if(typeof source === 'undefined'){
        source = destination;
        destination = this;     //指向$.fn.init.prototype
    }
    for(var property in source){
        if(source.hasOwnProperty(property)){
            destination[property] = source[property];   
        }
    }
    return destination;    //可以实现链式操作
}

四、each方法的实现

//主要实现对对象中elements的遍历操作
$.fn.extend({
    each : function(callback){
        for(var i=0; i<this.length; i++){
            callback(this[i],i);    
        }
        return this;    
    }   
})

五、DOM相关方法的实现

//主要实现DOM操作,添加、删除、清空 (append、prepend、remove、empty)
$.fn.extend({
    clone : function(){
        var obj = {};
        console.log(this[0].tagName.toLowerCase());
        var node = document.createElement(this[0].tagName.toLowerCase());
        node.innerHTML = this[0].innerHTML;
        obj[0] = node;
        return obj;
    },
    append : function(ele){
        this.each(function(v,k){
            v.appendChild($(ele).clone()[0]);
        });
        ele = null;
        return this;
    },
    prepend : function(ele){
        this.each(function(v,k){
            v.insertBefore($(ele).clone()[0],v.childNodes[0]); 
        })
        ele = null;
        return this;
    },
    remove : function(){
        this.each(function(v,k){
            v.parentNode.removeChild(v);
        });
        return this;
    },
    empty : function(){
        this.each(function(v,k){
            v.innerHTML = '';
        })  
    }
})

六、css方法的实现

$.fn.extend({
    css : function(cssRules,value){
        if($.isString(cssRules)){
            if(value){                              //  $().css('color','red')
                this.each(function(v,k){
                    v.style[cssRules] = value;
                })                  
            }else{                           // $().css('width')  注意:获取时length=1
                return window.getComputedStyle(this[0], null).getPropertyValue(cssRules);
            }
        }else{                                  // $().css({})
            for(var att in cssRules){      
                this.each(function(v,k){
                    v.style[att] = cssRules[att];
                })  
            }
        }
    }   
})

七、DOM过滤相关方法的实现

$.fn.extend({
    children : function(selector){
        return $(selector,this[0]);
    },
    perent : function(){
        return $(this[0].parentNode);
    },
    siblings : function(){
        var obj = {};
        var elesArr = [];
        var j = 0;
        var nodes = this[0].parentNode.childNodes;
        for(var i=0; i<nodes.length; i++){
            if(nodes[i].nodeType === 1){
                elesArr.push(nodes[i]); 
            }   
        }
        obj['length'] = elesArr.length - 1;
        for(var i=0; i<elesArr.length; i++){
            if(elesArr[i]!==this[0]){
                obj[j] = elesArr[i];
                j++;    
            }
        }
        return obj;
    }
})

八、实现属性的操作以及class的操作

$.fn.extend({
    attr : function(attr,value){
        if(value){
            this.each(function(v,k){
                v[attr] = value;
            })  
            return this;
        }else{
            return this[0].getAttribute(attr);  
        }
    },
    removeAttr : function(attr){
        this.each(function(v,k){
            v.removeAttrbute(attr);
        })
        return this;
    },
    hasClass : function(className){
        var classList = this[0].classList;
        for(var i=0; i<classList.length; i++){
            if(classList[i] === className){
                return true;    
            }   
        }
        return false;
    },
    addClass : function(className){
        this.each(function(v,k){
            if(!$(v).hasClass(className)){
                console.log(v.className + ' ' + className);
                v.className += ' ' + className; 
            }
        })
    },
    removeClass : function(className){
        this.each(function(v,k){
            if($(v).hasClass(className)){
                var newClass = v.className.split(' ').filter(function(curr,index){
                    return curr !== className;
                }).join(' ');
                v.className = newClass;
            }
        })
    },
    html : function(htmlText){
        if(htmlText){
            this.each(function(v,k){
                v.innerHTML = htmlText;
            })  
        }else{
            return this[0].innerHTML;   
        }
    }
})

九、事件相关操作

//jq中采用的是事件绑定,类似的下面也采用事件绑定
$.fn.extend({
    on : function(event,fn){
        this.each(function(v,k){
            v.addEventListener(event,fn,false);
        });
        return this;
    },
    off : function(event,fn){
        this.each(function(v,k){
            v.removeEventListener(event,fn,false);  
        });
        return this;
    },
    trigger : function(event){
        var evt = document.createEvent('HTMLEvents');
        evt.initEvent(event,false,false);
        this.each(function(v,k){
            v.dispatchEvent(evt);
        });
        return this;
    }   
})

十、ajax

//注意:Promise对象已在ES6中规范,不再自己实现
$.extend({
    ajax : function(opts){
        var xhr = new XMLHttpRequest(),
            type = opts.type || 'GET',
            url = opts.url,
            params;

        params = (function(obj){                        //传输的参数
            var str = '';
            for(var k in obj){
                str += k + '=' + obj[k] + '&';  
            }
            str = str.slice(0, str.length-1);
        })(opts.data)

        type = type.toUpperCase();

        if(type === 'GET'){
            url += '?' + params;    
        }

        xhr.open(type, url);

        if(type === 'POST'){
            xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        }
        xhr.send(params ? params : null);

        return new Promise(function(resolve, reject){
            xhr.onload = function(){
                if(xhr.status === 200){
                    resolve(xhr.responseText);  
                }else{
                    reject(xhr.status); 
                }
            }
        })
    }
})

十一、Cookie

$.extend({
    cookie: function (cookieName, cookieValue, day) {
        var readCookie = function (name) {
            var arr,
                reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'),
                matched = document.cookie.match(reg);
            if(arr = matched) {
                return unescape(arr[2]);
            } else {
                return null;
            }
        };
        var setCookie = function (name, value, time) {
            var Days = time || 30;
            var exp = new Date();
            exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
            document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();
        };
        if (cookieName && cookieValue) {
            setCookie(cookieName, cookieValue, day);
        } else if (cookieName && $.isNull(cookieValue)) {
            setCookie(cookieName, '', -1);
        } else if (cookieName) {
            return readCookie(cookieName);
        }
    }
})

结语

在实现的过程中没有考虑过多的细节,如浏览器的差异等。欢迎补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值