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 ) {...},  //jq对象入栈
    each: function( callback, args ) {...},  //遍历集合
    ready: function( fn ) {...},  //dom加载的接口
    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进行了一些简单的分配。判断selector的类型,是空值、字符串、还是数组,然后对相应的类型做一些初始化操作,这里面判断selector类型的操作还是相当琐碎的,各个方面都考虑的很周全。

posted @ 2019-07-30 18:14  有梦想的咸鱼·-·  阅读(134)  评论(0编辑  收藏  举报