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

jQuery.contains, jQuery.globalEval, jQuery.grep, jQuery.inArray, jQuery.isArray, jQuery.isEmptyObject

从这篇文章开始,我可能更多的是从api入手,根据api去分析源码。(api文档地址:http://www.css88.com/jqapi-1.9/)

1. 核心==》实用工具 ==》jQuery.contains( container, contained )

(1)用法:

检测一个元素是否在另一个元素之内。 不支持文档和注释节点。
注意: 第一个参数必须是一个DOM元素,不是一个jQuery对象或普通的JavaScript对象。

1
2
console.log($.contains( document.documentElement, document.body )); // true
console.log($.contains( document.body, document.documentElement )); // false

//jQuery,.contains底层调用的方法来自于JS的contains方法和compareDocumentPosition方法
1>. JS的contains方法是直接判断一个元素是否包含另一个元素,返回值为布尔值

1
console.log(document.contains(document.body)); //true

2>. compareDocumentPosition() 方法比较两个节点,并返回描述它们在文档中位置的整数。

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
<!DOCTYPE html>
<html>
<body>
<p id="p1">This is a paragraph</p>
<p id="p2">This is another paragraph</p>
<p id="demo">请点击按钮来比较两个段落的位置。</p>
<button onclick="myFunction()">试一下</button>
<script>
function myFunction()
{
var p1=document.getElementById("p1").lastChild;
var p2=document.getElementById("p2").lastChild;
var x=document.getElementById("demo");
x.innerHTML=p1.compareDocumentPosition(p2);
}
</script>
</body>
</html>
//1:没有关系,两个节点不属于同一个文档。
//2:第一节点(P1)位于第二个节点后(P2)。
//4:第一节点(P1)定位在第二节点(P2)前。
//8:第一节点(P1)位于第二节点内(P2)。
//16:第二节点(P2)位于第一节点内(P1)。
//32:没有关系,或是两个节点是同一元素的两个属性。
//注释:返回值可以是值的组合。例如,返回 20 意味着在 p2 在 p1 内部(16),并且 p1 在 p2 之前(4)。

(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
/* Contains
---------------------------------------------------------------------- */
//判断浏览器是否支持compareDocumentPosition
hasCompare = rnative.test( docElem.compareDocumentPosition );
// Element contains another
// Purposefully self-exclusive
// As in, an element does not contain itself
//一个元素包含另一个,但是不包含自身
//若浏览器不支持compareDocumentPosition,则检测是否支持js的contains函数
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
//nodeType = 9,代表整个文档(DOM 树的根节点)。如果a元素是document对象,
//那么获取a的documentElement对象,否则保存a对象即可
var adown = a.nodeType === 9 ? a.documentElement : a,
//若 b 元素存在,则获取 b 元素的父节点
bup = b && b.parentNode;
//1. 若 b 元素的父节点是 a 元素,则直接 return true
//2. 若 b 元素的父节点不是 a 元素, b 的父元素存在且其 nodeType 值为1
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
//若支持contains方法,那么调用原生js的contains方法,判断 a 是否包含 b 元素的父节点
//contains方法直接返回布尔值
adown.contains( bup ) :
//若不支持contains方法,则用compareDocumentPosition方法来判断,16这个数值在上面有说明
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
));
} :
//若浏览器不支持compareDocumentPosition 和 contains函数
// b 元素存在,且 b 元素的父节点和 a 元素完全相等时,return true
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
// 否则返回false
return false;
};

2. 核心==》实用工具 ==》jQuery.globalEval( code )

code 是用来执行的JavaScript代码,为 string 类型。它是在全局上下文下执行(这对加载外部动态脚本很重要)。

(1)用法:
1
2
3
4
5
function test(){
jQuery.globalEval("var newVar = true;")
}
test();
console.log(newVar); //true
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function DOMEval( code, doc ) {
//若 doc 不存在,则 doc 就是 document 对象
doc = doc || document;
// 创建一个 script 标签
var script = doc.createElement( "script" );
// 标签的内容是传值过来的 code
script.text = code;
// 把 script 标签动态添加给 head 的头部,然后 全局的对象添加给了window,接着再把 script 这个标签删除
// 即便 script 标签删除了,全局对象已经添加给了 window 对象。
doc.head.appendChild( script ).parentNode.removeChild( script );
}
// Evaluates a script in a global context
globalEval: function( code ) {
// 调用 DOMEval 方法
DOMEval( code );
}

3.核心==》实用工具 ==》jQuery.grep( array, function(elementOfArray, indexInArray) [, invert ] )

$.grep() 函数使用指定的函数过滤数组中的元素,并返回过滤后的数组。
提示:源数组不会受到影响,过滤结果只反映在返回的结果数组中。

array : 用于查询元素的类数组对象.
elementOfArray : Function类型 指定的过滤函数。grep()方法为function提供了两个参数:
其一为当前迭代的数组元素,其二是当前迭代元素在数组中的索引。
invert : 可选。 Boolean类型 默认值为false,指定是否反转过滤结果。

