浅析jQuery团体框架与完成(上)

媒介

jQuery团体框架甚是庞杂,也不容易读懂,这几日一直在研讨这个笨重而壮大的框架。jQuery的整体架构可以分为:进口模块、底层模块和功用模块。这里,我们以jquery-1.7.1为例举行剖析。

jquery的整体架构

16 (function( window, undefined ) {
         // 组织 jQuery 对象
  22     var jQuery = (function() {
  25         var jQuery = function( selector, context ) {
  27                 return new jQuery.fn.init( selector, context, rootjQuery );
  28             },
                 // 一堆局部变量声明
  97         jQuery.fn = jQuery.prototype = {
  98             constructor: jQuery,
  99             init: function( selector, context, rootjQuery ) { ... },
                 // 一堆原型属性和要领
 319         };
 322         jQuery.fn.init.prototype = jQuery.fn;
 324         jQuery.extend = jQuery.fn.extend = function() { ... };
 388         jQuery.extend({
                 // 一堆静态属性和要领
 892         });
 955         return jQuery;
 957     })();
          // 省略其他模块的代码 ...
9246     window.jQuery = window.$ = jQuery;
9266 })( window );

剖析一下以上代码,我们发明jquery采取了匿名函数自实行的写法,如许做的优点就是可以有用的防备定名空间与变量污染的题目。缩写一下以上代码就是:

(function(window, undefined) {
    var jQuery = function() {}
    // ...
    window.jQuery = window.$ = jQuery;
})(window);

参数window

匿名函数传了两个参数进来,一个是window,一个是undefined。我们晓得,在js中变量是有作用域链的,这两个变量的传入就会变成匿名函数的局部变量,接见起来的时刻速率会更快。经由过程传入window对象可以使window对象作为局部变量运用,那末,函数的参数也都变成了局部变量,当在jquery中接见window对象时,就不须要将作用域链退回到顶层作用域,从而可以更快的接见window对象。

参数undefined

js在查找变量的时刻,js引擎起首会在函数本身的作用域中查找这个变量,假如没有的话就继承往上找,找到了就返回该变量,找不到就返回undefinedundefinedwindow对象的一个属性,经由过程传入undefined参数,但又不举行赋值,可以收缩查找undefined时的作用域链。在 自挪用匿名函数 的作用域内,确保undefined是真的未定义。由于undefined可以被重写,给予新的值。

jquery.fn是啥?

 jQuery.fn = jQuery.prototype = {
              constructor: jQuery,
              init: function( selector, context, rootjQuery ) { ... },
                 // 一堆原型属性和要领
        };

经由过程剖析以上代码,我们发明jQuery.fn即是jQuery.prototype,如许写的优点就是越发简短吧。以后,我们又看到jquery为了简约,痛快运用一个$标记来替代jquery运用,因而,在我们运用jquery框架的运用常常都邑用到$()

组织函数jQuery()

《浅析jQuery团体框架与完成(上)》

jQuery的对象并非经由过程 new jQuery 建立的,而是经由过程 new jQuery.fn.init 建立的:

var jQuery = function( selector, context ) {

       return new jQuery.fn.init( selector, context, rootjQuery );

}

这里定义了一个变量jQuery,他的值是jQuery组织函数,在955行(最上面的代码)返回并赋值给jQuery变量

jQuery.fn.init

jQuery.fn (上面97行)是组织函数jQuery()的原型对象,jQuery.fn.init()jQuery原型要领,也可以称作组织函数。担任剖析参数selectorcontext的范例并实行响应的查找。

参数context:可以不传入,或许传入jQuery对象,DOM元素,一般js对象之一
参数rootjQuery:包括了document对象的jQuery对象,用于document.getElementById()查找失利等状况。

jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype

jQuery(selector [,context])

默许状况下,对婚配元素的查找从根元素document 对象最先,即查找局限是悉数文档树,不过也可以传入第二个参数context来限制它的查找局限。比方:

