jQuery 源码剖析 (二) 选择器


jQuery 源码解析代码地址: https://github.com/Geek-James/Blog

建议下载源码然后据文章思路学习,最好自己边思考边多敲几遍。

一:剖析源码前准备

  • 1.首先官网下载源码jQuery官网
  • 2.选择jQuery版本并下载到本地,并在本地给自己新建件myjQuery-1.0.1.js(这个文件是用来仿写jQuery).
  • 3.创建入口文件并引入这官方jQuery和自己创建的myjQuery-1.0.1.js文件.
  • 4.开始剖析源码.

二.开始剖析

本次主要剖析jQuery选择器的封装,在平时使用jQuery时,非常容易的通过$()就可以查找相关的选择器,那么具体是如何实现的呢?跟随我来一探究竟…

首先先来做个例子:
我们先通过官方的jQuery框架输出以下几个实例:

// 传入字符串
console.log($("a"));    //创建DOM节点包装成jQuery对象
// 传入HTML
console.log($("<div>"));  // //创建DOM节点包装成jQuery对象
// 传入对象
console.log($(document));
// 传入选择器
console.log($('.box'));
// 传入对象
console.log($(this));   // 把传入的对象包装成jQuery对象 
// 传入方法 
$(function(){
    console.log(11111);  //这个是在页面加载完成后加载执行的,等效于在DOM文档加载完成后执行了$(document).read()方法        
})

输出结果:


根据输入的结果我们类阅读源码,反推如何实现类似的效果:
首先$()调用的是jQuery.prototype.init()的初始化方法,所以要在init()方法里做扩展。

以下仅仅是核心的代码片段,详细代码在github上,自行下载查看.

剖析源码下载

init:function (selector,context) {
            context = context || document;
            var match,elem,index=0;
            if(!selector){
                return this;
            }
            /**
             * 检测传过来的数据是否是字符串
             * **/
            if(typeof selector === "string"){
                if (selector.charAt(0) === "<" && selector.charAt(selector.length-1)=== ">" && selector.length>=3) {
                   // 此时是HTML  
                    match = [selector];
                }
                // match 有值的情况
                if (match) {
                    /**
                     * 合并数组
                     * merge parseHTML是静态扩展方法
                     * **/
                    jQuery.merge(this, jQuery.parseHTML(selector,context));
                  // 查询DOM节点  
                } else {
                    elem = document.querySelectorAll(selector);
                    // 转化为真数组
                    var elems = Array.prototype.slice.call(elem);
                    this.length = elem.length;

                    for (;index = elem.length;index ++) {
                        this[index] = elems[index];
                    }
                    this.context = context;
                    this.selector = selector;
                }
                // HANDLE: $(DOMElement)
            } else if (selector.nodeType){ // 
                this.context = this[0] = selector;
                this.length = 1;
                return this;
              //  $(function)
              // 函数处理
            } else if (isFunction( selector )) { 
                jQuery(document).ready(selector); // 实例对象的方法
            }
            /**
            *makeArray 是jQuery扩展的方法
            **/
            return jQuery.makeArray( selector, this );
        },
jQuery 扩展的静态方法
        /**
         *  合并数组
         *  [first] jQuery的实例对象  this
         *  [second] DOM 节点 
         */
        merge:function (first,second) {
            var l = second.length, // 1
                i = first.length, // 0
                j = 0;
            if (typeof l === "number") {
                for ( ; j < l; j++){ // 遍历DOM节点
                    first[i++] = second[j];
                }
            } else {
                while (second[j] !== undefined) {
                    first[i++] = second[j++];
                }
            } 
            first.length = i;
            // 返回jQuery的实例对象
            return first;   
        },
        /**
         * 解析HTML
         * [data] 传入的数据
         * [context] 返回的值
         * **/
        parseHTML:function (data,context) {
            if (!data || typeof data !== "string") {
                return null;
            }
            /**
             * exec() 是正则方法 返回为数组   
             * [0] 为正则表达式相匹配的文本
             * [1] 表达式相匹配的文本    
             * **/
            // 过滤掉符号,只提取标签 "<a>" ==> "a"
            var parse = rejectExp.exec(data);
            // 返回一个创建DOM的元素
            return [context.createElement(parse[1])];
        },
        /**
         * 将一个类数组对象转换为真正的数组对象
         * [arr] 传入的数组
         * [result] 返回的数组
         * **/
        makeArray:function(arr,result){
            var ret = result || [];
            if ( arr != null ) {
                if ( isArrayLike( Object( arr ) ) ) {
                    jQuery.merge( ret,
                        typeof arr === "string" ?
                        [ arr ] : arr
                    );
                } else {
                   [].push.call( ret, arr );
                }
            }
            return ret;
        },
        isReady:false,
        readylist:[],// list 
        ready:function(){ // 事件函数
           jQuery.isReady = true;
           jQuery.readylist.forEach(function(callback){
               callback.call(document);
           })  
           // 清空
           jQuery.readylist = null;
        }
    });
    /**
     *  定义全局函数
     * **/
    
    // 判断是否是方法
    var isFunction = function isFunction( obj ) {   
        return typeof obj === "function" && typeof obj.nodeType !== "number";
        };
    // 判断是否是windows
    var isWindow = function isWindow( obj ) {
        
		return obj != null && obj === obj.window;
    };
    

通过以上代码可以做以下几点分析:

1.$()传过来的selector数据是属于什么类型?不同的数据类型,分析不同数据类型的行为,有以下几种情况:

  • 1.1 如果传过来的数据是字符串:那么要分析字符串是否是HTML标签,如果是HTML那么就通过正则提取关键字并创建一个HTM标签输出
  • 1.2 如果传过来的数据是不是html元素,那么要通过querySelectorAll来查询过滤,如果可以查询到是DOM中的选择器,那么就遍历输出他的值.
  • 1.3 如果传过来的元素是DOM节点,直接返回
  • 1.4 如果传过来的数据是一个对象方法,那么要通过$(document).read()方法,监听拦截DOMContentLoaded方法,改变对象方法的指针然后依次加入到数组中,输出.

通过剖析jQuery选择器模块,进行测试:

测试代码:

// 创建DOM
        // 传入字符串
        console.log($("a"));    //创建DOM节点包装成jQuery对象
        // 传入HTML
        console.log($("<div>"));  // //创建DOM节点包装成jQuery对象
        // 传入对象
        console.log($(document));
        // 传入选择器
        console.log($('.box'));
        // 传入对象
        console.log($(this));   // 把传入的对象包装成jQuery对象
        $(function(){
            console.log(11111); 

输出结果:


至此完美收官 !! ?

总结

通过剖析jQuery源码选择器部分,有以下几点个人收获:

  • 1.jQuery 设计的静态属性扩展和jQuery的原型链属性扩展(实例扩展)的巧妙应用
  • 2.巧妙应用正则来筛选数据

其他

jQuery 源码剖析 系列目录地址:https://github.com/Geek-James/Blog

jQuery 源码剖析 系列预计写十篇左右,旨在加深对原生JavaScript 部分知识点的理解和深入,重点讲解 jQuery核心功能函数、选择器、Callback 原理、延时对象原理、事件绑定、jQuery体系结构、委托设计模式、dom操作、动画队列等。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star⭐️,对作者也是一种鼓励。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值