jQuery源码阅读(五)---init函数

来源:互联网 发布:在线字体识别软件 编辑:程序博客网 时间:2024/05/17 09:03

在jQuery源码阅读(二)里面,大体介绍了jQuery创建对象时如何创建的,并且对init函数里面的函数进行了梳理,但具体的逻辑还是没有彻底搞清楚,这一篇博客主要是对init函数的一个详细梳理。

在理解init函数内部逻辑之前,我们先想想用$()和jQuery()来创建jQuery对象时,参数有哪些可能的情况?或者想想我们平时会用到哪些?这在jQuery源码阅读(二)里面也有说到,我们再来回顾一下:

$(),$('')     //这种情况基本很少用到,但还是需要考虑

返回结果都是一个空对象:
这里写图片描述

$('<li></li>');$('<img />');$('<ul><li>苹果</li></ul>');

返回结果如下:
这里写图片描述
可以看到每个返回结果都是类似与数组的对象,为什么说类似于数组?因为他们也是按照0,1,…下标来保存元素的,并且还有length属性。
对于最后一个$('<ul><li>苹果</li></ul>');比较复杂的情况,返回的同样是一个长度为1的类数组(对象),它里面的元素是ul,那么li元素跑到哪里去了?将ul元素打开,可以找到在childNode下面有li元素。
这里写图片描述
那么这个在jQuery里面是如何实现的?我们后面理源码的时候再分析。

<body>    <input id="btn" type="button" value="点击按钮" />    <input type="text" value="输入" />    <input type="text" id="128738-" />    <div class='.box'>一个小盒子</div>    <ul>        <li>            <a href="#" class="first">链接列表一</a>        </li>        <li>            <a href="#">链接列表二</a>        </li>    </ul></body>$('#128738-');$('.box')$('ul li .first')

这里写图片描述

