一步一步DIY jQuery库1

版权声明:本文为博主原创文章,转载请注明出处和原文链接。 https://blog.csdn.net/future_todo/article/details/52725171

前一段时间,看到一篇系列文章《从零开始,DIY一个jQuery 1-3》三篇文章从头讲解如何DIY一个jQuery,感觉挺有意思,今天想试一试看看。

我在前端已经有了一年的经验,jquery几乎是每天都会使用到,但是现在还没时间去研究一下它的源码,即使这样我也想动手尝试下DIY一个JQuery库,我相信这可以加深我对jquery的理解,当然我也试着去学习jquery的源码,这很可能会写成一个系列的文章,这个系列文章以一个入门的jquery原理探索者的视角。当然,这篇文章作为入门的入门。

【注】所有代码挂在我的github

1.实现一个基本的框架

1.1 整体

  • 整体是个闭包,独立作用域,避免污染全局变量
  • 不用new,是因为使用了工厂模式,new放在了内部return new jQuery.fn.init(selector)
  • jQuery是最基础的对象,方法放在了jQuery.prototype中,即是jQuery.fn
  • .extend/.fn.extend 来扩展静态方法和原型方法
  • 使用全局变量window.$, window.jQuery即可调用
  • return this为了链式调用
//IIFE 独立作用域
    (function() {
        var version = '0.0.1';

        jQuery = function(selector) {
            return new jQuery.fn.init(selector); //jQuery实例的构造函数已经变成了 jQuery.fn.init 
        };

        /*jQuery.fn   主要方法*/
        jQuery.fn = jQuery.prototype = {
            jquery: version,
            construct: jQuery,
            //方法
            setBackground: function(color) {
                this[0].style.background = color;
                return this;  //链式调用
            },
        };

        var init = jQuery.fn.init = function(selector) {
            if (!selector) {
                return this;
            } else {
                var elem = document.querySelector(selector);
                if (elem) {
                    this[0] = elem;
                    this.length = 1;
                }
                console.info(this);
                return this;
            }
        };
        init.prototype = jQuery.prototype; //把 jQuery.fn.init 的原型指向 jQuery 的原型(jQuery.prototype / jQuery.fn)即可

        window.$ = window.jQuery = jQuery;
    })();

测试

var $div = $('div');
console.log($div);
$div.setBackground('blue');
console.log($div.jquery); //0.0.1
console.log($.fn.jquery); //0.0.1

$div的结果

- j…y.fn.init {0: div, length: 1}
  + 0:div
  + length:1

1.2 冲突处理

/*冲突处理*/
var _jQuery = window.jQuery,
_$ = window.$;
//deep 参数类型为 Boolean,若为真,表示要求连window.jQuery 变量都需要吐回去
jQuery.noConflict = function(deep) {
    if (window.$ === jQuery) {
    window.$ = _$;
}
//确保window.jQuery没有再次被改写
if (deep && window.jQuery === jQuery) {
    window.jQuery = _jQuery;
}
return jQuery; //返回 jQuery 接口引用
};

测试

var $$$ = jQuery.noConflict();
$$$('div').setBackground('red');

1.3 $.extend / $.fn.extend 来扩展静态方法和原型方法

jQuery.extend = jQuery.fn.extend = function() {
     var target = arguments[0] || {};
     for(var key in target){
         jQuery.fn[key] = target[key];
     }
};

但是调用必须是$().min而不能是$.min

jQuery.extend({
    min: function(a, b) {
        return a < b ? a : b;
    },
    max: function(a, b) {
        return a > b ? a : b;
    }
});
console.log($().min(3,5));

当然这只是个人的一些想法,我们来仿照jquery源码实现

var isObject = function(obj) {
    return Object.prototype.toString.call(obj) === "[object Object]";
};
var isArray = function(obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
};
var options, i = 1,
length = arguments.length,
target = arguments[0] || {},
deep = false; //默认为浅复制

if (typeof target === "boolean") {
    deep = target;
    taeget = arguments[i] || {};
    i++;
}
if (typeof target !== "object" && typeof target !== "function") {
    target = {};
}

//target后面没有其他参数了(要拷贝的对象),直接扩展jQuery自身,target并入jQuery
if (i === length) {
    target = this;
    i--;
}
for (; i < length; i++) {
    if ((options = arguments[i]) != null) {
    var name, clone, copy;
    for (name in options) {
        src = target[name]; //jQuery是否已经有该属性
        copy = options[name];
        if (target === copy) {
           continue;
         }
       //1-深拷贝,且确保被拷属性为对象/数组
       if (deep && copy && (isObject(copy)||(copyIsArray = isArray(copy)))){
       //被拷贝属性为数组
          if(copyIsArray){
              copyIsArray = false;
              //被合并属性
              clone = src && isArray(src)?src:[];
          }else{//被拷贝属性为对象
              clone = src && isArray(src)?src:{};
          }
           //右侧递归,直到内部属性值是非对象
         target[name] = jQuery.extend(deep,clone,copy);
      }else if(copy!==undefined){//2-非对象/数组,或者浅复制的情况
         target[name] = copy;//递归结束
       }
    }
  }
}
//返回修改后的target
return target;

