声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢!

     打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象,所以狭隘的说呢,jQuery就是一个选择器,并这个基础上构建和运行查询过滤器!

  1. 工欲善其事,必先利其器,所以先从正则入手

我们来分解一个表达式

  1. // A simple way to check for HTML strings
  2. // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
  3. // Strict HTML recognition (#11290: must start with <)
  4. rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,

作者的解释呢很简单,一个简单的检测HTML字符串的表达式

分解:

1. 通过选择|分割二义,匹配^开头或者$结尾

  • ^(?:\s*(<[\w\W]+>)[^>]*
  • #([\w-]*))$

2. ^(?:\s*(<[\w\W]+>)[^>]*

  • (?:pattern) : 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用
  • \s* : 匹配任何空白字符,包括空格、制表符、换页符等等 零次或多次 等价于{0,}
  • (pattern) : 匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,使用 $0$9 属性
  • [\w\W]+ : 匹配于'[A-Za-z0-9_]'或[^A-Za-z0-9_]' 一次或多次, 等价{1,}
  • (<[wW]+>) :这个表示字符串里要包含用<>包含的字符,例如<p>,<div>等等都是符合要求的
  • [^>]* : 负值字符集合,字符串尾部是除了>的任意字符或者没有字符,零次或多次等价于{0,},

3. #([\w-]*))$

  • 匹配结尾带上#号的任意字符,包括下划线与-

4. 还要穿插一下exec方法

  • 如果执行exec方法的正则表达式没有分组(没有括号括起来的内容),那么如果有匹配,他将返回一个只有一个元素的数组,这个数组唯一的元素就是该正则表达式匹配的第一个串;如果没有匹配则返回null。
  • exec如果找到了匹配,而且包含分组的话,返回的数组将包含多个元素,第一个元素是找到的匹配,之后的元素依次为该匹配中的第一、第二...个分组(反向引用)

所以综合起来呢大概的意思就是:匹配HTML标记和ID表达式(<前面可以匹配任何空白字符,包括空格、制表符、换页符等等)


简单测试下:

  1. var str = ' <div id=top></div>';
  2. var match = rquickExpr.exec(str);
  3. console.log(match)
  4. //[" <div id=top></div>", "<div id=top></div>", undefined, index: 0, input: " <div id=top></div>"]
  1. var str = '[?\f\n\r\t\v]<div id=top></div>';
  1. var str = '#test';
  2. var match = rquickExpr.exec(str);
  3. console.log(match)
  4. //["#test", undefined, "test", index: 0, input: "#test"]

 


jQuery选择器接口

API

jQuery是总入口,选择器支持9种方式的处理

  1. 1.$(document)
  2. 2.$(‘<div>’)
  3. 3.$(‘div’)
  4. 4.$(‘#test’)
  5. 5.$(function(){})
  6. 6.$("input:radio", document.forms[0]);
  7. 7.$(‘input’, $(‘div’))
  8. 8.$()
  9. 9.$("<div>", {
  10. "class": "test",
  11. text: "Click me!",
  12. click: function(){ $(this).toggleClass("test"); }
  13. }).appendTo("body");
  14. 10$($(‘.test’))

jQuery这个选择器重构了几次后,现在逻辑结构相当的清晰了,一看大概就知道

不能不得说jQuery的反模式,非职责单一深受开发者喜欢,一个接口承载的职责越多内部处理就越复杂了

jQuery查询的的对象是dom元素,查询到目标元素后,如何存储?

  • 查询的到结果储存到jQuery对象内部,由于查询的dom可能是单一元素,也可能是合集
  • jQuery内部应该要定义一个合集数组,用于存在选择后的dom元素,
  • 当然啦,根据API,jQuery构建的不仅仅只是DOM元素,还有HTML字符串,Object,[] 等等…

 


本质上讲jQuery.fn.init构建的出来的对象,就是jQuery对象

  1. init: function( selector, context, rootjQuery ) {
  2. var match, elem;
  3.  
  4. // HANDLE: $(""), $(null), $(undefined), $(false)
  5. if ( !selector ) {
  6. return this;
  7. }
  8.  
  9. // Handle HTML strings
  10. if ( typeof selector === "string" ) {
  11. // HANDLE: $(DOMElement)
  12. } else if ( selector.nodeType ) {
  13. // HANDLE: $(function)
  14. // Shortcut for document ready
  15. } else if ( jQuery.isFunction( selector ) ) {
  16. return rootjQuery.ready( selector );
  17. }
  18.  
  19. if ( selector.selector !== undefined ) {
  20. this.selector = selector.selector;
  21. this.context = selector.context;
  22. }
  23.  
  24. return jQuery.makeArray( selector, this );
  25. },

源码缩进后的结构:

  • 处理"",null,undefined,false,返回this ,增加程序的健壮性
  • 处理字符串
  • 处理DOMElement,返回修改过后的this,给this添加了
  • 处理$(function(){})

 


匹配模式一:$("#id")

1. 进入字符串处理

  1. if ( typeof selector === "string" ) {

2. 发现不是 "<"开始,">"结尾 $('<p id="test">My <em>new</em> text</p>')这种的情况

   如果selector是html标签组成的话,直接match = [ null, selector, null ];

   而不用正则检查

  1. if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {

3. 否则的话需要match = rquickExpr.exec( selector )

  1. match = rquickExpr.exec( selector );

4. 匹配的html或确保没有上下文指定为# id

  1. if ( match && (match[1] || !context) ) {

5. match[1]存在,处理$(html) -> $(array),,也就是处理的是html方式

  1. if ( match[1] ) {

6. 处理ID

  1. elem = document.getElementById( match[2] );
  2.  
  3. // Check parentNode to catch when Blackberry 4.6 returns
  4. // nodes that are no longer in the document #6963
  5. if ( elem && elem.parentNode ) {
  6. // Inject the element directly into the jQuery object
  7. this.length = 1;
  8. this[0] = elem;
  9. }
  10.  
  11. this.context = document;
  12. this.selector = selector;
  13. return this;

至此本次检索完毕!

可以看到

this就是jQuery工厂化后返回的对象

  • this.length    
  • this[0] = elem
  • this.context = document;
  • this.selector = selector;

 


匹配模式二:<htmltag>

重复的地方跳过直接看处理接口

  1. if ( match && (match[1] || !context) ) {
  2. // HANDLE: $(html) -> $(array)
  3. if ( match[1] ) {
  4. context = context instanceof jQuery ? context[0] : context;
  5. // scripts is true for back-compat
  6. jQuery.merge( this, jQuery.parseHTML(
  7. match[1],
  8. context && context.nodeType ? context.ownerDocument || context : document,
  9. true
  10. ) );
  11. // HANDLE: $(html, props)
  12. if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
  13. for ( match in context ) {
  14. // Properties of context are called as methods if possible
  15. if ( jQuery.isFunction( this[ match ] ) ) {
  16. this[ match ]( context[ match ] );
  17. // ...and otherwise set as attributes
  18. } else {
  19. this.attr( match, context[ match ] );
  20. }
  21. }
  22. }
  23. return this;
  24. // HANDLE: $(#id)
  25. } else {

传入上下文:

  1. context && context.nodeType ? context.ownerDocument || context : document

ownerDocument和 documentElement的区别

  • ownerDocument是Node对象的一个属性,返回的是某个元素的根节点文档对象:即document对象
  • documentElement是Document对象的属性,返回的是文档根节点
  • 对于HTML文档来说,documentElement是<html>标签对应的Element对象,ownerDocument是document对象

具体请看API手册

jQuery.merge( first, second ) 合并两个数组内容到第一个数组。

 

jQuery.parseHTML

使用原生的DOM元素的创建函数将字符串转换为一组DOM元素,然后,可以插入到文档中。

  1. str = "hello, <b>my name is</b> jQuery.",
  2. html = $.parseHTML( str ),

源码:

  1. parseHTML: function( data, context, keepScripts ) {
  2. if ( !data || typeof data !== "string" ) {
  3. return null;
  4. }
  5. if ( typeof context === "boolean" ) {
  6. keepScripts = context;
  7. context = false;
  8. }
  9. context = context || document;
  10. var parsed = rsingleTag.exec( data ),
  11. scripts = !keepScripts && [];
  12. // Single tag
  13. if ( parsed ) {
  14. return [ context.createElement( parsed[1] ) ];
  15. }
  16. parsed = jQuery.buildFragment( [ data ], context, scripts );
  17. if ( scripts ) {
  18. jQuery( scripts ).remove();
  19. }
  20. return jQuery.merge( [], parsed.childNodes );
  21. },

匹配一个独立的标签

  1. rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
  • ^<(\w+)\s*\/?>  : 以<开头,至少跟着一个字符和任意个空白字符,之后出现0或1次/>
  • (?:<\/\1>|)$        : 可以匹配<、一个/或者空白并以之为结尾

      这样如果没有任何属性和子节点的字符串(比如'<html></html>'或者'<div></div>'这样)会通过正则的匹配,当通过正则的匹配后则会通过传入的上下文直接创建一个节点:

只是单一的标签:

  1. if ( parsed ) {
  2. return [ context.createElement( parsed[1] ) ];
  3. }

而未通过节点的字符串,则通过创建一个div节点,将字符串置入div的innerHTML:

  1. parsed = jQuery.buildFragment( [ data ], context, scripts );

它会把传入的复杂的html转为文档碎片并且存储在jQuery.fragments这个对象里。这里要提一下,document.createDocumentFragment()是相当好用的,可以减少对dom的操作.

创建一个文档碎片DocumentFragment

  • 如果要插入多个DOM元素,可以先将这些DOM元素插入一个文档碎片,然后将文档碎片插入文档中,这时插入的不是文档碎片,而是它的子孙节点;相比于挨个插入DOM元素,使用文档碎片可以获得2-3倍的性能提升;
  • 如果将重复的HTML代码转换为DOM元素,可以将转换后的DOM元素缓存起来,下次(实际是第3次)转换同样的HTML代码时,可以直接缓存的DOM元素克隆返

 

当一个HTML比一个没有属性的简单标签复杂的时候,实际上,创建元素的处理是利用了浏览器的innerHTML 机制。

  1. 1 tmp = tmp || fragment.appendChild( context.createElement("div") );
  2. 2
  3. 3 // Deserialize a standard representation
  4. 4 tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase();
  5. 5 wrap = wrapMap[ tag ] || wrapMap._default;
  6. 6 tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
  • 特别说明,jQuery创建一个新的<div>元素,并且设置innerHTML属性为传入的HTML代码片段。当参数是一个单标签,就像 $('<img />') or $('<a></a>'),jQuery将使用javasrcipt原生的 createElement()函数创建这个元素。
  • 当传入一个复杂的html,一些浏览器可能不会产生一个完全复制HTML源代码所提供的DOM。正如前面提到的,jQuery使用的浏览器.innerHTML属性来解析传递的HTML并将其插入到当前文档中。在此过程中,一些浏览器过滤掉某些元素,如<html>, <title>, 或 <head>的元素。其结果是,被插入元素可能不是传入的原始的字符串。
  • 不过,这些被过滤掉的标签有限的。有些浏览器可能不完全复制所提供的HTML源代码生成DOM。例如,Internet Explorer的版本8之前转换所有链接的href属性为绝对URL路径,和Internet Explorer第9版之前,不增加一个单独的兼容层的情况下,将无法正确处理HTML5元素。
  • 为了确保跨平台的兼容性,代码片段必须是良好的。标签可以包含其他元素,但需要搭配的结束标记

 

 如果第一个参数(HTML字符串)为一个空的单标签,且第二个参数context为一个非空纯对象

  1. var jqHTML = $('<div></div>', { class: 'css-class', data-name: 'data-val' });
  2.  
  3. console.log(jqHTML.attr['class']); //css-class
  4. console.log(jqHTML.attr['data-name']); //data-val

 

匹配模式三:$(.className)

如果第一个参数是一个.className,jQuery对象中拥有class名为className的标签元素,并增加一个属性值为参数字符串、document的selector、context属性

  1. return jQuery(document).find(className);

匹配模式四:$(.className, context)

如果第一个参数是.className,第二个参数是一个上下文对象(可以是.className(等同于处理$(.className .className)),jQuery对象或dom节点), 
jQuery对象包含第二个参数上下文对象中拥有class名为className的后代节点元素,并增加一个context和selector属性

  1. return jQuery(context).find(className);

 

匹配模式五:$(jQuery对象)

如果第一个参数是jQuery对象,上面已经分析过如果在查询dom时,参数是一个#加元素id,返回的jQuery对象会增加一个属性值为参数字符串、document的selector、context属性

  1. var jq = $('#container');
  2. console.log(jq.selector); // #container
  3. console.log(jq.context); // document

那么当出现$($('#container'))该如何处理呢?同样的,返回的jQuery对象同情况

  1. var jq2 = $($('#container'));
  2. console.log(jq2.selector); // #container
  3. console.log(jq2.context); // document

 

等等..................

 


jQuery 构造器

     由此可见,从本质上来说,构建的jQuery对象,其实不仅仅只是dom,还有很多附加的元素,用数组的方式存储,当然各种组合有不一样,但是存储的方式是一样的

总的来说分2大类:

  • 单个DOM元素,如$(ID),直接把DOM元素作数组传递给this对象
  • 多个DOM元素,集合形式,可以通过CSS选择器匹配是有的DOM元素,过滤操作,构建数据结构

CSS选择器是通过jQuery.find(selector)函数完成的,通过它可以分析选择器字符串,并在DOM文档树中查找符合语法的元素集合

选择器这章有点乱,东西太多了,不能一一陈列 , 后期在慢慢整理

jQuery 2.0.3 源码分析core - 选择器的更多相关文章

  1. jQuery 2.0.3 源码分析core - 整体架构

    拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...

  2. jQuery 2.0.3 源码分析Sizzle引擎解析原理

    jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...

  3. jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

    Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...

  4. jQuery 2.0.3 源码分析 Deferred概念

    JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...

  5. jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

    事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...

  6. jQuery 2.0.3 源码分析 事件体系结构

    那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...

  7. jQuery 2.0.3 源码分析 Deferrred概念

    转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...

  8. jQuery 2.0.3 源码分析Sizzle引擎 - 词法解析

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 浏览器从下载文档到显示页面的过程是个复杂的过程,这里包含了重绘和重排.各家浏览器引擎的工作原理略有差别,但也有一定规则. 简 ...

  9. jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...

随机推荐

  1. Windows中使用TortoiseGit提交项目到GitLab配置

    下文来给各位介绍Windows中使用TortoiseGit提交项目到GitLab配置过程,下在全部图片希望对各位带来方便面. Gitlab默认的配置推荐使用shell命令行与server端进行交互,作 ...

  2. [Leetcode] Repeated DNA Sequences

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  3. 使用sublime text 开发node.js

    http://blog.csdn.net/jwkfreedom/article/details/8450005 本机环境: windows7 64位 1. 下载安装sublime text, 不用注册 ...

  4. iOS推送原理

    1.首先app会和apns建立长连接,会发送 udid和 bundle id给apns 2.apns会返回给app一个device token 3.用会把device token 发给自己的服务器 4 ...

  5. java反射学习之一反射机制概述

    一.反射机制背景概述 1.反射(reflection)是java被视为动态语言的一个关键性质 2.反射机制指的是程序在运行时能获取任何类的内部所有信息 二.反射机制实现功能概述 1.只要给定类的全名, ...

  6. STM32之PWM君

    PWM..英语好的人估计又知道这三个大写字母代表哪三个英语单词了.小弟不才,就说中文意思好了:脉冲宽度调制,玩过飞思卡尔的人估计对PWM非常的不陌生吧.电机驱动需要PWM,控制舵机的转向需要PWM,总 ...

  7. linux配置ftp高级权限

    建一个用于管理的ftp高级账号,ftproot,定义它的目录,也就是我们存放项目的地址,所属组www, useradd -d /home/www -g www ftproot www里存放很多项目,我 ...

  8. js正则获取url所带参数值

    在js字符串对象原型中添加这个获取链接参数值方法,getAddrVal(): String.prototype.getAddrVal = String.prototype.getAddrVal||fu ...

  9. git学习笔记一

    一.概念理解 1.理解工作区和暂存区以及版本库 工作区我理解就是我们创建的程序所在的文件夹,比如test文件夹.其中有个.git文件,这个就是版本库,其中版本库中有个区域叫暂存区或叫索引. 截自廖雪峰 ...

  10. nodejs学习之events

    在node里许多对象都发出事件:一个net.Server对象每次一个连接到来,都发出一个事件,一个fs.readStream对象在文件打开时放出一个事件.所有能放出事件的对象都是event.Event ...