jQuery 源码学习之路四(jQuery api 核心==>实用工具系列三)

jQuery.each, jQuery.map, jQuery.proxy, jQuery.parseHTML, jQuery.parseXML

1. 核心==>实用工具 ==>jQuery.each()

jQuery.each(array, callback )
array 遍历的数组。
callback 该函数会在每个对象上(迭代)调用。
jQuery.each( object, callback )
object 遍历的对象。
callback 该函数会在每个对象上(迭代)调用。
描述: 一个通用的迭代函数,它可以用来无缝迭代对象和数组。数组和类似数组的对象通过一个长度属性(如一个函数的参数对象)来迭代数字索引,从0到length - 1。其他对象通过其属性名进行迭代。
注意: $.each()函数会在内部检索并且使用传递集合的 length属性。 所以,如果集合有一个名为length的属性 - 比如 {bar: ‘foo’, length: 10} - 这个函数可能无法正常工作。
$.each()函数和 $(selector).each()是不一样的,那个是专门用来遍历一个jQuery对象。$.each()函数可用于迭代任何集合,无论是“名/值”对象(JavaScript对象)或数组。在迭代数组的情况下,回调函数每次传递一个数组索引和相应的数组值作为参数。(该值也可以通过访问this关键字得到,但是JavaScript将始终将this值作为一个Object ,即使它是一个简单的字符串或数字值。)该方法返回其第一个参数,这是迭代的对象。

(1)用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//迭代一个数组,并同时访问迭代的元素及它的索引值。
$.each( ['a','b','c'], function(i, l){
console.log( "Index #" + i + ": " + l );
});
// Index #0: a
// Index #1: b
// Index #2: c
//在一个元素的属性上进行迭代,并同时访问它的键及值。
$.each( { name: "John", lang: "JS" }, function(k, v){
console.log( "Key: " + k + ", Value: " + v );
});
// Key: name, Value: John
// Key: lang, Value: JS
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
each: function( obj, callback ) {
var length, i = 0; //初始化 length 和 i 的值为 0;
//判断传入的对象是否是类数组
if ( isArrayLike( obj ) ) {
//是类数组的情况,length 就等于数组长度
length = obj.length;
for ( ; i < length; i++ ) {
//去执行回调函数,直到回调函数的执行 return false 的情况或是循环结束
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
} else {
//不是类数组的情况,for in 循环对象
for ( i in obj ) {
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
break;
}
}
}
return obj;
}

2. 核心==>实用工具 ==>jQuery.map()

jQuery.map( array, callback(elementOfArray, indexInArray) )
array 待转换数组。
callback(elementOfArray, indexInArray) 处理每一个元素的函数。第一个参数是数组元素,第二个参数是该元素的索引值。该函数可以返回任何值。在函数内部,this将是全局的window对象。
jQuery.map( arrayOrObject, callback( value, indexOrKey ) )
arrayOrObject 待转换数组或对象。
callback( value, indexOrKey ) 处理每一个元素的函数。第一个参数是数组中元素或对象的值,第二个参数是该元素在数组中的索引值或该对象的键。该函数可以返回任何值,该返回值会被添加到数组中。若返回是数组,则会将该数组中的元素添加到最终的结果数组中。在函数内部, this指的是全球(window)的对象。
描述: 将一个数组中的所有元素转换到另一个数组中。

(1)用法:

这里只是列举两个例子,具体的,可以到api中去查看[!http://www.css88.com/jqapi-1.9/jQuery.map/]

1
2
3
4
5
6
7
8
9
10
11
//若原始数组中的值大于 0,则将该值加 1 后,映射到新的数组中,否则在结果数组中将不包含该值。
var m1 = $.map( [0,1,2], function(n){
return n > 0 ? n + 1 : null;
});
console.log(m1); //[2, 3]
//返回对象属性值乘以 2 的数组
var dimensions = { width: 10, height: 15, length: 20 };
dimensions = $.map( dimensions, function( value, index ) {
return value * 2;
});
console.log(dimensions); // [20, 30, 40]

(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// arg is for internal usage only
map: function( elems, callback, arg ) {
var length, value,
i = 0,
ret = [];
// Go through the array, translating each of the items to their new values
//判断是否是一个类数组对象
if ( isArrayLike( elems ) ) {
length = elems.length; // length 的长度即数组的长度
//对数组做循环处理,value即callback 中 return 出的值
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) { //value值不为空,则向数组 ret 中添加
ret.push( value );
}
}
// Go through every key on the object,
} else {
//如果不是类数组对象,直接用 for in 循环对象,value即callback 中 return 出的值
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
//value值不为空,则向数组 ret 中添加
if ( value != null ) {
ret.push( value );
}
}
}
// Flatten any nested arrays
//返回数组
return concat.apply( [], ret );
}

