jquery transform怎么使用
发布网友
发布时间:2022-04-06 13:02
我来回答
共1个回答
热心网友
时间:2022-04-06 14:31
jQuery.prototype中定义了许多非常有趣的方法,同样对比Spark RDD的逻辑可以将其一部分划分为几个大的类型:transform类型及与之相关的方法集、action类型及与之相关的方法集,本篇将着重分析Transform类型的方法。这两大类方法是针对"已有选中原生客户端对象集合"的jQuery对象而言的,"怎样在文档对象中选中原生客户端对象集"可以看做是构造RDD的一大类方法,这个主题(Sizzle)几乎是jQuery最核心的部分,将在后续文章详细阐述。
首先看看jQuery原型对象的each方法,这个方法单独提前分析是因为它与其他的transform类型的方法不太一致。
// Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); },
前一篇文章已经详细分析过jQuery类型静态工具方法jQuery.each的原理和实现,那么这里就很好理解这个原型对象的each方法了。 第一个参数this,在原型对象中关键字this可以看做是jQuery实例对象的引用,既然jQuery对象是Array-Like Object,那么this传入jQuery.each就被当做数组处理了(jQuery对象几乎总是被当做一个Array实例处理的)。 第二个参数callback已经在jQuery.each中分析过,约束了其参数类型和顺序,参见jQuery.each,第一个参数是index,第二个参数是jQuery选中客户端对象集合中的元素。 特别需要注意的是jQuery.each返回的对象就是第一个参数对象,因此这里返回的就是传入的this--当前jQuery实例对象,但是其选中集合中的每个原生对象元素都可能被回调函数callback处理过。each方法与其他transform实例方法不同的地方就在于它没有在jQuery对象stack上新产生一个jQuery对象,接下来的几个transform方法都产生新实例对象了。
前面的each方法与这里的map方法都类似于Spark的map操作,区别是each方法不生成新jQuery对象,map方法生成一个新jQuery对象。
map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, 这里出现一个新实例方法--pushStack,先不用去关注其具体实现,首先看其参数--是一个由静态方法jQuery.map处理后返回的数组对象。其次,关注下回调函数callback,由于传递给jQuery.map的第二个参数是一个匿名回调函数,这个匿名回调函数的参数类型和顺序已经被静态方法jQuery.map约束了,但是其内部调用[实例方法map的回调函数参数]callback的时候把参数顺序换了一下,所以jQuery实例方法map的回调函数与jQuery静态类方法map的回调函数的参数顺序是相反的。其次关于为什么callback中this关键字表示的是原生客户端对象,在jQuery.each的解析部分已解释过,这里类似,匿名函数的第一个参数elem已经是原生客户端对象了,借用callback的时候作为第一个参数--callback.call( elem, i, elem ),当然callback中的this就指向elem了。 再来看看pushStack的实现,这也是jQuery原型对象的实例方法并被其他大量的实例方法广泛调用。
// Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = this.constructor(); if ( jQuery.isArray( elems ) ) { push.apply( ret, elems ); } else { jQuery.merge( ret, elems ); } // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; if ( name === "find" ) { ret.selector = this.selector + (this.selector ? " " : "") + selector; } else if ( name ) { ret.selector = this.selector + "." + name + "(" + selector + ")"; } // Return the newly-formed element set return ret; }, 对这个方法的理解有难度,如果看不懂第一行代码的话:“var ret = this.constructor();”,其注释是一个很好的解释:Build a new jQuery matched element set。this.constructor指向jQuery函数 var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, 调用this.constructor()后返回的是由jQuery真正的构造函数jQuery.prototype.init生成的一个jQuery实例对象,问题是此处没有传递任何参数,所以虽然返回的是jQuery实例对象,但它几乎是一个光杆司令--连length属性都没有开辟(详情参见对jQuery构造函数的阐述篇)。 但是接下来的活儿就是可劲儿的渲染这个空jQuery对象的各种属性了:push.apply( ret, elems )或者jQuery.merge( ret, elems ),由于map传入的是一个数组,因此执行的是push.apply( ret, elems ),在此不得不对Array.prototype.push方法表示由衷的敬意--Array.prototype.slice好歹要求处理的对象起码得有length属性,push方法没有任何条款!此步调用后jQuery对象如愿以偿的拥有index属性和length属性了--Array-Like Object。 继续渲染一个属性:ret.prevObject = this,所谓栈数据结构就形成了。(像不像java的HashMap中hash位上的对象栈?) 当然还要设置上下文属性的:ret.context = this.context;(jQuery的上下文对象又足以开辟一个很长很长的专章去阐述了。。。) 由于map方法没有传递第二个参数,所以ret没有selector属性,因为这个ret对象没有辛辛苦苦的去使用selector遍历DOM(Sizzle的专题)产生,而是使用拿来主义直接利用原jQuery对象经过map后的集合产生,所以不对这类jQuery对象开辟selector属性是有道理的。
jQuery.prototype.eq(i)、jQuery.prototype.first()、jQuery.prototype.last()都是jQuery.prototype.slice方法的变种,这里只分析jQuery.prototype.slice方法。
lice: function() { return this.pushStack( slice.apply( this, arguments ), "slice", slice.call(arguments).join(",") ); }, 与jQuery.prototype.map方法类似,slice方法也产生一个新的jQuery对象。这个新的jQuery对象在原jQuery对象上经过slice操作而来,关于Array.prototype.slice的操作参见其专题,特点是足够灵活。 这个方法没有定义形式参数就是因为slice的使用方法足够灵活:传入"-1"表示截取数组的最后一个元素,传入"i,i+1"表示截取数组指定位置i上的一个元素,传入"i,i+n"表示截取数组从i到i+n之间的所有元素(不包含i+n位置)。 需要注意的是pushStack的第二、三个参数,结合上面对其分析,可知返回的新jQuery对象具有selector属性且值为“oriselector.slice(i1,i2)”。
filter方法定义如下,对应winnow函数一并列出。
filter: function( selector ) { return this.pushStack( winnow(this, selector, true), "filter", selector ); }, // Implement the identical functionality for filter and not function winnow( elements, qualifier, keep ) { // Can't pass null or undefined to indexOf in Firefox 4 // Set to 0 to skip string check qualifier = qualifier || 0; if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep(elements, function( elem, i ) { var retVal = !!qualifier.call( elem, i, elem ); return retVal === keep; }); } else if ( qualifier.nodeType ) { return jQuery.grep(elements, function( elem, i ) { return (elem === qualifier) === keep; }); } else if ( typeof qualifier === "string" ) { var filtered = jQuery.grep(elements, function( elem ) { return elem.nodeType === 1; }); if ( isSimple.test( qualifier ) ) { return jQuery.filter(qualifier, filtered, !keep); } else { qualifier = jQuery.filter( qualifier, filtered ); } } return jQuery.grep(elements, function( elem, i ) { return (jQuery.inArray( elem, qualifier ) >= 0) === keep; }); } 重点在于winnow函数,所以分析下这个函数。可以看到充当filter pridicate的qualifier参数可以是函数、可以是dom node、也可以是string或其他。 当qualifier是函数时,其参数顺序和类型与jQuery("xx").each方法、jQuery("").map方法保持一致,即第一个参数是index,第二个参数是elements元素。并且可以根据winnow第三个参数keep决定取反逻辑。 当qualifier是dom node时(有nodeType属性),只精确选定elements中就是qualifier节点的那些元素。(该qualifier对象只有一个,但是引用可以有多个) 当qualifier是string时,首先从elements中过滤出只是Element类型的节点集合,然后在这个集合中进一步过滤--通过调用静态工具方法jQuery.filter,jQuery.filter涉及到Sizzle核心,专题讨论。 当qualifier不是以上三种类型时(可能是Array对象或jQuery对象),过滤出elements中包含于qualifier的元素,类似两个集合的intersection操作。