jQuery each的实现

each介绍

jQuery中each方法,可以用来遍历数组和对象

1
jQuery.each(object, [callback])

这里传递两个参数,一个是要遍历的对象的成员或数组的索引,一个是回调函数(里面写你要对每个数据元素的操作)

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
//遍历数组
var arr = ['a','b',3,4,5]

$.each(arr, function(key, val){
console.log(key, val);
})

// 0 "a"
// 1 "b"
// 2 3
// 3 4
// 4 5

//遍历对象
var obj = {
value : '1',
name : 'blue',
sex : 'boy',
arr : [1,2,3,4,5],
obj : {value: 2, sex: 'girl'}
}
$.each(obj, function(key, val){
console.log(key, val);
})
// value 1
// name blue
// sex boy
// arr (5) [1, 2, 3, 4, 5]
// obj {value: 2, sex: "girl"}

推出循环

Es5中提供了遍历的方法 forEach,但是forEach没有办法中止或者跳出forEach循环,除了抛出一个异常。但是对于jQuery的each函数,如果需要退出 each循环可使回调函数返回false,其他返回值将被忽略。

1
2
3
4
5
6
7
$.each(["a", 1, "c", 3, 4, 5], function(key, val) {
if(key > 2) return false;
console.log(key, val);
})
// 0 "a"
// 1 1
// 2 "c"

第一版

首先,我们要根据参数的类型进行判断,如果是数组或类数组就使用for循环,对象的话使用for in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function each(obj, callback) {
var length, i = 0;
// isArrayLike 判断是否是一个数组或类数组
if(isArrayLike(obj)) {
length = obj.length;
for(; i < length; i++) {
callback(i, obj[i])
}
}else {
for(i in obj){
callback(i, obj[i])
}
}
return obj;
}

中止循环

只需要把:callback(i, obj[i])
替换成:callback(i, obj[i]) === false ? break : '';
当判断条件成立后,程序会中止。

this

关于this的一个例子

1
2
3
4
5
6
7
//第一种写法
$.each($('p'), function(){
$(this).hover(function(){ ... });
})

// 第二种写法
$('p').each(function(){ ... })

推荐的是第一种写法,因为$(“p”).each()方法是定义在jQuery函数的prototype对象上面的,而$.each()方法是定义jQuery函数上面的,调用的时候从不复杂的jQuery对象上调用,速度快得多。所以我们推荐使用第一种写法。

each最终的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function each(obj, callback) {
var length, i = 0;

if(isArrayLike(obj)) {
length = obj.length;
for(; i < length; i ++) {
if(callback.call(obj[i], i, obj[i]) === false ) {
break;
}
}
}else {
for(i in obj) {
if(callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
return obj;
}

性能比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var arr = Array.from({length: 10000000}, (v, i) => i);

console.time('for')
var i = 0;
for(; i < arr.length; i++) {
i += arr[i];
}
console.timeEnd('for')
console.time('each')
var j = 0;
$.each(arr, function(index, item) {
j += item;
});

console.timeEnd('each')

// 打印结果
// for: 0.02783203125ms
// each: 312.53466796875ms

each内部使用的也是for循环,为什么会差这么多呢?

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
function each(obj, callback) {
var i = 0;
var length = obj.length
for (; i < length; i++) {
value = callback(i, obj[i]);
}
}

function eachWithCall(obj, callback) {
var i = 0;
var length = obj.length
for (; i < length; i++) {
value = callback.call(obj[i], i, obj[i]);
}
}

function eachWithApply(obj, callback) {
var i = 0;
var length = obj.length;
for(; i < length; i ++) {
value = callback.apply(obj[i], [i,obj[i]]);
}
}

var arr = Array.from({length: 10000000}, (v, i) => i)

console.time('each')
var i = 0;
each(arr, function(index, item){
i += item;
})
console.timeEnd('each')


console.time('eachWithCall')
var j = 0;
eachWithCall(arr, function(index, item){
j += item;
})
console.timeEnd('eachWithCall')

console.time('eachWithApply')
var i = 0;
each(arr, function(index, item){
i += item;
})
console.timeEnd('eachWithApply')

//each: 815.879150390625ms
//eachWithCall: 308.455078125ms
//eachWithApply: 828.31591796875ms

这里的each效率比使用了call还要慢???—待解决