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函数的分析点击这里
- jQuery源码阅读(五)---init函数
- jQuery源码阅读(七)--init()遗留部分buildFragment()函数
- jQuery-源码阅读,init()方法
- jQuery源码阅读(二)---初识init方法
- 用户进程init(顺序阅读五)
- jQuery源码分析 (init)
- jQuery源码研究分析学习笔记-jQuery.fn.init()(五)
- jQuery 源码剖析-3 init 函数分析
- jQuery 源码剖析-4 init 函数分析
- jQuery源码之init函数的分析
- WINVNC源码阅读(五)
- jQuery源码剖析--jQuery入口函数-init实现
- jQuery源码阅读心得(一) 自调用匿名函数
- jQuery源码阅读(九)---ready函数理解
- jQuery-源码阅读,使用init()而不用普通原型模式的原因
- Argo源码阅读(五):Convention
- Gaea源码阅读(五):C客户端
- ZooKeeper源码阅读(五):Leader选举
- [Leetcode] 277. Find the Celebrity 解题报告
- ES6 -- Iterator 的基本用法
- PostgreSQL错误解决:ERROR: current transaction is aborted, commands ignored until end of transaction bloc
- nginx的配置、虚拟主机、负载均衡和反向代理(3)
- *+=* 和 *=*+*的区别
- jQuery源码阅读(五)---init函数
- CNTK API文档翻译(7)——对MNIST数据使用卷积神经网络
- netty5 用户指南
- 验证二叉查找树-LintCode
- 递归优化
- h.265继承了h.264哪些编码技术,抛弃了h.264哪些编码技术,又引入了哪些新的编码技术?
- 关于自定义View的Paint、Canvas和PorterDuffXfermode的用法
- Python使用struct处理二进制
- display:inline display:block