五、jQuery源码分析——工具方法

       jQuery的工具方法,也就是jQuery对象的静态方法,直接以$.functionName的方式进行调用。通过这些方法,极大的提升了我们的开发效率。由于这部分方法比较多,单个方法也比较容易理解,所以,我不打算在本篇文章中对他们进行逐一讲解,只一些我自己感觉设计比较精彩的地方做一些补充和说明。

一、工具方法简化结构

       由于源码长度过长,所以我们还是以列出结构的方式,庖丁解牛般的理解这部分代码。

jQuery.extend({
  expando: 生成唯一的jQuery字符串(内部)
  noConflict():防止冲突
  isReady():DOM是否加载完()
  readyWait:等待多少文件的计数器
  holdReady:推迟DOM触发
  ready:准备DOM触发
  isFunction:是否为函数
  isArray:是否为数组
  isWindow:是否为window
  isNumeric:是否为数字
  type:判断数据类型
  isPlainObject:是否为对象字面量
  isEmptyObject:是否为空的对象
  error:抛出异常
  parseHTML:解析节点
  parseJSON:解析json
  parseXML:解析XML
  noop:空函数
  globalEval:全局解析js
  camelCase:转驼峰
  nodeName:是否为指定节点名(内部)
  each():遍历集合
  trim():去前后空格
  makeArray():类数组转真数组
  inArray():数组版indexOf
  merge():合并数组
  grep()过滤新数组
  map()映射新数组
  guid()唯一标识符(内部)
  proxy()改this指向
  access() 多功能值操作(内部)
  now()当前时间
  swap():css交换(内部)

})
jQuery.ready.promise = function(){};    监控DOM的异步操作(内部)
function isArraylike(){}    类似数组的判断

二、jQuery工具方法的使用

1. expando 生成唯一的jQuery字符串
expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
// 使用
$.expando
2. noConflict 防冲突

       所谓防冲突,我们要理解怎么个防止法。jQuery中的防冲突方法就是当其他的第三库使用了或者jQuery。没错,就是放弃,另起一个名字。够伟大吧,或许我们可以把这种方式运用到我们的生活中,放弃设计另一种新生!

2.1 用法及源码分析

jQuery初始化中定义的变量

// Map over jQuery in case of overwrite
_jQuery = window.jQuery,

// Map over the $ in case of overwrite
_$ = window.$,

jQuery初始化的赋值操作

window.jQuery = window.$ = jQuery;

源码:

noConflict: function( deep ) {
   if ( window.$ === jQuery ) {
        window.$ = _$;
    }

    if ( deep && window.jQuery === jQuery ) {
        window.jQuery = _jQuery;
    }
    return jQuery;
},

使用1

var miaov = $.noConflict()
var $ = 123
miaov(function(){
    alert($)   // 123
 })

       因为var $ = 123$.noConflict()后面调用,所以window.$ !== jQuery,window.jQuery === jQuery,因为这两行代码在jQuery初始化的过程中已经被执行了。结合源码,我们可以知道这时$的值已经是123了,只有window.jQuery依然坚挺,并将其赋值于的miaov。所以miaov便代理了$
使用2




 





       上面的代码会执行源码中的第一个if条件循环。执行顺序是

var $ = 123    //步骤1
var $ = jQuery = function(){}    //步骤2
var maiov = $. noConflict()    //步骤3

在执行步骤2,加载jquery库时间,在jquery库的第38、41行分别执行以下代码:

// 也就是将在加载jquery库之前定义的$和jquery,在例子中的123、456保存在变量_jQuery  和变量_$
var
_jQuery = window.jQuery,
_$ = window.$

使用3




 





       这种用法是上一用法的增强版,通过传递第二个参数(boolean)来确定是否改变window.jQuery的值。

3. holdReady 等待多少文件的计数器