拷贝主要分为两个部分:

//1-深拷贝,且确保被拷属性为对象/数组
if (deep && copy && isObject(copy)||(copyIsArray = isArray(copy))){
    /*...*/}
//2-非对象/数组,或者浅复制的情况
else if(copy!==undefined){
}

使用的几种情况

$.extend(  targetObj,  copyObj1[,  copyObj2...]  )
$.extend(  true,  targetObj,  copyObj1[,  copyObj2...]  )
$.extend(  copyObj  )
$.extend(  true,  copyObj

测试

 jQuery.extend({
     min: function(a, b) {
         return a < b ? a : b;
     },
     max: function(a, b) {
         return a > b ? a : b;
     }
});
console.log($.min(3, 5));

注意:这个时候只是使用isObject,isArray并不严谨
在某些浏览器中,像 document 在 Object.toSting 调用时也会返回和 Object 相同结果;
这些我们将在《一步一步DIY一个自己jQuery库2》中进行补充。

2.全部代码

//IIFE 独立作用域
    (function() {
        var version = '0.0.1';

        jQuery = function(selector) {
            return new jQuery.fn.init(selector); //jQuery实例的构造函数已经变成了 jQuery.fn.init 
        };

        /*冲突处理*/
        var _jQuery = window.jQuery,
            _$ = window.$;
        //deep 参数类型为 Boolean,若为真,表示要求连window.jQuery 变量都需要吐回去
        jQuery.noConflict = function(deep) {
            if (window.$ === jQuery) {
                window.$ = _$;
            }
            //确保window.jQuery没有再次被改写
            if (deep && window.jQuery === jQuery) {
                window.jQuery = _jQuery;
            }

            return jQuery; //返回 jQuery 接口引用
        };

        /*jQuery.fn   主要方法*/
        jQuery.fn = jQuery.prototype = {
            jquery: version,
            construct: jQuery,
            //方法
            setBackground: function(color) {
                this[0].style.background = color;
                console.warn(this);
                return this;
            },
        };

        var init = jQuery.fn.init = function(selector) {
            if (!selector) {
                return this;
            } else {
                var elem = document.querySelector(selector);
                if (elem) {
                    this[0] = elem;
                    this.length = 1;
                }
                console.info(this);
                return this;
            }
        };
        init.prototype = jQuery.prototype; //把 jQuery.fn.init 的原型指向 jQuery 的原型(jQuery.prototype / jQuery.fn)即可

        jQuery.extend = jQuery.fn.extend = function() {
            var isObject = function(obj) {
                return Object.prototype.toString.call(obj) === "[object Object]";
            };
            var isArray = function(obj) {
                return Object.prototype.toString.call(obj) === "[object Array]";
            };
            var options, i = 1,
                length = arguments.length,
                target = arguments[0] || {},
                deep = false; //默认为浅复制

            if (typeof target === "boolean") {
                deep = target;
                taeget = arguments[i] || {};
                i++;
            }
            if (typeof target !== "object" && typeof target !== "function") {
                target = {};
            }

            //target后面没有其他参数了(要拷贝的对象),直接扩展jQuery自身,target并入jQuery
            if (i === length) {
                target = this;
                i--;
            }
            for (; i < length; i++) {
                if ((options = arguments[i]) != null) {
                    var name, clone, copy;
                    for (name in options) {
                        src = target[name]; //jQuery是否已经有该属性
                        copy = options[name];
                        if (target === copy) {
                            continue;
                        }
                        //深拷贝,且确保被拷属性为对象/数组
                        if (deep && copy && (isObject(copy) || (copyIsArray = isArray(copy)))) {
                            //被拷贝属性为数组
                            if (copyIsArray) {
                                copyIsArray = false;
                                //被合并属性
                                clone = src && isArray(src) ? src : [];
                            } else { //被拷贝属性为对象
                                clone = src && isArray(src) ? src : {};
                            }
                            //右侧递归,直到内部属性值是非对象
                            target[name] = jQuery.extend(deep, clone, copy);
                        } else if (copy !== undefined) { //非对象/数组,或者浅复制的情况
                            target[name] = copy; //递归结束
                        }
                    }
                }
            }

            //返回修改后的target

            return target;

        };

        window.$ = window.jQuery = jQuery;
    })();

    //测试
    var $div = $('div');
    console.log($div);
    $div.setBackground('blue');
    console.log($div.jquery); //0.0.1
    console.log($.fn.jquery); //0.0.1
    console.log(jQuery.extend());
    console.log($.extend.jquery);
    //冲突
    /*var $$$ = jQuery.noConflict();
    $$$('div').setBackground('red');*/
    //
    jQuery.extend({
        min: function(a, b) {
            return a < b ? a : b;
        },
        max: function(a, b) {
            return a > b ? a : b;
        }

    });
    console.info(jQuery.prototype);
    console.info(jQuery);
    // console.log($().min(3,5));
    // console.log($.prototype.min(3, 5));
    console.log($.min(3, 5));

参考阅读:
- 从零开始,DIY一个jQuery(1)
- 从零开始,DIY一个jQuery(2)
- 从零开始,DIY一个jQuery(3)

阅读更多

没有更多推荐了,返回首页