jQuery transform/action类型静态工具方法探究

Posted 程序猿子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jQuery transform/action类型静态工具方法探究相关的知识,希望对你有一定的参考价值。

研读了jQuery源码后,发现非常有趣的一个现象:一个前端单节点内的脚本语言编程框架与大数据领域内集群上的编译语言型编程框架从逻辑抽象层面上并没有差异,更通俗的说是与函数式编程的普遍思想没有差异--编程果然是殊途同归的。本篇博客参照Spark RDD的Transformations和Actions对jQuery静态工具方法做一个简单的总结。


一 jQuery.each


这里探究的的each是jQuery类型的静态方法,不是原型对象中的实例方法。
		// args is for internal usage only
		each: function( object, callback, args ) 
			var name, i = 0,
				length = object.length,
				isObj = length === undefined || jQuery.isFunction( object );

			if ( args ) 
				if ( isObj ) 
					for ( name in object ) 
						if ( callback.apply( object[ name ], args ) === false ) 
							break;
						
					
				 else 
					for ( ; i < length; ) 
						if ( callback.apply( object[ i++ ], args ) === false ) 
							break;
						
					
				

			// A special, fast, case for the most common use of each
			 else 
				if ( isObj ) 
					for ( name in object ) 
						if ( callback.call( object[ name ], name, object[ name ] ) === false ) 
							break;
						
					
				 else 
					for ( ; i < length; ) 
						if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) 
							break;
						
					
				
			

			return object;
		,

该定义的实现说明jQuery.each实际上才是类似Spark Transformations中经典的map类型的操作(而不是另一个名为map的工具函数),用伪码描述就是op(map)。
该实现也约束了回调函数的参数类型和顺序(只看不使用第三个参数arg的用法):
如果处理的是数组类型的参数,callback.call( object[ i ], i, object[ i++ ] ):第一个参数是集合元素的index,第二个参数是该集合的元素。
如果处理的是对象类型的参数,callback.call( object[ name ], name, object[ name ] ):第一个参数是对象name,第二个参数是对应的对象value。
此处不是直接调用callback函数的--callback(i, object[ i++ ]),而是用了借用语法--call,所以为什么jQuery("xxx").each方法(本方法也一样)的回调函数中this关键字指向的是选中集合中原生的客户端对象就有了解释了:object是jQuery对象,但是object[index]是原生客户端对象--jQuery对象是Array-like Object。
最后一点是,该工具函数的返回值类型可能直接是jQuery类型的对象(如果第一个参数object是jQuery类型对象),也可能是Array类型(如果第一个参数object本身就是Array),当然也可能是其他任何Object对象--一切取决于传入的第一个参数的类型。

二 jQuery.map


这里探究的map同样是jQuery类型的静态方法,不是原型对象中的实例方法。

		// arg is for internal usage only
		map: function( elems, callback, arg ) 
			var value, key, ret = [],
				i = 0,
				length = elems.length,
				// jquery objects are treated as arrays
				isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;

			// 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 ( key in elems ) 
					value = callback( elems[ key ], key, arg );

					if ( value != null ) 
						ret[ ret.length ] = value;
					
				
			

			// Flatten any nested arrays
			return ret.concat.apply( [], ret );
		,

该定义的实现说明jQuery.map实际上是类似Spark Transformations中flatMap操作加上Actions中collect操作,用伪码描述就是op(collect)(op(flatMap))。 当然如果灵活应用该方法也可以充当filter的功能,比如传递一个实现如下的函数:
    jQuery.map(elems, function(elem)
        return predicate(elem,slice.call(arguments, 1)) ? elem : null;
    );