$('div.foo').click(function () {
            $('span',this).addClass('bar');//限制查找局限,即上面的context
   });

$.extend()和$.fn.extend()

要领jQuery.extend(object)jQuery.fn.extend(object)用于兼并两个或多个对象到第一个对象。相干源代码以下(部份):

jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,//定义的一组局部变量
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

相干变量寄义以下:

变量options:指向某个源对象
变量name:示意目的对象的某个属性名
变量src:示意目的对象的某个属性的原始值
变量copy:示意某个源对象的某个属性的值
变量copyIsArray:指点变量copy是不是是数组
变量clone:示意深度复制时原始值的修改值
变量target:指向目的对象
变量i:示意源对象的肇端下标
变量length:示意参数的个数,用于修转变量
变量deep:指点是不是实行深度复制,默许为false

jQuery.extend(object); 为jQuery类增加增加类要领,可以明白为增加静态要领。如:

$.extend({ 
  add:function(a,b){
        return a+b;
    } 
}); 

便为 jQuery 增加一个为add 的 “静态要领”,以后便可以在引入 jQuery 的处所,运用这个要领了, 就是将add要领兼并到jquery的全局对象中。

$.add(3,4); //return 7 

jQuery.fn.extend(object),检察一段官网的代码演示以下:

<label><input type="checkbox" name="foo"> Foo</label>
<label><input type="checkbox" name="bar"> Bar</label>

<script>
    jQuery.fn.extend({
        check: function() {
            return this.each(function() {
                this.checked = true;
            });
        },
        uncheck: function() {
            return this.each(function() {
                this.checked = false;
            });
        }
    });
    // Use the newly created .check() method
    $( "input[type='checkbox']" ).check();
</script>

看下面一个jQuery对象的扩大要领:

 $.fn.extend({
      sayHello : function(){
        alert('hello');
    }
 });
 

就是将sayHello要领兼并到jquery的实例对象中

CSS选择器引擎 Sizzle

可以说,jQuery是为操纵DOM而降生的,jQuery之所以云云壮大,得益于CSS选择器引擎 Sizzle,剖析划定规矩援用网上的一段实例:

selector:"div > p + div.aaron input[type="checkbox"]"

剖析划定规矩:
1 根据从右到左
2 掏出末了一个token  比方[type="checkbox"]
                            {
                                matches : Array[3]
                                type    : "ATTR"
                                value   : "[type="
                                checkbox "]"
                            }
3 过滤范例 假如type是 > + ~ 空 四种关联选择器中的一种,则跳过,在继承过滤
4 直到婚配到为 ID,CLASS,TAG  中一种 , 由于如许才经由过程浏览器的接口讨取
5 此时seed种子合集中就有值了,如许把刷选的前提给缩的很小了
6 假如婚配的seed的合集有多个就须要进一步的过滤了,修改选择器 selector: "div > p + div.aaron [type="checkbox"]"
7 OK,跳到一下阶段的编译函数

deferred对象

开辟网站的过程当中,我们常常碰到某些耗时很长的javascript操纵。个中,既有异步的操纵(比方ajax读取服务器数据),也有同步的操纵(比方遍历一个大型数组),它们都不是立即能获得效果的。

一般的做法是,为它们指定回调函数(callback)。即事前划定,一旦它们运转完毕,应当挪用哪些函数。

然则,在回调函数方面,jQuery的功用异常弱。为了转变这一点,jQuery开辟团队就设想了deferred对象。

简朴说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意义是”耽误”,所以deferred对象的寄义就是”耽误”到将来某个点再实行。

回忆一下jQueryajax操纵的传统写法:

$.ajax({
 
   url: "test.html",
 
   success: function(){
     alert("哈哈,胜利了!");
   },
 
   error:function(){
     alert("失足啦!");
   }
 

 });

在上面的代码中,$.ajax()接收一个对象参数,这个对象包括两个要领:success要领指定操纵胜利后的回调函数,error要领指定操纵失利后的回调函数。