3.1 用法
//在添加上下面的一行代码时,下面的代码不会再执行
$.holdReady(true)
// 在添加上一行代码的情况下,再添加上下面的代码,程序完整执行
$.holdReady(false)
$(function(arg){
     alert('123')
})
/* a.js */
// 下面的代码运行结果,先弹出2,再弹出1,这是因为getScript函数异步加载了a.js
alert('1)
$.getScript('a.js',function(){ })
$(function(){
      alert(2)
 })

可以通过holdReady()方法来解决这个异步加载造成的问题

$.holdReady(true)
$.getScript('a.js',function(){
    $.holdReady(false)
})
$(function(){
    alert(2)
})
3.2 源码分析
holdReady: function( hold ) {
        //当传递的参数为true时,变量readyWait递增
    if ( hold ) {
        jQuery.readyWait++;
    } else {
        // 当不传参数时,会执行jQuery.ready()
        jQuery.ready( true );
    }
},

4. isReady DOM是否加载完

4.1比较下面的两种事件加载
$(function(){ })   // 第一种方式
window.onload= function(){ }    //第二种方式

第一种方式方式其实使用的是DOMContentLoaded这个事件,这个事件只需要网页结构加载完成后就可被触发。
第二种方式onload会等到网页加载完成并且页面中的资源(图片等)下载完成后再触发。

4.2 jQuery中事件的执行流程
1.$(function(){ })
2.$(document).ready(function(){ })
3.$().ready()
4.jQuery.ready.promise().done( fn )
5.if (document.readyState === 'complete') {
} else {
  completed
}
6.readyList.resolveWith( document, [jQuery])
7.执行fn函数。

第1、2、3步都是对函数之间的相互引用。
第4、5步查看819-840行代码

jQuery.ready.promise = function( obj ) {
    if ( !readyList ) {
        readyList = jQuery.Deferred();
        if ( document.readyState === "complete" ) {
            setTimeout( jQuery.ready );
        } else {
            document.addEventListener( "DOMContentLoaded", completed, false );
            window.addEventListener( "load", completed, false );
        }
    }
    return readyList.promise( obj );
};

在第一次只执行的时候,readyList为空,会执行整个if函数,给readyList赋值延迟对象,这个函数最后会返回一个promise对象,它的作用是不让外部修改readyList的状态,原理后面再续。
在内层的循环中

if ( document.readyState === "complete" ) {
  //document.readyState === 'complete'时,页面完全加载
 //此处使用setTimeout为了兼容ie
    setTimeout( jQuery.ready );
} else {
//页面在没有完全加载的时,监测DOMContentLoaded和load事件
  document.addEventListener( "DOMContentLoaded", completed, false );
  window.addEventListener( "load", completed, false );
}

上面的代码中,在页面没有完全加载的时候,同时监听了DOMContentLoaded和load事件,这是因为,load事件会被缓存,所以此处的操作表示,那个事件被触发的早就执行哪个事件。事件的触发之后,会调用completed函数。completed函数的源码如下:

// 解绑定DOMContentLoaded和load事件,并且执行jQuery.ready()
completed = function() {
    document.removeEventListener( "DOMContentLoaded", completed, false );
    window.removeEventListener( "load", completed, false );
    jQuery.ready();
};

第6步 jQuery.ready()
jQuery.ready()函数中有这么一行代码(line:398)

// 这行代码为第4步中的fn函数指定执行环境(document)和传入参数(jQuery)
eadyList.resolveWith( document, [ jQuery ] );

//可以编写如下测试代码
$(function(arg){
      alert(this) //[object HTMLDocument]
      alert(arg)  //jQuery构造函数
})
4.3 ready方法详解
ready: function( wait ) {
        //block1
    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
        return;
    }
    jQuery.isReady = true;
        //block2
    if ( wait !== true && --jQuery.readyWait > 0 ) {
        return;
    }
        //block3
        readyList.resolveWith( document, [ jQuery ] );
        //block4
    if ( jQuery.fn.trigger ) {
        jQuery( document ).trigger("ready").off("ready");
     }
},
4.31 block1
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
    return;
}
jQuery.isReady = true;

在源码中调用ready函数的时候一般都是$.ready()这样的形式,也就是ready函数的wait=false,由于jQuery.isReady默认为false,所以第一次执行是不执行这个if块中的代码的,第二次才会执行,但是要在jQuery.readyWait大于0的情况下。
这种机制是为了解决下面这种问题

$.holdReady(true)
$.getScript('a.js',function(){
    $.holdReady(false)
})
$.holdReady(true)
$.getScript('a.js',function(){
    $.holdReady(false)
})
$.holdReady(true)
$.getScript('a.js',function(){
    $.holdReady(false)
})

上面的代码执行了三次.holdReady()把jQuery.readyWait的值递减为0时,才表明DOM准备好了,就可以执行函数了。

4.33 block3 处理内部jQuery.ready(true)的情况,逻辑与4.3.2相同。
4.34 block4 以事件的方式调用ready函数

除了下面两种方式触发初始化事件,还有第三种方式,block4的代码

//方式1
$(function(){ })
//方式2
$(document).ready(function(){ })
//方式3
$(document).on('ready', function(){
})

5、isFunction 判断是不是函数

源码以及使用如下

// 调用了jQuery.type函数
isFunction: function( obj ) {
    return jQuery.type(obj) === "function";
},

// 用法
function show(){}
var show1 = function(){}
$(function(){
    alert($.isFunction(show))   // true
    alert($.isFunction(show1))  // true
 })