该实现也约束了回调函数的参数类型和顺序:
如果处理的是数组类型的参数,callback( elems[ i ], i, arg ):第一个参数是集合的元素,第二个参数是该元素的index,第三个参数是可选的。
如果处理的是对象类型的参数,callback( elems[ key ], key, arg ):第一个参数是对象value,第二个参数是对应的对象key,第三个参数是可选的。
最后一点是,该工具函数的返回值类型是Array,不是jQuery类型的对象,即op(filter)逻辑之后还有一个op(collect)逻辑(虽然内部是一次性实现的)。

三 jQuery.grep


这里探究的grep是jQuery类型的静态方法,不是原型对象中的实例方法。

		grep: function( elems, callback, inv ) 
			var ret = [], retVal;
			inv = !!inv;

			// Go through the array, only saving the items
			// that pass the validator function
			for ( var i = 0, length = elems.length; i < length; i++ ) 
				retVal = !!callback( elems[ i ], i );
				if ( inv !== retVal ) 
					ret.push( elems[ i ] );
				
			

			return ret;
		,

该定义的实现说明jQuery.grep实际上是类似Spark Transformations中filter操作加上Actions中collect操作,用伪码描述就是op(collect)(op(filter))。
该实现也约束了回调函数的参数类型和顺序,callback( elems[ i ], i ):第一个参数是集合的元素,第二个参数是该元素的index。同时回调函数callback应该严格返回boolean类型值,即使不返回boolean类型值,grep方法也会显式转换其为boolean值。
还有一点扩展用法是第三个参数:inv,默认不传的话在函数体内值为undefined,!!inv运算之后为false,但是for循环内部还有一层取反判断,即通过回调函数callback审核的元素被接纳,不通过的过滤掉。若想实现外部API取反逻辑,第三个参数需要传入boolean类型为true的值。
最后一点是,该工具函数的返回值类型是Array,不是jQuery类型的对象,即op(filter)逻辑之后还有一个op(collect)逻辑(虽然内部是一次性实现的。并且这个返回数组内的元素一定是原始集合elems中的元素,不像map那样更灵活--这里的回调函数callback真的只是承担predicate的功能。

四 jQuery.merge(jQuery.makeArray方法类似)


这里探究的merge是jQuery类型的静态方法,不是原型对象中的实例方法。

		merge: function( first, second ) 
			var i = first.length,
				j = 0;

			if ( typeof second.length === "number" ) 
				for ( var l = second.length; j < l; j++ ) 
					first[ i++ ] = second[ j ];
				

			 else 
				while ( second[j] !== undefined ) 
					first[ i++ ] = second[ j++ ];
				
			

			first.length = i;

			return first;
		,

如果要类比的话,这个工具方法jQuery.merge非常类似Spark Transformations中的union操作,用伪码描述是op(union)。
这个方法的参数同样可以比较灵活,可以有四种组合类型:(jQuery,Array)/(jQuery, jQuery)/(Array, jQuery)/(Array, Array)
该方法并没有新建一个融合后的集合对象作为返回对象,而是把第一个参数表示的对象扩展后作为返回值的,即扩展的是第一个参数对象。

五 扩展
如果类比Spark Transformations的其他操作,我们其实还可以给jQuery静态工具方法设计sample/subtract/intersection等API。作为jQuery的静态工具方法,其操作或返回的主要对象并不局限于jQuery类型对象,其实这一套静态工具方法主要目的和功能是提供给jQuery.prototype使用的(大多对应一个同名的原型方法并被其调用)。如果把jQuery类型的实例对象比作RDD的话,jQuery.prototype的实例方法才是更类似于Spark Transformations的操作,下一篇总结jQuery.prototype实例方法中真正的仿Spark Transformations操作。

以上是关于jQuery transform/action类型静态工具方法探究的主要内容,如果未能解决你的问题,请参考以下文章

jQuery 是不是有任何函数来确定 jQuery 对象引用的 DOM 元素的标签类型?

TypeScript 中 jQuery 对象的类型是啥?

jQuery's One - 使用多种事件类型触发一次

jquery - 检查元素的类型

通过 jQuery 选择数字类型的输入

jQuery原理:判断字符类型