jQuery3.2.1 源码 解读

参考文章:http://www.cnblogs.com/coco1s/p/5261646.html
http://schifred.iteye.com/blog/2317239
https://segmentfault.com/a/1190000003933990
http://www.cnblogs.com/losesea/p/4415676.html

jquery 整体框架:
这里写图片描述

一. JS中的模块规范(Common JS,AMD,CMD)

1.1 Common JS

服务器端JS的模块规范,同步的,CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}.
像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。

1.2 AMD

AMD就只有一个接口:define(id?,dependencies?,factory);
它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样:

 define(['dep1','dep2'],function(dep1,dep2){...});

RequireJS就是实现了AMD规范的呢。

1.3 CMD

CMD是SeaJS 在推广过程中对模块定义的规范化产出

CMD和AMD的区别有以下几点:

1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。

2.CMD推崇依赖就近,AMD推崇依赖前置。
//AMD
define([‘./a’,’./b’], function (a, b) {

//依赖一开始就写好 
a.test(); 
b.test(); 

});

//CMD
define(function (requie, exports, module) {
//依赖可以就近书写
var a = require(‘./a’);
a.test();

... 
//软依赖 
if (status) { 
    var b = requie('./b'); 
    b.test(); 
} 

});
虽然 AMD也支持CMD写法,但依赖前置是官方文档的默认模块定义写法。

3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。

二. Jquery 预先定义方法入口

(function(window, undefined) {
    var
        // 定义了一个对象变量,一个字符串变量,一个数组变量
        class2type = {},
        core_version = "1.10.2",
        core_deletedIds = [],

        // 保存了对象、字符串、数组的一些常用方法 concat push 等等...
        core_concat = core_deletedIds.concat,
        core_push = core_deletedIds.push,
        core_slice = core_deletedIds.slice,
        core_indexOf = core_deletedIds.indexOf,
        core_toString = class2type.toString,
        core_hasOwn = class2type.hasOwnProperty,
        core_trim = core_version.trim;

})(window);

应用时:

jQuery.fn = jQuery.prototype = {
    // ...

    // 将 jQuery 对象转换成数组类型
    toArray: function() {
        // 调用数组的 slice 方法,使用预先定义好了的 core_slice ,节省查找内存地址时间,提高效率
        // 相当于 return Array.prototype.slice.call(this)
        return core_slice.call(this);
    }
}

作用:
那么 jQuery 为什么要这样做呢,我觉得:
1、以数组对象的 concat 方法为例,如果不预先定义好 core_concat = core_deletedIds.concat 而是调用实例 arr 的方法 concat 时,首先需要辨别当前实例 arr 的类型是 Array,在内存空间中寻找 Array 的 concat 内存入口,把当前对象 arr 的指针和其他参数压入栈,跳转到 concat 地址开始执行,而当保存了 concat 方法的入口 core_concat 时,完全就可以省去前面两个步骤,从而提升一些性能;
2、另外一点,借助 call 或者 apply 的方式调用,让一些类数组可以直接调用数组的方法。就如上面是示例,jQuery 对象是类数组类型,可以直接调用数组的 slice 方法转换为数组类型。又譬如,将参数 arguments 转换为数组类型:

function test(a,b,c){
    // 将参数 arguments 转换为数组 使之可以调用数组成员方法
    var arr = Array.prototype.slice.call(arguments);
    ...
}

三.Sizzle

3.1 Sizzle各个模块,整体设计思路

Sizzle各种选择匹配器Expr = Sizzle.selectors
Find:查找函数
Filter:过滤函数
relative: 块间关系处理
Pseudos

(1) 分割解析
对于复杂的选择器表达式,原生的API无法直接对其进行解析,但是却可以对其中的某些单元进行操作,那么很自然就可以采取先局部后整体的策略:把复杂的选择器表达式拆分成一个个块表达式和块间关系。在下图中可以看到,1、选择器表达式是依据块间关系进行分割拆分的;2、块表达式里面有很多伪类表达式,这是Sizzle的一大亮点,而且还可以对伪类进行自定义,表现出很强的工程性;3、拆分后的块表达式有可能是简单选择器、属性选择器、伪类表达式的组合,例如div.a、.a[name = “beijing”]。
这里写图片描述