(1)用法:
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
var arr1 = [ 1, 9, 3, 8, 6, 1, 5, 9, 4, 7, 3, 8, 6, 9, 1 ];
console.log( arr1.join( ", " ) ); // 1, 9, 3, 8, 6, 1, 5, 9, 4, 7, 3, 8, 6, 9, 1
var arr = jQuery.grep(arr1, function( n, i ) {
return ( n !== 5 && i > 4 );
});
console.log( arr.join( ", " ) ); //1, 9, 4, 7, 3, 8, 6, 9, 1 返回数组中不为 5 和索引值在原数组中大于 4 的
arr = jQuery.grep(arr, function( a ) {
return a !== 9;
});
console.log( arr.join( ", " ) ); // 1, 4, 7, 3, 8, 6, 1 返回数组不包含 9
var arr2 = [0,1,2,3,4];
var iArr = $.grep(arr2,function(b){
return b !== 3;
},true);
console.log(iArr.join(",")); // 3 不加true,正常应返回不包含 3 的数组;加true,就是返回与条件相反的数组,即返回 3 ;
```
###### (2)源码:
```bash
grep: function( elems, callback, invert ) {
var callbackInverse,
matches = [],
i = 0,
length = elems.length, // 数组长度
callbackExpect = !invert; // callbackExpect 默认值给 true
// Go through the array, only saving the items
// that pass the validator function
for ( ; i < length; i++ ) {
//对数组进行循环,判断当前元素值和索引值是否满足过滤函数中的要求
// 因为我们的过滤函数中是 return 出来的是一个布尔值
// 若不满足过滤条件 callbackInverse 则为 true
callbackInverse = !callback( elems[ i ], i );
// 当callbackInverse与callbackExpect不相等时,就往matches中添加当前元素
if ( callbackInverse !== callbackExpect ) {
matches.push( elems[ i ] );
}
}
//最后返回 matches 数组,即过滤后的数组
return matches;
}

4.核心==》实用工具 ==》jQuery.inArray( value, array [, fromIndex ] )

$.inArray() 函数用于在数组中查找指定值,并返回它的索引值(如果没有找到,则返回-1)
提示:源数组不会受到影响,过滤结果只反映在返回的结果数组中。
value : 任意类型 用于查找的值。
array : Array类型 指定被查找的数组。
fromIndex :可选。Number类型 指定从数组的指定索引位置开始查找,默认为 0。

(1)用法:

1
2
3
4
5
var arr = [ 4, "Pete", 8, "John" ];
console.log( jQuery.inArray( "John", arr ) ); // 3
console.log( jQuery.inArray( 4, arr ) ); // 0
console.log( jQuery.inArray( "Karl", arr ) ); // -1
console.log( jQuery.inArray( "Pete", arr, 2 ) ); // -1
(2)源码:
1
2
3
4
5
6
7
inArray: function( elem, arr, i ) {
// 数组不存在,则直接返回 -1;
// 如果你不太了解js中 call 和 apply的用法,可以参考我的另一篇文章 [!文章路径]
// indexOf.call(arr,elem,i) ; 其实就是把字符串的indexOf 继承给数组,并且传递 elem 和 i 参数。
// 更简单一点其实可以理解为: arr.indexOf(elem,i);
return arr == null ? -1 : indexOf.call( arr, elem, i );
}

5. 核心==》实用工具 ==》jQuery.isArray( obj )

$.isArray()函数用于判断指定参数是否是一个数组。
obj : 任意类型 需要进行判断的任意值。

(1)用法:
1
2
3
console.log($.isArray([0,1,2])); // true
console.log($.isArray("string")); // false
console.log($.isArray(new Object())); // false
(2)源码:
1
2
3
4
5
6
isArray: Array.isArray // 就是调用原生js 的 Array.isArray 方法
// 仿 jq 用原生 js 的 Array.isArray 方法
var arr = [0,1,2];
var isArr = Array.isArray;
console.log(isArr(arr)); // true

6. 核心==》实用工具 ==》jQuery.isEmptyObject( object )

检查对象是否为空(不包含任何属性)。
object : 将要检查是否为空的对象。

(1)用法:
1
2
3
4
5
console.log(jQuery.isEmptyObject()) // true
console.log(jQuery.isEmptyObject("")); // true
console.log(jQuery.isEmptyObject("aaa")); //false
console.log(jQuery.isEmptyObject({})); // true
console.log(jQuery.isEmptyObject({ foo: "bar" })); //false
(2)源码:
1
2
3
4
5
6
7
8
9
10
11
12
isEmptyObject: function( obj ) {
/* eslint-disable no-unused-vars */
// See https://github.com/eslint/eslint/issues/6125
var name;
// 只要for循环执行了,说明obj不是空的否则返回true,这种判断很简单没有判断参数是否是对象的情况下就直接循环了。
// 也可以用它来判断是否是空字符串。
for ( name in obj ) {
return false;
}
return true;
}

结束语:

这篇文章先写到这里,实用工具这一模块还有很多的方法,但不在这一篇文章中全部展现了,是为了避免篇幅太长。下一篇,接着聊。