$.ajax()操纵完成后,假如运用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法举行链式操纵;假如高于1.5.0版本,返回的是deferred对象,可以举行链式操纵。

如今,新的写法是如许的:

$.ajax("test.html")
 
  .done(function(){ alert("哈哈,胜利了!"); })
 
  .fail(function(){ alert("失足啦!"); });

为多个操纵指定回调函数

deferred对象的另一大优点,就是它许可你为多个事宜指定一个回调函数,这是传统写法做不到的。

请看下面的代码,它用到了一个新的要领$.when()

$.when($.ajax("test1.html"), $.ajax("test2.html"))
 
 .done(function(){ alert("哈哈,胜利了!"); })
 
 .fail(function(){ alert("失足啦!"); });

这段代码的意义是,先实行两个操纵$.ajax("test1.html")$.ajax("test2.html"),假如都胜利了,就运转done()指定的回调函数;假如有一个失利或都失利了,就实行fail()指定的回调函数。

jQuery.Deferred( func ) 的完成道理

内部保护了三个回调函数列表:胜利回调函数列表、失利回调函数列表、音讯回调函数列表,其他要领则缭绕这三个列表举行操纵和检测。

jQuery.Deferred( func ) 的源码构造:

 jQuery.extend({
 
    Deferred: function( func ) {
            // 胜利回调函数列表
        var doneList = jQuery.Callbacks( "once memory" ),
            // 失利回调函数列表
            failList = jQuery.Callbacks( "once memory" ),
            // 音讯回调函数列表
            progressList = jQuery.Callbacks( "memory" ),
            // 初始状况
            state = "pending",
            // 异步行列的只读副本
            promise = {
                // done, fail, progress
                // state, isResolved, isRejected
                // then, always
                // pipe
                // promise           
            },
            // 异步行列
            deferred = promise.promise({}),
            key;
        // 增加触发胜利、失利、音讯回调函列表的要领
        for ( key in lists ) {
            deferred[ key ] = lists[ key ].fire;
            deferred[ key + "With" ] = lists[ key ].fireWith;
        }
        // 增加设置状况的回调函数
        deferred.done( function() {
            state = "resolved";
        }, failList.disable, progressList.lock )
        .fail( function() {
            state = "rejected";
        }, doneList.disable, progressList.lock );
        // 假如传入函数参数 func,则实行。
        if ( func ) {
            func.call( deferred, deferred );
        }
 
        // 返回异步行列 deferred
        return deferred;
    },
}
         
 
jQuery.when( deferreds )
 
供应了基于一个或多个对象的状况来实行回调函数的功用,一般是基于具有异步事宜的异步行列。
jQuery.when( deferreds ) 的用法
 
假如传入多个异步行列对象,要领 jQuery.when() 返回一个新的主异步行列对象的只读副本,只读副本将跟踪所传入的异步行列的终究状况。
 
一旦一切异步行列都变成胜利状况,“主“异步行列的胜利回调函数被挪用;
 
假如个中一个异步行列变成失利状况,主异步行列的失利回调函数被挪用。
 
 
/*
要求 '/when.do?method=when1' 返回 {"when":1}
要求 '/when.do?method=when2' 返回 {"when":2}
要求 '/when.do?method=when3' 返回 {"when":3}
*/
var whenDone = function(){ console.log( 'done', arguments ); },
    whenFail = function(){ console.log( 'fail', arguments ); };
$.when(
    $.ajax( '/when.do?method=when1', { dataType: "json" } ),
    $.ajax( '/when.do?method=when2', { dataType: "json" } ),
    $.ajax( '/when.do?method=when3', { dataType: "json" } )
).done( whenDone ).fail( whenFail );

《浅析jQuery团体框架与完成(上)》

异步行列 Deferred

解耦异步使命和回调函数

为 ajax 模块、行列模块、ready 事宜供应基本功用。

原型属性和要领

原型属性和要领源代码:

 
  97 jQuery.fn = jQuery.prototype = {
  98     constructor: jQuery,
  99     init: function( selector, context, rootjQuery ) {}
 210     selector: "",
 213     jquery: "1.7.1",
 216     length: 0,
 219     size: function() {},
 223     toArray: function() {},
 229     get: function( num ) {},
 241     pushStack: function( elems, name, selector ) {},
 270     each: function( callback, args ) {},
 274     ready: function( fn ) {}, //
 284     eq: function( i ) {},
 291     first: function() {},
 295     last: function() {},
 299     slice: function() {},
 304     map: function( callback ) {},
 310     end: function() {},
 316     push: push,
 317     sort: [].sort,
 318     splice: [].splice
 319 };

属性selector用于纪录jQuery查找和过滤DOM元素时的选择器表达式。
属性.length示意当前jquery对象中元素的个数。
要领.size()返回当前jquery对象中元素的个数,功用上等同于属性length,但应当优先运用length,由于他没有函数挪用开支。

.size()源码以下:

size():function(){
    return this.length;
}

要领.toArray()将当前jQuery对象转换为真正的数组,转换后的数组包括了一切元素,其源码以下:

toArray: function() {
        return slice.call( this );
    },

要领.get(index)返回当前jQuery对象中指定位置的元素,或包括了悉数元素的数组。其源
码以下:

    get: function( num ) {
        return num == null ?

            // Return a 'clean' array
            this.toArray() :

            // Return just the object
            ( num < 0 ? this[ this.length + num ] : this[ num ] );
    },

假如没有传入参数,则挪用.toArray()返回了包括有锁元素的数组;假如指定了参数index,则返回一个零丁的元素,index从0最先计数,而且支撑负数。

起首会推断num是不是小于0,假如小于0,则用length+num从新盘算下标,然后运用数组接见操纵符([])猎取指定位置的元素,这是支撑下标为负数的一个小技能;假如大于即是0,直接返回指定位置的元素。

eg()和get()运用详解:http://segmentfault.com/blog/…

要领.each()用于遍历当前jQuery对象,并在每一个元素上实行回调函数。要领.each()内部经由过程简朴的挪用静态要领jQuery.each()完成:

each: function( callback, args ) {
        return jQuery.each( this, callback, args );
    },

回调函数是在当前元素为上下文的语境中触发的,即关键字this老是指向当前元素,在回调函数中return false 可以停止遍历。

要领.map()遍历当前jQuery对象,在每一个元素上实行回调函数,并将回调函数的返回值放入一个新jQuery对象中。该要领常用于猎取或设置DOM元素鸠合的值。

map: function( callback ) {
        return this.pushStack( jQuery.map(this, function( elem, i ) {
            return callback.call( elem, i, elem );
        }));
    },

原型要领.pushStack()建立一个新的空jQuery对象,然后把DOM元素鸠合放入这个jQuery对象中,并保存对当前jQuery对象的援用。

原型要领.pushStack()是中心要领之一,它为以下要领供应支撑:

jQuery对象遍历:.eq()、.first()、.last()、.slice()、.map()。

DOM查找、过滤:.find()、.not()、.filter()、.closest()、.add()、.andSelf()。

DOM遍历:.parent()、.parents()、.parentsUntil()、.next()、.prev()、.nextAll()、.prevAll()、.nextUnit()、.prevUnit()、.siblings()、.children()、.contents()。

DOM插进去:jQuery.before()、jQuery.after()、jQuery.replaceWith()、.append()、.prepent()、.before()、.after()、.replaceWith()。

定义要领.push( elems, name, selector ),它接收3个参数:

参数elems:将放入新jQuery对象的元素数组(或类数组对象)。

参数name:发生元素数组elems的jQuery要领名。

参数selector:传给jQuery要领的参数,用于修改原型属性.selector。

要领.end()完毕当前链条中近来的挑选操纵,并将婚配元素还原为之前的状况

end: function() {
        return this.prevObject || this.constructor(null);
    },

返回前一个jQuery对象,假如属性prevObect不存在,则构建一个空的jQuery对象返回。要领.pushStack()用于入栈,要领.end()用于出栈

静态属性和要领

相干源码以下:

388 jQuery.extend({
 389     noConflict: function( deep ) {},
 402     isReady: false,
 406     readyWait: 1,
 409     holdReady: function( hold ) {},
 418     ready: function( wait ) {},
 444     bindReady: function() {},
 492     isFunction: function( obj ) {},
 496     isArray: Array.isArray || function( obj ) {},
 501     isWindow: function( obj ) {},
 505     isNumeric: function( obj ) {},
 509     type: function( obj ) {},
 515     isPlainObject: function( obj ) {},
 544     isEmptyObject: function( obj ) {},
 551     error: function( msg ) {},
 555     parseJSON: function( data ) {},
 581     parseXML: function( data ) {},
 601     noop: function() {},
 606     globalEval: function( data ) {},
 619     camelCase: function( string ) {},
 623     nodeName: function( elem, name ) {},
 628     each: function( object, callback, args ) {},
 669     trim: trim ? function( text ) {} : function( text ) {},
 684     makeArray: function( array, results ) {},
 702     inArray: function( elem, array, i ) {},
 724     merge: function( first, second ) {},
 744     grep: function( elems, callback, inv ) {},
 761     map: function( elems, callback, arg ) {},
 794     guid: 1,
 798     proxy: function( fn, context ) {},
 825     access: function( elems, key, value, exec, fn, pass ) {},
 852     now: function() {},
 858     uaMatch: function( ua ) {},
 870     sub: function() {},
 891     browser: {}
 892 });

要领jQuery.noConflict([removeAll])用于开释jQuery对全局变量$的控制权,可选参数removeAll指点是不是开释对全局变量jQuery的控制权。
在运用jQuery的同时,假如须要运用另一个javascript库,可以挪用$.noConflict()返回$给其他库

要领jQuery.isFunction(obj)用于推断传入的参数是不是是函数;
要领jQuery.isArray(obj)用于推断传入的参数是不是是数组。

这两个要领的完成依赖于jQuer.type(obj),经由过程推断jQuery.type(obj)返回值是不是是functionarray来完成
相干源码以下:

492  isFunction : function(obj){
493        return jQuery.type(obj) === "function";
494     },
495  
496     isArray: Array.isArray || function(obj){
497        return jQuery.type(obj) === "array";
498     },

要领jQuery.type(obj)用于推断参数的内建JavaScript范例。假如参数是undefinednull ,返回 undefinednull;假如参数是JavaScript内部对象,则返回对象
的字符串称号;其他状况一概返回object,相干源码以下:

509  type :function(obj){
510        return obj == null ?
511          String( obj ) :
512          class2type[toString.call(obj)] || "object";
513    },

要领jQuery.isWindow(obj)用于推断传入的参数是不是是window对象,经由过程检测是不是存在特性属性setInterval来完成,相干代码以下:

 
501        isWindow: function(obj){
502            return obj && typeof obj ==="object" && "setInterval" in obj;
503        },

本人兼职前端付费技术顾问,如需协助请加本人微信hawx1993,非诚勿扰

1.为初学前端而不晓得怎么做项目的你指点
2.指点并踏实你的JavaScript基本
3.帮你预备口试并供应相干指点性看法
4.为你的前端之路供应极具建设性的看法
5.让你的进修更有用率,对知识点的明白越发透辟
6.不解答可以百度到的答案,也不解答你碰到的bug

付费(每个月仅需99)能让我更有耐烦和责任心的为你指点,让两边发生更优越的合作
    原文作者:trigkit4
    原文地址: https://segmentfault.com/a/1190000000754325
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