(2) 块表达式查找
经过块内查找, 得到了一个基本的元素集合,那如何处理块间关系呢?通过观察可以发现,对一个复杂的选择器表达式存在两种顺序:
• 从左到右:对得到的集合,进行内部逐个遍历,得到新的元素集合,只要还有剩余的代码块,就需要不断地重复查找、过滤的操作。总结下就是:多次查找、过滤。
• 从右到左:对得到的元素集合,肯定包括了最终的元素,而且还有多余的、不符合条件的元素,那么接下来的工作就是不断过滤,把不符合条件的元素剔除掉。

对于“相邻的兄弟关系(+)”、“之后的兄弟关系(~)”,哪种方式都无所谓了,效率没什么区别。但是对于“父子关系”、“祖先后代关系”就不一样了,此时Sizzle选择的是以从右到左为主,下面从两个维度进行解释:

这里写图片描述

a、设计思路
• 左到右:不断查询,不断缩小上下文,不断地得到新的元素集合
• 右到左:一次查询,多次过滤,第一查找得到的元素集合不断缩小,知道得到最终的集合
b、DOM树
• 左到右:从DOM的上层往底层进行的,需要不断遍历子元素或后代元素,而一个元素节点的子元素或后代元素的个数是未知的或数量较多的
• 右到左:从DOM的底层往上层进行的,需要不断遍历父元素或祖先元素,而一个元素的父元素或者祖先元素的数量是固定的或者有限的
但是从右到左是违背我们的习惯的,这样做到底会不会出现问题呢?答案是会出现错误,请看下面的一个简单DOM树:
这里写图片描述

在上面的例子中,我们看到当选择器表达式中存在位置伪类的时候,就会出现错误,这种情况下没有办法,准确是第一位,只能选择从左到右。
结论: 从性能触发,采取从右到左; 为了准确性,对位置伪类,只能采取从左到右。

3.2涉及编程技巧
3.2.1 短路表达式

逻辑与操作和逻辑或操作是短路操作,可以应用于任何类型的操作数,而不仅仅是布尔值,在有一个操作数不是布尔值的情况下,这两个操作不一定会返回布尔值。
这里写图片描述
这里写图片描述

// rbuggyQSA.length != 0时, 将rbuggyQSA转化为字符串赋给 rbuggyQSA
1405 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
1406 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );

//冒号表达式 相当于多个if else
return operator === "=" ? result === check :
        operator === "!=" ? result !== check :
        operator === "^=" ? check && result.indexOf( check ) === 0 :
        operator === "*=" ? check && result.indexOf( check ) > -1 :
        operator === "$=" ? check && result.slice( -check.length ) === check :
        operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
        operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :false;
3.2.2 闭包实现缓存
function createCache() {
        var keys = [];
        function cache( key, value ) {
                // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
                if ( keys.push( key + " " ) > 10 ) {
                        // Only keep the most recent entries shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
                        delete cache[ keys.shift() ];
                }
                console.log("cache:");
                console.log(cache.hasOwnProperty("12 "));
                //函数即对象  cache函数在实现缓存功能的同时 还作为缓存的存储空间存放键值对
                return (cache[ key + " " ] = value);
        }
        return cache;
}
var testCacheFunc = createCache();
var cacheResult = testCacheFunc(12, "3333" );
console.log("cacheResult 12 333: ");
console.log(cacheResult);//3333
console.log(testCacheFunc["12 "]);//3333
//再次使用testCacheFunc时 console.log(cache.hasOwnProperty("12 "));打印为true
var cacheResult = testCacheFunc(13, "4444" );
console.log("cacheResult 13 444: ");
console.log(cacheResult);//4444
console.log(testCacheFunc["13 "]); //4444

var testCacheFunc2 = createCache(); //相当于定义了另一个缓存区域
console.log("testCacheFunc2 12 ");
console.log(testCacheFunc2["12 "]);//undefined
3.2.3排序数组去重
//有序数组去重函数 
function quchong(results){ 
    var elem,i=0,j;
    var duplicates = []; 
    while ( (elem = results[i++]) ) { 
        console.log("i:" + i ); //从1开始 
        if ( elem === results[ i ] ) { 
        j = duplicates.push( i ); //返回duplicates长度 
        } 
        console.log("i:" + i ); 
        } 
    while ( j-- ) {
        results.splice( duplicates[ j ], 1 ); 
    } 
} 
var results = [1,2,3,3,4,4,4,5]; 
quchong(results); console.log(results);//[1,2,3,4,5]

