解密jQuery内核 Sizzle引擎筛选器 - 位置伪类(一)
本章开始分析过滤器,根据API的顺序来
主要涉及的知识点
jQuery的组成
pushStack方法的作用
sizzle伪类选择器
首页我们知道jQuery对象是一个数组对象
内部结构
jQuery的选择最终还是依靠的DOM提供的接口,jQuery只是最了最佳的方式最快的匹配到合适的位置
构建一个基础的jQuery对象有:
元素合集
元素数量
上下文
通过pushStack()方法构建的prevObject的引用储存,这个在DOM操作的时候特别有用
选择器
具体文章前面有分析,pushStack有什么用处,sizzler如何最佳匹配,这里就不详说了,接下来看具体的操作
.eq( index )
如果一个jQuery对象表示一个DOM元素的集合,.eq()
方法从集合的一个元素中构造新的jQuery对象。所提供的索引标识这个集合中的元素的位置。
根据jQuery的结构,这个很好处理了,返回选择器中的元素,数组索引从0开始的
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 );
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
},
这里有个细节,我们取DOM元素是索引,取出来的只是一个节点了,那么还要实现链式操作,我们都知道返回this就能链式,
但是jQuery的每一次操作都是可以回溯的,包括节点的遍历查找,所以这时候返回的当前的this是不行的,这样就需要构建一个新的上下文对象
li.eq(2).css('background-color', 'red');
所以取出来的DOM节点还要通过pushStack方法给包装一次
.first()
first: function() {
return this.eq( );
},
.last()
last: function() {
return this.eq( - );
},
可见first,last方法都是一路货,通过eq取索引罢了
.slice()
如果提供的jQuery代表了一组DOM元素,.slice()
方法从匹配元素的子集中构造一个新的jQuery对象。
所提供的start
索引标识的设置一个集合中的元素的位置;如果end
被省略,这个元素之后的所有元素将包含在结果中
slice: function() {
return this.pushStack( core_slice.apply( this, arguments ) );
},
其实就是
[].slice.apply(this,arguments )
这里的this是jq对象,根据参数转成数组子集
.map( callback(index, domElement) )
描述: 通过一个函数匹配当前集合中的每个元素,产生一个包含新的jQuery对象。
map有点类似each的赶脚,本质上还是有区别的
首先:
在设计上each是不可改变的迭代器,each方法就相当于js中的for循环,返回 'false' 将停止循环 (就像在普通的循环中使用 'break')
map的方法可以作为一个迭代器,但实际是为了操纵提供的数组并返回一个新数组。
map将一组元素转换成其他数组(不论是否是元素数组),你可以用这个函数来通过新规则(如过滤掉数字)来建立一个新列表,
不论是值、属性还是CSS样式,或者其他特别形式。这都可以用'$.map()'来方便的建立。
可见map除了迭代的功能还要返回一新的对象,所以潜在的有内存消耗
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
jQuery.map: function( elems, callback, arg ) {
var value,
i = ,
length = elems.length,
isArray = isArraylike( elems ),
ret = []; // Go through the array, translating each of the items to their
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg ); if ( value != null ) {
ret[ ret.length ] = value;
}
} // Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg ); if ( value != null ) {
ret[ ret.length ] = value;
}
}
} // Flatten any nested arrays
return core_concat.apply( [], ret );
},
可见除了执行回调,还要收集返回值
value = callback( elems[ i ], i, arg ); if ( value != null ) {
ret[ ret.length ] = value;
}
通过jQuery.map会返回一个新的迭代对象,然后又让pushStack方法包装一个新的堆上的元素集合(jQuery对象)
.filter()
选择器是个比较复杂的东东了,这里又要涉及到sizzle的处理了,顺便一起回顾以往的知识点,加以巩固
讲过滤器,就里不得不提一下.find(),虽然2者都是一个效果,但是处理有本质的区别
filter与find
jQuery官方的API这样说明filter和find函数:
filter(selector):
Description: Reduce the set of matched elements to those that match the selector or pass the function’s test. find(selector):
Description: Get the descendants of each element in the current set of matched elements, filtered by a selector.
find()会在当前指定元素中查找符合条件的子元素,是对它的子集操作,
filter()则是在当前指定的元素集合中查找符合条件的元素,是对自身集合元素进行筛选。
显而易见find()是对它的子集操作,filter()对自身集合元素筛选
find以后再说,先看看filter的实现
filter实例
官方给出的直接搬过来
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
</ul>
li.filter(':even').css('background-color', 'red');
filter源码
第一步
可见winnow一定是返回一个数据对象,在通过pushStack包装
filter: function( selector ) {
return this.pushStack( winnow(this, selector || [], false) );
},
第二步
jQuery.extend({
filter: function( expr, elems, not ) {
var elem = elems[ 0 ]; if ( not ) {
expr = ":not(" + expr + ")";
} return elems.length === 1 && elem.nodeType === 1 ?
jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
return elem.nodeType === 1;
}));
},
filter是针对元素节点本身操作的,所以还要顾虑一下保证合集中都是elem.nodeType === 1的节点类型才行
第三步
方法最终还是靠sizzle去解析,传递一个符合解析的实参
传递了选择器与种子合集了,这里具体可以参考之前的sizzle系列文章,走的流程依然一样
通过词法分析器,解析出词法关系
Token:{
value:'匹配到的字符串',
type:'对应的Token类型',
matches:'正则匹配到的一个结构'
}
几种Token : TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME,但是这里的type对应是一种伪类PSEUDO,这是在之前sizzle里面没有提到的
sizzle伪类
其实之前的文章分析了tokenize方法,把选择语句分解成分词
当然看这里需要结合之前的sizzle系列的篇幅了,比较复杂
Sizzle巧妙的就是它没有直接将拿到的“分词”结果与Expr中的方法逐个匹配逐个执行,而是先根据规则组合出一个大的匹配方法,最后一步执行
编译函数机制中最核心的一段
Sizzle.compile:
i = group.length;
while ( i-- ) {
cached = matcherFromTokens( group[i] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
}
// Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
1 :matcherFromTokens方法,通过tokens生成匹配程序,它充当了selector“分词”与Expr中定义的匹配方法的串联与纽带的作用,可以说选择符的各种排列组合都是能适应的了
2: matcherFromGroupMatchers方法,通过返回curry的superMatcher方法执行
伪类如何生成最终的匹配器
matcherFromTokens源码核心部分
for ( ; i < len; i++ ) {
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
} else {
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
可见根据分词器类型type的不同,会适配2组不同的处理方案
第一种是位置词素,之前就讲过,主要看下面一种
用Expr.filter的工厂方法来生成匹配器
每条选择器规则最小的几个单元可以划分为:ATTR | CHILD | CLASS | ID | PSEUDO | TAG
位置伪元素
每个子规则都有对应的匹配器,同样道理,位置伪类也有特殊的匹配器,它是由setMatcher工厂生成。
为了区分其他规则跟位置伪类,需要对位置伪类的过滤器匹配器等打个标记。
Sizzle源码里边用到的打标记方法
function markFunction( fn ) {
fn[ expando ] = true;
return fn;
}
位置伪元素共同的特点被createPositionalPseudo给curry一次了
function createPositionalPseudo(fn) {
return markFunction(function(argument) {
argument = +argument;
return markFunction(function(seed, matches) {
var j,
matchIndexes = fn([], seed.length, argument),
i = matchIndexes.length; // Match elements found at the specified indexes
while (i--) {
if (seed[(j = matchIndexes[i])]) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
}
curry化 :它是一种通过把多个参数填充到函数体中,实现将函数转换成一个新的经过简化的(使之接受的参数更少)函数技术
当然也会形成闭包了,保存私有值在作用
所以这里很好理解,返回的函数其实最终是
function(argument) {
argument = +argument;
return markFunction(function(seed, matches) {
var j,
matchIndexes = fn([], seed.length, argument),
i = matchIndexes.length; // Match elements found at the specified indexes
while (i--) {
if (seed[(j = matchIndexes[i])]) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
所以总结,针对(位置型的)伪筛选:first/:last/:eq/:even/:odd/:It/:gt,都是做了单独的处理,
然后过滤器还有别的比如:not,:has,:contains 等等,其实基础的流程处理都差不多,只是针对不通的情况分别hack
在初始化的时候就:
1 返回新的curry函数
2 打上标记markFunction
回到主线方法:
Expr.filter.PSEUDO
"PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
}
The user may use createPseudo to indicate that
其实显而易见了,fn执行的正是上面
"even": createPositionalPseudo(function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
执行继续嵌套形成的curry 函数了,在返回一个curry方法,给新方法继续打上一个标记
function( seed, matches ) {
var j,
matchIndexes = fn( [], seed.length, argument ),
i = matchIndexes.length; // Match elements found at the specified indexes
while ( i-- ) {
if ( seed[ (j = matchIndexes[i]) ] ) {
seed[j] = !(matches[j] = seed[j]);
}
}
}
所以最终的匹配器方法贼恶心的,嵌套了4,5层作用域
matcher :
为什么要写这么复杂?因为可以合并不同的参数传递
其实sizzle就是核心处理机制是不变的,只是针对不同的分支做了不同的处理方式,当然写东西整合在一起就显得尤为的复杂了
下一章在深入位置伪类特有的 setMatcher 匹配器工厂
解密jQuery内核 Sizzle引擎筛选器 - 位置伪类(一)的更多相关文章
- jQuery1.11源码分析(8)-----jQuery调用Sizzle引擎的相关API
之所以把这部分放在这里,是因为这里用到了一些基本API,前一篇介绍过后才能使用. //jQuery通过find方法调用Sizzle引擎 //jQuery通过find方法调用Sizzle引擎 jQuer ...
- jQuery查找标签--选择器,筛选器,模态对话框, 左侧菜单栏
查找标签 选择器: 基本选择器(同css) id选择器 $("#id") 标签选择器 $('tagName') class选择器 $(".className") ...
- jquery选择器之基本筛选器
HTML示例代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- 解密jQuery内核 DOM操作方法(二)html,text,val
回顾下几组DOM插入有关的方法 innerHTML 设置或获取位于对象起始和结束标签内的 HTML outerHTML 设置或获取对象及其内容的 HTML 形式 看图对照区别 innerText 设置 ...
- 解密jQuery内核 DOM操作的核心函数domManip
domManip是什么 dom即Dom元素,Manip是Manipulate的缩写,连在一起就是Dom操作的意思. .domManip()是jQuery DOM操作的核心函数 对封装的节点操作做了参数 ...
- 解密jQuery内核 DOM操作的核心buildFragment
文档碎片是什么 http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3 DocumentFragment is a & ...
- 解密jQuery内核 DOM操作
jQuery针对DOM操作的插入的方法有大概10种 append.prepend.before.after.replaceWith appendTo.prependTo.insertBefore.in ...
- 解密jQuery内核 样式操作
基础回顾 jQuery里节点样式读取以及设置都是通过.css()这个方法来实现的,本章通一下分解探究下jquery里这部分代码的实现 那么jQuery要处理样式的哪些问题? 先简单回顾下样式操作会遇到 ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...
随机推荐
- 【SAP BO】无法识别账户信息:无法访问CMS。计算机上的CMS由于某个严重错误而停止。(FWM 20031)
1.系统环境 OS:Windows Server 2008 R2 RDBMS:Oracle 11g R2(Server.Client同时存在) BI:SAP Business Objects 4.2 ...
- Mysql在windows系统下的配置
因为项目测试需求,不得不在本地装一个Mysql才能更方便地进行程序调试,整个过程虽然简单,但也遇到了一点麻烦,所以贴出来当是备忘. 这里采用MySQL Community Server 5.7.12 ...
- python基础之初始python
初始python之基础一 一.Python 介绍 1.python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发 ...
- Ubuntu13.10下安装HADOOP
2013-03-05 09:04 995人阅读 评论(0) 收藏 举报 运行这个脚本: #/bin/sh sudo add-apt-repository ppa:webupd8team/java su ...
- js中substr,substring,indexOf,lastIndexOf的用法小结
第一组:str.substr(start,length) 和 str.substring(start,end) 定义: str.substr(start,length) substr(start,le ...
- 基于java平台的常用资源整理
这里整理了基于java平台的常用资源 翻译 from :akullpp | awesome-java 大家一起学习,共同进步. 如果大家觉得有用,就mark一下,赞一下,或评论一下,让更多的人知道.t ...
- #研发解决方案#分布式并行计算调度和管理系统Summoner
郑昀 创建于2015/11/10 最后更新于2015/11/12 关键词:佣金计算.定时任务.数据抽取.数据清洗.数据计算.Java.Redis.MySQL.Zookeeper.azkaban2.oo ...
- python RecursionError: maximum recursion depth exceeded in comparison错误
处理快速排序,递归深度可能非常大,而系统默认的深度可能没有这么大 需要设置最大递归深度 import sys sys.setrecursionlimit(100000) # 这个值的大小取决你自己,最 ...
- dubbo 常见错误
1. Caused by: java.lang.reflect.MalformedParameterizedTypeException 或 Caused by: java.lang.NoSuchMet ...
- (转)win7 64 安装mysql-python:_mysql.c(42) : fatal error C1083: Cannot open include file: 'config-win.h': No such file or directory
原文地址:http://www.cnblogs.com/fnng/p/4115607.html 作者:虫师 今天想在在win7 64位环境下使用python 操作mysql 在安装MySQL-pyth ...