6、isArray 判断是不是数组

// 这个直接调用了原生的函数
isArray: Array.isArray,

7、isWindow 判断是不是window对象

isWindow: function( obj ) {
    return obj != null && obj === obj.window;
},

js中的window有两种意义,第一种是作为全局对象,第二种是作为窗口对象。
对于这两个条件的讲解如下:

// undefinded == null     // true
// null == null                 // true
obj != null
当这个条件成立时,就会排除obj是undefinded和null的可能,进入第二个条件
obj === obj.window
//本着只有对象才有属性的概念,如果obj === obj.window时,就可以断定是window对象。

8、isNumeric判断是不是number类型

//原则
// 非Nan
// 有限
isNumeric: function( obj ) {
    return !isNaN( parseFloat(obj) ) && isFinite( obj );
},

9、type 判断数据类型

9.1 用法

alert($.type('antiai'))     // string
alert($.type(null))         // null
alert($.type(undefined))    //undefinded
alert($.type([]))           //array
alert($.type({}))           //object

9.2 源码解读

type: function( obj ) {
    if ( obj == null ) {
        return String( obj );
    }
    return typeof obj === "object" || typeof obj === "function" ?
        class2type[ core_toString.call(obj) ] || "object" :
        typeof obj;
}

9.2.1 传入的参数如果是 null 或者undefinded,会进入第一个循环,原样输出。
9.2.2 通过typeof判断出来的结果如果是object || function,则会说明它是复杂的引用类型,进行class2type[ core_toString.call(obj) ] || "object" 运算,如果不是,则说明它是基本数据类型,则进行typeof obj操作即可。
关于class2type[ core_toString.call(obj) ]操作,介绍如下:

//class2type[ core_toString.call(obj) ]
//在源码844-846行为class2type保存了一些值
//执行这段代码后,class2type的保存的值的格式如下截图
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
    class2type[ "[object " + name + "]" ] = name.toLowerCase();
});
五、jQuery源码分析——工具方法_第1张图片
image.png

再对core_toString.call(obj) 进行分析

// line56
core_toString = class2type.toString,

//使用
alert({}.toString.call({}))             //[object Object]
alert({}.toString.call([]))             //[object Array]
alert({}.toString.call(function(){}))   //[object Function]

从返回值,我们就可以知道它的使用用途了。

10.isPlainObject判断是否为对象字面量

10.1用法

var obj = {}
alert($.isPlainObject(obj))              // true
alert($.isPlainObject(new Object()))     // true
alert($.isPlainObject([]))               // false

10.2 源码解析

    isPlainObject: function( obj ) {
        if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
            return false;
        }

        try {
            if ( obj.constructor &&
                    !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
                return false;
            }
        } catch ( e ) {
            return false;
        }

        return true;
    }

10.2.1 首先判断当传入的参数满足下面的三个条件,便返回false。

//1、不是对象
jQuery.type( obj ) !== "object"
//2、是DOM节点
obj.nodeType
//3、是window对象
jQuery.isWindow( obj )

10.2.2 接着判断 这个对象不是是满足下面的两个条件,如果都不足,就返回false

// 有原型
obj.constructor

var obj = {}
console.log(obj.constructor)    //Object构造函数

//line:57   core_hasOwn = class2type.hasOwnProperty,
// 根据上面的推导结果可知,下面的两行代码是等价的
core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" )
Object.prototype.hasOwnProperty('isPrototypeOf')    //true

11、isEmptyObject 是否为空对象

11.1 用法

alert($.isEmptyObject({}))   // true
alert($.isEmptyObject({name: 'antiai'})) // false
           
function Aaa(){}
var aaa = new Aaa()
alert($.isEmptyObject(aaa))  // true

function Bbb(){
    this.func = function(){}
 }
var bbb = new Bbb()
alert($.isEmptyObject(bbb))  // false

function Ccc(){}
Ccc.prototype.func = function(){}
var ccc = new Ccc()
alert($.isEmptyObject(ccc))  // false

11.2 源码分析

isEmptyObject: function( obj ) {
    var name;
    for ( name in obj ) {
        return false;
    }
    return true;
},

这段代码主要运用了对象的for...in 循环,for ... in循环只能遍历出挂在对象自生上面的属性和方法并且是自定义的,而不能遍历处原型上面的方法。

13、error抛出错误

13.1 用法

$.error('this is error')

13.2 源码分析

error: function( msg ) {
    throw new Error( msg );
},

14、parseHTML 解析节点

14.1 用法

var str = '
  • 1
  • 2
  • 你可能感兴趣的:(五、jQuery源码分析——工具方法)