四 $.callbacks函数管理器实现pub/sub模式

$.Callbacks用来管理函数队列。采用了观察者模式,通过add添加操作到队列当中,通过fire去执行这些操作。实际上$.Callbacks是1.7版本从$.Deferred对象当中分离出来的,主要是实现$.Deferred功能。

应用:

var topics = {};
jQuery.Topic = function( id ) {
  var callbacks, method,
    topic = id && topics[ id ];
  if ( !topic ) {
    callbacks = jQuery.Callbacks();
    topic = {
      publish: callbacks.fire,
      subscribe: callbacks.add,
      unsubscribe: callbacks.remove
    };
    if ( id ) {
      topics[ id ] = topic;
    }
  }
  return topic;
};

// Subscribers
$.Topic( "mailArrived" ).subscribe( fn1 );
$.Topic( "mailArrived" ).subscribe( fn2 );
$.Topic( "mailSent" ).subscribe( fn1 );
// Publisher
$.Topic( "mailArrived" ).publish( "hello world!" );
$.Topic( "mailSent" ).publish( "woo! mail!" );

五 $.deferred 对象

jQuery的所有Ajax操作函数,默认返回的就是一个deferred对象。

5.1 为什么需要deferred对象

由于JavaScript单线程的特点,如果某个操作耗时很长,其他操作就必需排队等待。为了避免整个程序失去响应,通常的解决方法是将那些排在后面的操作,写成“回调函数”(callback)的形式。这样做虽然可以解决问题,但是有一些显著缺点:

1.回调函数往往写成函数参数的形式,导致函数的输入和输出非常混乱,整个程序的可阅读性差;
2.回调函数往往只能指定一个,如果有多个操作,就需要改写回调函数。
3.整个程序的运行流程被打乱,除错和调试的难度都相应增加。

Promises就是为了解决这些问题而提出的,它的主要目的就是取代回调函数,成为非同步操作的解决方案。它的核心思想就是让非同步操作返回一个对象,其他操作都针对这个对象来完成。

5.2 $.deferred对象与promise对象

一个$.deferred对象对象既有promise对象属性,也有promise(obj)方法,deferred.promise()也是deferred对象

promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }

$.deferred 对象是通过promise.promise( deferred );扩展来的,promise有相应的回调函数增添机制,但是没有与改变执行状态有关的方法(比如resolve()方法和reject()方法),相关源码如下:

// promise.progress = list.add
// promise.done = list.add
// promise.fail = list.add
promise[ tuple[ 1 ] ] = list.add;

// deferred.notify = function() { deferred.notifyWith(...) }
// deferred.resolve = function() { deferred.resolveWith(...) }
// deferred.reject = function() { deferred.rejectWith(...) }
deferred[ tuple[ 0 ] ] = function() {
     deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                return this;
};

// deferred.notifyWith = list.fireWith
// deferred.resolveWith = list.fireWith
// deferred.rejectWith = list.fireWith
deferred[ tuple[ 0 ] + "With" ] = list.fireWith;

$.when()接受多个deferred对象作为参数,当它们全部运行成功后,才调用resolved状态的回调函数,但只要其中有一个失败,就调用rejected状态的回调函数。它相当于将多个非同步操作,合并成一个。

$.deferred整体结构:

