jQuery2.0.3源码学习---总体架构

jquery源码学习

  本系列笔记是根据妙味课堂的jquery源码解析视频和高云大佬的著作jquery技术内幕总结而来,主要学习对象还是jquery2.0.3,那些繁琐的兼容问题实在不想面对。前者是的jquery是2.0.3版本的,后者是1.7.1版本的,虽然现在jquery已经发布到3.4.1了,但并没有找到较新的学习资料,只能按照这些古董来学习,希望学习完后有能力写一些自己的东西出来,最好根据最新的jquery版本写一个源码解读。目标已立,希望自己能做到

 

 总体架构

  jquery的模块可以分为三部分:入口模块,底层支持模块和功能模块(高云大佬对1.7版本的分类,2.0依然适用)入口模块就是构造jquery对象,功能模块主要有属性操作,事件系统,dom操作和样式操作等,底层支持模块主要是Sizzle选择器,数据缓存和队列等。在视频中老师对jquery源码是这样划分的:

    (function(){
    
    (21 , 94) 定义了一些变量和函数 jQuery = function(){};
    
    (96 , 283) 给JQ对象,添加一些方法和属性
    
    (285 , 347) extend : JQ的继承方法
    
    (349 , 817) jQuery.extend() : 扩展一些工具方法
    
    (877 , 2856)  Sizzle : 复杂选择器的实现 
    
    (2880 , 3042) Callbacks : 回调对象 : 对函数的统一管理
    
    (3043 , 3183) Deferred : 延迟对象 : 对异步的统一管理
    
    (3184 , 3295) support : 功能检测
    
    (3308 , 3652) data() : 数据缓存
    
    (3653 , 3797) queue() : 队列方法 : 执行顺序的管理 
    
    (3803 , 4299) attr() prop() val() addClass()等 : 对元素属性的操作
    
    (4300 , 5128) on() trigger() : 事件操作的相关方法
    
    (5140 , 6057) DOM操作 : 添加 删除 获取 包装 DOM筛选
    
    (6058 , 6620) css() : 样式的操作
    
    (6621 , 7854) 提交的数据和ajax() : ajax() load() getJSON()
    
    (7855 , 8584) animate() : 运动的方法
    
    (8585 , 8792) offset() : 位置和尺寸的方法
    
    (8804 , 8821) JQ支持模块化的模式
    
    (8826)  window.jQuery = window.$ = jQuery;
    
})();

  因为主要研究对象是jquery2.0,就按照视频上老师划分的结构来学习。

  首先在jquery外面包裹的是一个立即执行的匿名函数:

