myprelude

我是练习时间长达两年半的前端练习生,擅长CTRL。

如何实现一个自己的jQuery库

31 May 2019 » javascript

作者:myprelude@github
原文链接: https://myprelude.github.io
转载请注明出处,保留原文链接和作者信息。

jQuery / $

    $('div').css({background:'red'})
    var jQ = new jQuery('div')
    jQ.css({width:'500px'}).show()

什么Vue,React,Angular,老夫就jQuery一把梭子走天下。我们在使用jQuery给我们带来快发快感时,是否思考过它是如何实现的?为什么可以实例化jQuery又可以直接调用jQuery?jQuery是如何解决命名冲突的?….等等

今天就从我理解的思维开始搭建一个简单的jQuery,让我们从走进大神的世界。

构建独立的作用域

    (function(window,document){
        function jQuery () {

        }
        jQuery.version = '1.0.0';
        window.jQuery = window.$ = jQuery;
    })(window,document)

    // test code
    console.log($.version)  // 1.0.0
    console.log(jQuery.version)  // 1.0.0

实现元素jQuery主体函数

    function jQuery (tag) {
        var Tag = document.querySelectorAll(tag);
        this.nodes = [];
        this.each(Tag,function(val,key){
            this.nodes.push(val)
        })
        
    }
    jQuery.prototype = {
        contrustor:jQuery,
        version:'1.0.0',
        each:function(arr,callback){
            for(var i=0;i<arr.length;i++){
                callback.call(this,arr[i],i)
            }  
        },
        css:function(){
            // this.jqDom.style = 
            console.log('set css function ...')
        }
    }

    new jQuery('div') // [div.content]
    new jQuery('div').version // 1.0.0
    new jQuery('div').css() // set css function ...

如何实现无new 实例化

在实际开发中,每次都要通过new jQuery()来实现实例化太麻烦了,有没有好的方法直接 jQuery()呢?

    function jQuery (tag) {
        if( !(this instanceof jQuery) ){  // 关键判断当前this
            return new jQuery(tag)
        }
        var Tag = document.querySelectorAll(tag);
        this.nodes = [];
        this.each(Tag,function(val,key){
            this.nodes.push(val)
        })
        
    }
    jQuery.prototype = {
        contrustor:jQuery,
        version:'1.0.0',
        each:function(arr,callback){
            for(var i=0;i<arr.length;i++){
                callback.call(this,arr[i],i)
            }  
        },
        css:function(){
            // this.jqDom.style = 
            console.log('set css function ...')
        }
    }

    new jQuery('div').css()  // set css function ...
    jQuery('div').css()  // set css function ...

我们来看看jQuery是如何实现无new 实例化

    function jQuery(tag){
        return new jQuery.prototype.init(tag)  // 关键 1
    }
    jQuery.prototype = {
        constructor:jQuery,
        init:function(tag){
            var Tag = document.querySelectorAll(tag);
            this.nodes = [];
            this.each(Tag,function(val,key){
                this.nodes.push(val)
            })
        },
        each:function(arr,callback){
            for(var i=0;i<arr.length;i++){
                callback.call(this,arr[i],i)
            }  
        },
        css:function(){
            console.log('set css function ...')
        }
    }
    jQuery.prototype.init.prototype = jQuery.prototype; // 关键 2

    new jQuery('div').css()  // set css function ...
    jQuery('div').css()  // set css function ...

可以看出来jQuery并不是通过instanceof来做判断,它同时实例化原型属性上的init方法实现的。

    return new jQuery.prototype.init(tag) 

当我们调用jQuery(‘div’)时,其实是先实例化jQuery.prototype.init,这样我们虽然可以实现无new但是又有一个问题出现,现在我们是如何拿到jQuery.prototype上的方法呢?这里jQuery做的方法就很巧妙。。

    jQuery.prototype.init.prototype = jQuery.prototype; 

如上:将jQuery.prototype.init的原型指向jQuery的原型。为啥这样我们就可以拿到jQuery原型的方法呢?这里我们需要知道new做了什么。

new到底做了什么

    var obj = {};
    obj.__proto__ = `构造函数`.prototype
    `构造函数`.call(obj)
    return obj

简单的说就是生成一个指向构造函数原型的对象,从而调用构造函数原型的方法,构造函数原型中的this指向该对象。

根据上面的逻辑来套

    jQuery('div')  => new jQuery.prototype.init  
                                ||
                                ||
                    jQuery.prototype.init.prototype  =>  jQuery.prototype          

jQuery('div') 拿到 jQuery.prototype.init原型上的方法,其原型是指向 jQuery.prototype,这样就行程了一个引用连。

如何实现jQuery方法的拓展

    function jQuery(tag){
        return new jQuery.prototype.init(tag)  
    }
    jQuery.extend = function(obj,tager){
        for(var k in obj){
            tager?target[k]:jQuery.prototype[k] = obj[k]
        }
    }

    jQuery.extend({hide:function(){console.log('hide')}})
    jQuery('div').hide() // hide 

如何解决jQuery在全局中命名冲突问题

    _jQuery = window.jQuery,  // 如果jQuery/$命名冲突像保存存在的
    _$ = window.$,
    jQuery.noConflict = function( deep ){  // 调用这个方法重写拿到之前储存的jQuery
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }
        return jQuery;
    }

如何导出jQuery

    (function(window){
        // AMD  // CMD
        if( typeof define ==='function' && (define.amd || define.cmd) ){
            define(function(){
                return jQuery
            })
        }else{
            window.$ = window.jQuery = jQuery;
        }
    })(window)
-->