jQuery.extend( {
    Deferred: function( func ) {
        var tuples = [
            // action, add listener, callbacks,
            // ... .then handlers, argument index, [final state]
            [ "notify", "progress", jQuery.Callbacks( "memory" ),
                jQuery.Callbacks( "memory" ), 2 ],
            [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                jQuery.Callbacks( "once memory" ), 0, "resolved" ],
            [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                    jQuery.Callbacks( "once memory" ), 1, "rejected" ]
            ],
            state = "pending",
            promise = {
                 state: function() {
                    return state;
                },
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },
                "catch": function( fn ) {
                    return promise.then( null, fn );
                },

                  // Keep pipe for back-compat  
            // 若参数为deferred对象:  
            //     注册一个新的deferred对象,起始deferred对象调用notify、resolve、reject方法时触发新deferred对象的同类方法  
            //     进而执行新deferred对象中progress、done、fail方法注册的函数  
            //     示例:  
            //          var dtd=$.Deferred(),deferred=$.Deferred();  
            //          function wait(dad){  
            //              setTimeout(function(){  
            //                  dtd.resovle();  
            //              },3000);  
            //                
            //              return dtd.promise();  
            //          }  
            //          wait(dtd).pipe($.Deferred())// 获得$.Deferred()返回对象  
            //          .done(fn);  
            //          实现功能似,$.Deferred()返回对象done方法中添加fn函数,dtd的done方法中也添加fn函数  
            //          dtd.resovle方法控制着dtd、$.Deferred()返回对象的done方法执行  
            // 若参数为普通函数:  
            //      以deferred.resovle方法传入的参数作为函数的参数,直接执行该函数,函数的返回值作为done、fail方法的参数  
            //      或者将deferred.resovle方法传入的参数作为done、fail方法的参数  
            //      示例:  
            //          var dtd=$.Deferred();  
            //          function wait(dad){  
            //              setTimeout(function(){  
            //                  dtd.resovle(a,b,c);  
            //              },3000);  
            //                
            //              return dtd.promise();  
            //          }  
            //          wait(dtd).pipe(fn1)  
            //          .done(fn2)  
            //          fn1用来格式化dtd.resovle(a,b,c)中a、b、c参数,作为参数传给fn2  
            //          若fn1不存在,直接传递a、b、c给fn2  
            //   
            // done,fail,progress状态执行函数顺序排列,tuples数组元素第四项为对应函数在参数arguments中的位置  
                // Keep pipe for back-compat
                pipe: function( /* fnDone, fnFail, fnProgress */ ) {
                     return jQuery.Deferred( function(   
                     newDefer ) {
                      …………..
                 // 返回newDefer的promise对象,使其不能使用deferred对象的resolve、reject方法在外部改变状态  
                // 链式使用done|fail方法注册函数时,启动函数在pipe方法执行中已挂载 
                    }).promise();
                },
                then: function( onFulfilled, onRejected, onProgress ) {
                     …………………
                     return jQuery.Deferred( function( 
                     newDefer ) {
                      // deferred对象的tuples[i][3]添加resovle函数返回的函数队列  
                    // 通过deferred.notify|resolve|reject方法启动执行,因此是在延迟函数执行过程或完毕以后调用  
                        // progress_handlers.add( ... )
                        tuples[ 0 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onProgress ) ?
                                    onProgress :
                                    Identity,
                                newDefer.notifyWith
                            )
                        );

                        // fulfilled_handlers.add( ... )
                         tuples[ 1 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onFulfilled ) ?
                                    onFulfilled :
                                    Identity
                            )
                     );

                     // rejected_handlers.add( ... )
                    tuples[ 2 ][ 3 ].add(
                            resolve(
                                0,
                                newDefer,
                                jQuery.isFunction( onRejected ) ?
                                    onRejected :
                                    Thrower
                            )
                );
              } ).promise();
              // 创建新的deferred对象,链式注册done|fail函数队列  
              // 该函数队列的触发函数加载到原有deferred对象的tuple[i][3]中  
            },//then
            // Get a promise for this deferred
            // If obj is provided, the promise aspect is added to the object
            promise: function( obj ) {
                    return obj != null ? jQuery.extend( obj, promise ) : promise;
                }
        },//promise
        deferred = {};

       // 以Callbacks构造deferred对象progress、done、fail方法的函数存储域  
        // promise.progress|done|fail执行该三组队列函数的注册  
       //deferred.notify |resolve |rejeact| deferred.notifyWith |resolveWith |rejeactWith  
       // 即时执行相应的函数队列,并执行tuple[3]中的函数队列(promise.then方法注册)  
      // done函数队列率先引入改变state状态值,使fail函数队列失效,progress队列锁定的函数,fail函数相应处理  
       // deferred在延时函数内部使用,延时函数返回promise对象,实现外部接口只能注册add、不能启动fire  
        // Add list-specific methods
        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],
                stateString = tuple[ 5 ];

            // promise.progress = list.add
            // promise.done = list.add
            // promise.fail = list.add
            promise[ tuple[ 1 ] ] = list.add;

            // Handle state
            if ( stateString ) {
                list.add(
                    function() {

                        // state = "resolved" (i.e., fulfilled)
                        // state = "rejected"
                        state = stateString;
                    },

                    // rejected_callbacks.disable
                    // fulfilled_callbacks.disable
                    tuples[ 3 - i ][ 2 ].disable,

                    // progress_callbacks.lock
                    tuples[ 0 ][ 2 ].lock
                );
            }

            // progress_handlers.fire
            // fulfilled_handlers.fire
            // rejected_handlers.fire
            // promise.then方法将函数队列注册在相应的tuple[3]的Callbacks对象里  
           // tuple[2]中添加tuple[3].fire,以执行tuple[3]中注册的函数队列  
            list.add( tuple[ 3 ].fire );

            // deferred.notify = function() { deferred.notifyWith(...) }
            // deferred.resolve = function() { deferred.resolveWith(...) }
            // deferred.reject = function() { deferred.rejectWith(...) }
            deferred[ tuple[ 0 ] ] = function() {
                deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                return this;
            };

            // deferred.notifyWith = list.fireWith
            // deferred.resolveWith = list.fireWith
            // deferred.rejectWith = list.fireWith
            deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
        } );
        // Make the deferred a promise
        promise.promise( deferred );

        // Call given func if any
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    },//Deferred
    // Deferred helper