3. 核心==>实用工具 ==>jQuery.proxy()

描述: 接受一个函数,然后返回一个新函数,并且这个新函数始终保持了特定的上下文语境。

jQuery.proxy( function, context )

function 将要改变上下文语境的函数。
context 函数的上下文语境(this)会被设置成这个 object 对象。

例一:
1
<button id="proxy">proxy</button>

(1)目标:点击button时,button的文字变成红色。

1
2
3
$("#proxy").click(function(){
$(this).css({"color" : "red"});
})

(2)接着上面的需求,现在要求,500ms后再变换字体的颜色

1
2
3
4
5
6
$("#proxy").click(function(){
setTimeout(function(){
$(this).css({"color" : "red"});
},500)
});
//这么写没有成功,那是因为此时this的指向不再是$("#proxy")这个对象

看$.proxy大展身手:

1
2
3
4
5
6
$("#proxy").click(function(){
setTimeout($.proxy(function(){
$(this).css({"color":"red"});
},this),500)
});
//点击button后,500ms后,button的颜色变成了红色。

jQuery.proxy( context, name )

context 函数的上下文语境会被设置成这个 object 对象。
name (string类型)将要改变上下文语境的函数名(这个函数必须是前一个参数 context 对象的属性)。

例二:
1
2
3
4
5
6
7
8
//还是接着例一后面的那个需求:
$("#proxy").click(function(){
this.fun = function(){ //fun 前一个参数 this 对象的属性
$(this).css({"color":"red"});
}
setTimeout($.proxy(this,"fun"),500);
});
//注意,这里这么调用,感觉并不方便。此处只是为了演示一下用法。
jQuery.proxy( function, context [, additionalArguments ] )

function 将要改变上下文语境的函数。
context 函数的上下文语境会被设置成这个 object 对象。
additionalArguments 任何数目的参数传递给function参数的函数引用。

例三:

还接着例一的例子继续说,现在又有一个需求了,想要第一次点击button时,button文字颜色是红色,之后的每次点击button的颜色是绿色。

1
2
3
4
5
6
7
8
9
10
11
12
var count = 0;
$("#proxy").click(function(){
setTimeout($.proxy(function(num){
if(num > 0){
$(this).css({"color":"green"});
return;
}
$(this).css({"color":"red"});
count ++;
},this,count),500);
});
//实现要的效果,对 $.proxy 内的函数进行了传参。

jQuery.proxy( context, name [, additionalArguments ] )

context 函数的上下文语境会被设置成这个 object 对象。
name 将要改变上下文语境的函数名(这个函数必须是前一个参数 context 对象的属性)。
additionalArguments 任何数目的参数传递给name参数中命名的函数。

