jQuery源码阅读(二)---初识init方法

上一篇博客大体了解了jQuery整个的架构,即分成jQuery入口模块,底层支持模块以及功能模块这三个大块,并且各个模块之间有一定的联系;下来主要理解了jQuery入口模块,即jQuery对象是如何创建的,其实真正创建的jQuery实例是由jQuery.fn.init()函数创建的,并且为了使得创建的每个对象实例都拥有jQuery对象的属性和方法,即所有jQuery对象共享一份属性和方法代码,这样可大大减少所需的内存空间,利用了原生JS原型链的概念,并使得jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype。

这一次主要想重点理解init()方法中是如何来创建jQuery对象的。我们使用过jQuery库的都知道如何将DOM元素转换成jQuery对象,如何创建一个含有HTML片段的jQuery对象,如何获取到id为‘id’的jQuery对象,一个简单的$(DOM元素)、$("<div></div>")和$("#id")就可以实现。这些都是init这一个函数解决的,可见,init函数里面的逻辑太庞大。下来一点一点的缕清它。

jQuery入口模块

想想我们用jQuery时,最终结果是创建了一个jQuery对象的常用方式有哪些?
我能想到的就是:

//参数为函数,ready方法,等价于$().ready(function(){})
$(function(){
})              
//参数为各种选择器
$('#id');$(''.class'');$(各种选择器);
//参数为body
$('body');
//参数为HTML代码片段
$('<div></div>'); $('<img src = '' />');
//参数为DOM元素,即将DOM元素转换为jQuery对象;或者是一个包含多个DOM元素的数组
var node = document.getELementById("id");
var nodeArray = document.getElementsByTagName("div");
$(node);  
$(nodeArray);

上面是我们利用jQuery常用的一些操作,参考了“jQuery技术内幕:深入解析jQuery架构设计与实现原理” 高云”这本书之后,里面对$()的用法还有一些补充,比如:
这里写图片描述
这里写图片描述
这里写图片描述

下面就是针对不同的参数去进行不同的操作,相当于函数的重载。
上面也可以看出参数为选择器,HTML片段或者为’body’,这些都是字符串形式,所以大方向上可以是分成下面这几种情况:

if(!selector)    //参数为空
{
    return this;
}
if(jQuery.isFunction(selector))       //参数为函数
{
    //调用jQuery的ready方法
    return jQuery.ready(selector);
}
if(typeof selector === 'string')       //参数为字符串
{
    //里面包含了选择器,HTML片段,'body'等情况
    return this;
}
if( selector.nodeType )    //参数为DOM元素或者DOM元素构成的数组
{
    return this;
}
if( selector.selector !== 'undefined' )      //参数为jQuery对象
{
    return this;
}
//当上面情况都没有return时,即其他情况
return jQuery.makeArray(selector, this);

整个init函数的大体逻辑就是这样,在这里我们看到了几个jQuery静态的方法,即直接挂载在jQuery 上面的方法,而不是jQuery对象上的方法。如
上面的jQuery.isFunction(selector);
jQuery.makeArray(selector, this);
这类方法算是jQuery的底层支持模块里的工具方法了。如下图:
这里写图片描述
图片来源:

jQuery底层支持模块-工具方法

可以利用源码查看工具来看工具方法的源码:地址

工具方法ifFunction()

对于isFunction这个方法:
这里写图片描述

代码很少,只有一行,但是又去调了另外一个工具方法type,由这一句就可以大概知道type的作用是判断参数的类型的,下面是type的源码:
这里写图片描述
这个代码也同样很短,但是它涵盖的内容却是极其庞大的,在jQuery中用到了很多技巧。

首先是&&,||逻辑。
我们平时用到的&& 、||,主要是if判断时,将多个条件的关系用&& 或者||来组合,但在jQuery中,却更多的是用他们来表达值。比如:

var s = num && num1;      //如果num存在,并为真,还要判断num1的值,s就为num1的值;如果num不存在或者为假,不需要判断num1,s 就是num的值;
var s1 = num2 || num3;    //同理:如果num2存在,并为真,不需要后面计算num3,s就为num2的值;如果num不存在或者为假,还要判断num3,s 就是num3的值;

这种的好处就是省去了if代码去判断,省了不少代码量,另一方面,应该也可以快速的执行。

另一个就是 jQuery中的钩子机制。
在type 这个方法中,引用了class2type这个数组,我们先来看看这个数组是什么样子。
class2type[ ‘[object Boolean]’ ] = ‘boolean’;
class2type[ ‘[object String]’ ] = ‘String’;
class2type[ ‘[object Number]’ ] = ‘Number’;
class2type[ ‘[object Function]’ ] = ‘function’;
class2type[ ‘[object Array]’ ] = ‘array’;
class2type[ ‘[object Date]’ ] = ‘date’;
class2type[ ‘[object RegExp]’ ] = ‘regexp’;
class2type[ ‘[object Object]’ ] = ‘object’;

为什么要这么做呢?如果不这么做,我们要去判断是哪一类,就需要if分支去判断,这样一步步去判断必然会导致比较慢。
而利用事先做的这个类似于表的对应关系,可以省去很多if-else操作。这就是jQuery中的钩子机制的一种,即表驱动方式。这只是钩子机制的一个小小的技巧,在别的地方还有很多其他的用法。

下来,判断类型主要有这么几种情况:
基本数据类型:null,undefined, boolean, string,number
引用数据类型:Object, Function
对于基本数据类型,typeof 就可以得到类型;
而对于Object类型, typeof并不能准确的判断出对象的具体类型,准确的方法是调用Object.prototype.toString.call()方法。
而对于Function类型,既可以用typeof操作得到,也可以调Object.toString()方法。源码如下:
这里先判断了null 和undefined类型,之后主要分成object、function类型和基本数据类型。

return null == a ? a+"" : typeof a == 'object' || typeof a == 'function' ? class2Type[toString.call(a)] || 'object' : typeof a;

工具方法makeArray

该方法主要是将伪数组对象转换成数组对象。

function (array, results) {
    var ret = results || [];    

    if (array != null) {   //array不为空、undefined时
        // The window, strings (and functions) also have 'length'
        // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
        var type = jQuery.type(array);     //得到array的类型

        if (array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow(array)) {    //如果array不为数组,也不是对象(伪数组)
            push.call(ret, array);    //调数组的push方法
        } else {
            jQuery.merge(ret, array);   //调jQuery的工具方法merge,该方法主要是将两个数组合并并保存到第一个数组里面
        }
    }

    return ret;
}

jQuery.merge方法

该方法主要是将两个数组进行合并并将结果保存到第一个里面。举两个例子:

var arr1 = ['12', 'sd', '34'];
var arr2 = ['ad', '12', 'df'];
$.merge(arr1, arr2);      //结果为["12", "sd", "34", "ad", "12", "df"]

这里写图片描述

var t = {
    0: 'li',
    1: 'li',
    length: 2
};
$.merge(t, arr1);     //

这里写图片描述

下面是merge函数的源码:

function (first, second) {
    var i = first.length,
        j = 0;

    if (typeof second.length === "number") {  //伪数组,对象,必须有length属性,并且length属性为Number类型的值
        for (var l = second.length; j < l; j++) {
            first[i++] = second[j];
        }

    } else {
        while (second[j] !== undefined) {
            first[i++] = second[j++];
        }
    }

    first.length = i;

    return first;
}

这里,就先总结这么多,感觉还是要好好理解才能深入下去,不过jQuery源码再怎么说也是原生JS,只要能将原生JS的基础理解和掌握,jQuery源码还是可以看明白的。另外一点,jQuery源码里面的很多思想都是值得学习的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值