(function(window,undefined){
    var rootjQuery  // 声明一些全局变量
    // 。。。
    if ( typeof window === "object" && typeof window.document === "object" ) {
        window.jQuery = window.$ = jQuery;
    }
}(window)

  这种模式有一个术语:IIFE,叫做立即执行的函数表达式。在函数外包裹一个()使之成为一个表达式,再在末尾添加一个()就可以立即执行该函数。IIFE还有另外一种形式:(function(){...}()),两种形式功能上是一致的。上面这个自调用的匿名函数,创建了一个特殊的函数作用域,该作用域中的方法,变量都是私有的,不会冲突。而把jquery对象挂载到window上,是之成为公开的全局变量,也就是对外提供了一个可以引用jquery对象的一个接口。那个if判断也是防止window对象和document对象被人可以更改了。至于传入window对象,高云大佬是这样解释的:一是使window对象变更为局部变量(即把函数参数作为局部变量使用),在jquery代码内部访问window对象时,不需要将作用域链回退到顶层,从而更快的访问window对象;二是可以在压缩代码时对其进行优化。匿名函数传入的第二个参数undefined,这也是window对象的一个属性,高云大佬对其这样解释的:一是把同window一样,缩短查找undefined的作用域链,压缩时可以优化;二是防止undefined值被更改。

二 jquery构造函数

  首先看下源码:

jQuery = function( selector, context ) {
    return new jQuery.fn.init( selector, context, rootjQuery );
}
jQuery.fn = jQuery.prototype =  {...};
jQuery.fn.init.prototype = jQuery.fn;

  当我们调用$()或者jQuery()时,实际上返回的是一个 new jQuery.fn.init()的一个实例,也就是说真正的构造函数是jQuery.fn.init(),而下面又将jQuery的原型指向jQuery.fn,也就是说fn就是jQuery的原型,然后又将jQuery的原型指向真正构造函数的原型,这样做是相当绕的,视频里老师讲这样做主要是为了我们能在调用$()时能够直接完成构造函数的初始化,而不是我们平常用的先$().init()初始化,再调用$()上的一些方法。虽然源码很绕,但实际使用过程中,还是简洁了很多。而高云大佬是这样解释的,通常我们创建对象或者实例的方式是在运算符new后面紧跟一个构造函数,如果构造函数有返回值,运算符所创建的对象就会被抛弃,返回值将作为new表达式的值。这样在我们创建jQuery对象时不需要new,直接些jQuery()。

   下面来看一些jQuery的原型,源码架构如下:

jQuery.fn = jQuery.prototype = {
    jquery: core_version, // 版本号
    constructor: jQuery,  // 防止原型被覆盖
    init: function( selector, context, rootjQuery ) {...}, // 初始化
    selector: "",
    length: 0,

    toArray: function() {...},
    get: function( num ) {...},
    pushStack: function( elems ) {...},
    each: function( callback, args ) {...},
    ready: function( fn ) {...},
    slice: function() {...},
    first: function() {...},
    last: function() {...},
    eq: function( i ) {...},
    map: function( callback ) {...},
    end: function() {...},

    push: core_push,
    sort: [].sort,
    splice: [].splice
};

  这里使用了一个对象对jQuery的原型进行了覆盖,接下来对该对象进行一个详细的剖析。

  首先这个对象里定义了一些属性和方法。这里着重看一下init这个初始化方法,该方法接受三个参数,selector是选择元素的字符串,context是被选择元素的上下文,rootjQuery就是document对象。接下来就对selector进行了一些简单的分配。源码如下:

init: function( selector, context, rootjQuery ) {
        var match, elem;
        if ( !selector ) {
            return this;
        }
        if ( typeof selector === "string" ) {
                if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
                match = [ null, selector, null ];
            } else {
                match = rquickExpr.exec( selector );
            }
            if ( match && (match[1] || !context) ) {
                if ( match[1] ) {
                    context = context instanceof jQuery ? context[0] : context;
                    jQuery.merge( this, jQuery.parseHTML(
                        match[1],
                        context && context.nodeType ? context.ownerDocument || context : document,
                        true
                    ) );
                    if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
                        for ( match in context ) {
                            if ( jQuery.isFunction( this[ match ] ) ) {
                                this[ match ]( context[ match ] );
                            } else {
                                this.attr( match, context[ match ] );
                            }
                        }
                    }
                    return this;
                } else {
                    elem = document.getElementById( match[2] );
                    if ( elem && elem.parentNode ) {
                        this.length = 1;
                        this[0] = elem;
                    }
                    this.context = document;
                    this.selector = selector;
                    return this;
                }
            } else if ( !context || context.jquery ) {
                return ( context || rootjQuery ).find( selector );
            } else {
                return this.constructor( context ).find( selector );
            }
        } else if ( selector.nodeType ) {
            this.context = this[0] = selector;
            this.length = 1;
            return this;
        } else if ( jQuery.isFunction( selector ) ) {
            return rootjQuery.ready( selector );
        }
        if ( selector.selector !== undefined ) {
            this.selector = selector.selector;
            this.context = selector.context;
        }
        return jQuery.makeArray( selector, this );
    }

未完待续·····

猜你喜欢

转载自www.cnblogs.com/OnePieceKing/p/11271839.html