例四:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//此处需求还是例三的需求,只是代码需要更改一下
var count = 0;
$("#proxy").click(function(){
this.fun = function(num){
if(num > 0){
$(this).css({"color":"green"});
return;
}
$(this).css({"color":"red"});
count ++;
}
setTimeout($.proxy(this,"fun",count),500);
});
//看完上面的例子,此处也就没有什么解释的必要了。
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// A global GUID counter for objects
guid: 1, //一个jquery的全局变量
// Bind a function to a context, optionally partially applying any
// arguments.
proxy: function( fn, context ) {
var tmp, args, proxy;
//判断 context 的类型是否是字符串,是字符串的话,代表传参过来的 fn 是上下文语句要设置的对象
// context 是 fn 的一个属性,且是一个函数名
if ( typeof context === "string" ) {
//通过 tmp 这个变量,把上下文的对象和函数做个调换,这样就与jQuery.proxy( function, context )一样
tmp = fn[ context ];
context = fn;
fn = tmp;
}
// Quick check to determine if target is callable, in the spec
// this throws a TypeError, but we will just return undefined.
//若 fn 不是一个函数,则返回 undefined。
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// Simulated bind
// args 是 arguments 除了前两个参数的集合
args = slice.call( arguments, 2 );
proxy = function() {
//对传入的那个函数进行传参的处理
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
};
// Set the guid of unique handler to the same of original handler, so it can be removed
proxy.guid = fn.guid = fn.guid || jQuery.guid++; //这里我的理解是记录了$.proxy调用的次数
return proxy;
}
#### 4. 核心==>实用工具 ==>jQuery.parseHTML( data [, context ] [, keepScripts ] )
data 类型: String 用来解析的HTML字符串。
context (默认: document) 类型: Element DOM元素的上下文,在这个上下文中将创建的HTML片段。
keepScripts (默认: false) 类型: Boolean 一个布尔值,表明是否在传递的HTML字符串中包含脚本。
安全注意事项:
大多数的jQuery的API接受的HTML字符串将运行所包含在HTML中的脚本。jQuery.parseHTML不运行HTML中解析出来的脚本,除非 keepScripts参数为true。然而,它仍然是可能在大多数环境中间接地执行脚本,例如通过<img onerror>属性。调用者应该意识到这一点,并通过清理或避免任何不可信来源的输入,如URL或cookies,来防止它。为了未来的兼容性,当keepScripts为不确定的或false时,调用者不应该依赖于这个能力来运行任何脚本内容。
###### (1)用法:
```bash
<div id="log">
<h3>Content:</h3>
</div>
<script>
var $log = $( "#log" ),
str = "hello, <b>my name is</b> jQuery.",
html = $.parseHTML( str );
// Append the parsed HTML
$log.append( html );
</script>
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
jQuery.parseHTML = function( data, context, keepScripts ) {
if ( typeof data !== "string" ) { // data 不是 string 类型,则返回 [];
return [];
}
//判断 context 是否是 boolean值,是的话,就意味着只传入了两个参数,context 是没有传值的。
if ( typeof context === "boolean" ) {
keepScripts = context; // 是否保存 script 标签
context = false;
}
var base, parsed, scripts;
// 若 context 没有传值
if ( !context ) {
// Stop scripts or inline event handlers from being executed immediately
// by using document.implementation
//支持 createHTMLDocument
if ( support.createHTMLDocument ) {
//创建一个全新的 document(和当前的document不同),参数为空,这个参数是新document的 title标题字段
context = document.implementation.createHTMLDocument( "" );
// Set the base href for the created document
// so any parsed elements with URLs
// are based on the document's URL (gh-2965)
//创建一个 <base> 的标签,base 的 href 属性就是当前 document 的路径,接着把 base 这个标签添加到 head 内
base = context.createElement( "base" );
base.href = document.location.href;
context.head.appendChild( base );
} else {
//否则 context 就是当前的 document
context = document;
}
}
//正则匹配传入的 data 字符串
parsed = rsingleTag.exec( data );
// scripts 在不保存 srcript 的条件下(keepScripts 为 false),为 [];
scripts = !keepScripts && [];
// Single tag
if ( parsed ) {
//如果是单个标签就调用相应的createElement方法,默认上下文是document!
return [ context.createElement( parsed[ 1 ] ) ];
}
//如果不是单个标签就调用buildFragment方法,把html字符串传入,同时上下文也传入,第三个参数就是scripts!
//如果paseHTML的第三个参数是false,那么这里的scripts就是一个数组,传递到buildFragment中会把所有的script标签放在里面
//所以就要收到移除!
parsed = buildFragment( [ data ], context, scripts );
if ( scripts && scripts.length ) {
jQuery( scripts ).remove();
}
//buildFragment返回的是文档碎片,所以要变成数组,调用jQuery.merge方法!
return jQuery.merge( [], parsed.childNodes );
};

5. 核心==>实用工具 ==>jQuery.parseXML( data )

data 类型: String 用来解析的格式良好的XML字符串。
描述: 解析一个字符串到一个XML文档。

(1)用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<p id="someElement"></p>
<p id="anotherElement"></p>
<script>
var xml = "<rss version='2.0'><channel><title>RSS Title</title></channel></rss>",
xmlDoc = $.parseXML( xml ),
$xml = $( xmlDoc ),
$title = $xml.find( "title" );
/* append "RSS Title" to #someElement */
$( "#someElement" ).append( $title.text() );
/* change the title to "XML Title" */
$title.text( "XML Title" );
/* append "XML Title" to #anotherElement */
$( "#anotherElement" ).append( $title.text() );
</script>
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
jQuery.parseXML = function( data ) {
var xml;
//若 data 不存在或是 data 的类型不是 string,那么返回 null
if ( !data || typeof data !== "string" ) {
return null;
}
// Support: IE 9 - 11 only
// IE throws on parseFromString with invalid input.
try {
//利用原生 js 解析 xml 文档的方法
//new DOMParser() DOMParser 对象解析 XML 文本并返回一个 XML Document 对象。
//要使用 DOMParser,使用不带参数的构造函数来实例化它,然后调用其 parseFromString() 方法。
//parseFromString(text, contentType) text 参数是要解析的 XML 标记。
//contentType 是文本的内容类型。可能是 "text/xml""application/xml"
//或 "application/xhtml+xml" 中的一个。注意,不支持 "text/html"
xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
} catch ( e ) {
//抛出错误
xml = undefined;
}
if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
//抛出错误
jQuery.error( "Invalid XML: " + data );
}
//返回 xml
return xml;
}

到此,jquery 源码 api 中 核心==>实用工具就告一段落了。api中的工具还有 $.ready 的源码没有解析,这个后期我会另写一篇文章与$(document).ready()一起来解析。
2017-11-23