$(function(){    //当页面处于ready状态时,触发函数;    //即当页面元素、需要的外部样式表以及脚本加载完成时触发,而不必等到需要的所有图片加载出来。})

还有一种情况:

$(document.getElementById('128738-'));     //这里需要说明一下,CSS选择器命名规则是不允许数字开头和纯数字形式的,但是在原生JS或者jQuery获取选择器时,是可以选到对应选择器所标注的元素的。

上述方法可以看作是将原生DOM节点转换成jQuery对象。
这里写图片描述

根据jQuery源码阅读(二)所讲,除了上面常用的一些用法,有效参数还包括jQuery对象和Object对象等情况,但是这两种并不常用。

经过上面所有参数情况的分析,这才理解了之前看的下面这部分源码:

//之前始终不明白,为什么jQuery对象要有length对象?为什么不管init函数里面走哪个分支,最后都对this.selector和this.length赋值了// Start with an empty selectorselector: "",// The current version of jQuery being usedjquery: "1.7.2",// The default length of a jQuery object is 0length: 0,

现在明白了,就是jQuery就想利用数组的一些属性方法来便于操作,但init这个是创建对象的方法,也就是说返回的肯定是个对象,因此为了能用数组的属性和方法,将其改造成类似于数组的对象就好了。

下来我先搭一个init()函数的源码框架,便于大家看清楚:

init: function(selector, context, rootjQuery){    //情况一: 输入参数为null,undefined或者""    if(! selector)    {        return this;    }    //输入参数为DOM元素(通过nodeType来判断是否为DDOM元素)    if( selector.nodeType)    {        this.context = this[0] = selector;        this.length = 1;        return this;    }    var match;         if( typeof selector === 'string')    {        //如果是标签          //$('<div></div>');   $('<div>dekwpoioewueoi</div>');  $('<ul><li>列表复杂标签</li></ul>')        if( selector.charAt(0) === '<' && selector.charAt(selector.length - 1) === '>' && selector.length >= 3) {            match = [null, selector, null];        }        else //不是完整标签;             //$('ul');   $('#id')   $('.class'); $('ul li.class');    $('<div>jdioeusroiuewo')        {            match = quickExpr.exec(selector);             //如果是标签 $('<div>jdioeusroiuewo'), match = ['<div>jdioeusroiuewo', 'div', undefined];            //如果是id选择器$('#id') , match = ['#id', undefined, 'id'];            //如果是其他情况,match = null;        }        //上面得到了match数组,不同情况对应不同的值;下来根据不同结果再分        if(match && (match[1] || !context))    //所有match为真的情况        {            //是标签              //$('<div></div>');    $('<div>dekwpoioewueoi</div>');  $('<ul><li>列表复杂标签</li></ul>')            //$('<div>jdioeusroiuewo')    match = ['<div>jdioeusroiuewo', 'div', undefined];            //id选择器$('#id')  match = ['#id', undefined, 'id'];            if(match[1])  //标签、不完整标签、以及复杂标签            {                //jQuery 对象中context的概念,表示当前上下文,一般情况下都是document,如果有iFrame的话可能是iFrame里面的contentWindow.document                //所以context还是有必要的                context = context instanceof jQuery ? context[0] : context;            } else {         //id选择器            }        } else {   //这几种情况$('ul');   $('.class');  $('ul li.class');         }    }    //输入参数为函数,只有一种用法:$(function(){})相当于$.ready(function(){})    if( jQuery.isFunction(selector))    {        //调jQuery.ready()    }    if(selector.selector !== undefined)    {        //返回jQuery对象的Copy        this.selector = selector.selector;        this.context = selector.context;        return this;    }    //其他情况    return jQuery.makeArray(this, selector);}

上面这个框架的逻辑与jQuery源码阅读(二)里面讲的是一致的,这里将参数selector为字符串的情况再细分了一下,下来我们主要分析selector为字符串的情况。
selector为字符串主要有这么几种情况:

$('#id')$('.class')$('li')  $('ul li .first')$('<li></li>')$('<li>苹果</li>')$('<ul><li></li><ul>')$('<img />')$('<img src="#" />')

大致可以分为上面几类。

根据上面的源码框架,将其分为一下两大类别:
第一种:标签

if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {    match = [ null, selector, null ];    //后面再说match的作用}
$('<li></li>')     //match = [null, '<li></li>', null]$('<li>苹果</li>')  //match = [null,  '<li>苹果</li>', null]$('<ul><li></li><ul>')  //match = [null,  '<ul><li></li><ul>', null]$('<img />')    //match = [null,  '<img />', null]$('<img src="#" />')  //match = [null,  '<img src="#" />', null]

第二种:非标签或非完整标签

{    match = quickExpr.exec(selector); }

这里注意下面各个情况得到的match,关于正则表达式的匹配我也在上一篇博客 jQuery源码阅读之正则表达式作了说明

$('#id')   //match = ['#id', undefined, 'id']$('<li>abcdefg')  //match = ['<li>abcdefg', '<li>', undefined]$('.class')           //match = null$('li')  $('ul li .first')   ////match = null

下来就是根据match的值来分情况处理:

if(match && (match[1] || !context)){    //match[1]为真是包含标签的情况,不管是完整标签,还是不完整标签,或者是复杂标签    !context为真是selector为ID的情况,因为ID 选择是上下文的    if(match[1]) {  //selector为标签的情况        context = context instanceof jQuery ? context[0] : context;        ret = rsingleTag.exec(selector);   //匹配单标签        //包括$('<li></li>'),$('<li>'), 也包括$('<img />')        if(ret)        {            if(jQuery.isPlainObject(context))            {                //为什么会有这种情况呢?因为在jQuery中允许这种用法:$('<li></li>', { title: 'Hello', html: 'Hellen'}),后面加测试来验证                selector = [ document.createElement( ret[1] ) ];                //去给DOM元素添加属性                jQuery.fn.attr.call( selector, context, true );                 }            else            {                //如果只是一个简单的标签,直接掉原生的createElement就可以了                selector = [ doc.createElement( ret[1] ) ];            }        } else  //ret为空,$('<li>apple</li>'); $('<img src="#" />'); $('<ul><li></li></ul>')这几种类似情况        {            //带内容的标签,复杂标签的情况,调jQuery.buildFragment方法,这个后面再看,看了会再更新回来            ret = jQuery.buildFragment( [ match[1] ], [ doc ] )( [ match[1] ], [ doc ] );            selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;        }        return jQuery.merge( this, selector );    }     else  //selector为ID的情况    {        //调原生JS  getElementById方法        elem = document.getElementById(match[2]);        //判断元素找到?        if ( elem && elem.parentNode ) {         //(需考虑浏览器兼容问题,在IE和Opera浏览器中,调document.geElementById("id")会选中name属性为id的元素)             if ( elem.id !== match[2] ) {                //调Sizzle模块中的find方法                return rootjQuery.find( selector );            }            this.length = 1;            this[0] = elem;        }        this.context = document;        this.selector = selector;        return this;    } else if( !context || context.jquery ){  //如果为其他选择器或复杂选择器        //无上下文时,调$(document).find()        //若有上下文,判断上下文是否为jQuery对象,如果是,则调context.find()方法        return ( context || rootjQuery ).find( selector );    } else //如果context不为jQuery对象,则先将其转换成jQuery对象再调find()方法。    {        return this.constructor( context ).find( selector );    }}

到此,selector为字符串的情况就分析结束了,可以看到这整个就是Init()函数的大头,并且也是我们平时常用的情况。所以还是有必要缕清楚的。

上面缕的过程中,主要涉及到两个jQuery复杂点的静态函数jQuery.find()和jQuery.buildFragment()方法,这里由于篇幅已经比较长了,所以决定后面再理。相信看到这里的已经很不容易了,希望大家一起努力,一起进步!

PS: 关于jQuery.buildFragment函数的分析点击这里

阅读全文
0 0
原创粉丝点击