// var dtd=$.Deferred();  
// function wait(dtd){  
//      setTimeout(function(){  
//          dtd.resovle()  
//      },3000)  
//        
//      return dtd.promise()  
// }  
// $.when(wait(dtd)).done(function(){console.log("success")})  
//   
// $.when方法主要意图:多个延迟对象同时执行,最末一个执行完毕,调用done方法注册的函数  
// 实际境况:触发函数在$.when(dtd)参数dtd函数中  
// 函数队列在$.when(dtd).done(fn).fail(fn)中以done|fail方法注册  
// 主要问题:dtd.resovle参数传入函数队列  
// $.when参数有多个延迟对象[dtd]时,判断dtd执行完毕总时间  
// 实现方案:以dtd.resovle方法启动dtd.done()注册的函数队列  
// 构造新的deferred对象master,且master.promise()作为返回结果  
// master.resovle|reject方法加入dtd.done()函数队列,因此dtd.resovle方法同时启动master的函数队列  
// 同时参数由dtd.resovle传入dtd.done(),最终传入master.done|fail方法中  
//多个延迟对象通过闭包外参数remaining判断这几个延迟对象是否执行完毕  
// 设计:updateFunc方法在$.when()参数延迟对象执行完毕后调用  
// 用remaining判断$.when()参数延迟对象是否执行完毕  
//  remaining==0时触发执行$.when().done()注册的函数队列  
// 通过条件语句区分$.when()参数为deferred对象,还是普通函数,或者为空  
// 若为延迟对象,通过deferred.done|then注册updateFunc,执行$.when().done()的函数队列  
// 若为普通函数,立即调用updateFunc,执行$.when().done()的函数队列,传参为$.when()中参数  
// 若为空,立即执行$.when().done()的函数队列  
    when: function( singleValue ) {
        var
            // count of uncompleted subordinates
            remaining = arguments.length,

            // count of unprocessed arguments
            i = remaining,

            // subordinate fulfillment data
            resolveContexts = Array( i ),
            resolveValues = slice.call( arguments ),

            // the master Deferred
            master = jQuery.Deferred(),
            // 通过闭包驻留remaining,用以判断$.when各参数deferred对象均执行完毕  
           // 执行完毕通过master.resolveWith方法,执行$.when().done()中done方法注册的函数  
            // subordinate callback factory
            updateFunc = function( i ) {
                return function( value ) {
                    resolveContexts[ i ] = this;
                    resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                    if ( !( --remaining ) ) {
                        master.resolveWith( resolveContexts, resolveValues );
                    }
                };
            };

        // Single- and empty arguments are adopted like Promise.resolve
        if ( remaining <= 1 ) {
            adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
                !remaining );

            // Use .then() to unwrap secondary thenables (cf. gh-3000)
            if ( master.state() === "pending" ||
                jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {

                return master.then();
            }
        }

        // Multiple arguments are aggregated like Promise.all array elements
        while ( i-- ) {
            adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
        }

        return master.promise();
    }
} );
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值