【Zepto源码】$.each方法

老姚
老姚 发布于 2016-10-17 17:15:25 浏览:2074 类型:原创 - 随笔 分类:JavaScript - zepto源码分析系列 二维码: 作者原创 版权保护
好久没写文章了,今天写一篇。

本文研究一下$.each相关的总总。
本文的结构
  1. 1.简介
  2. 2.break的等效写法
  3. 3.continue的等效写法
  4. 4.源码分析
  5. 5.$.fn.each
  6. 6.likeArray的源码
  7. 7.判断类型方法的实现
  8. 8.总结

1.简介
对于each方法,应该没有同学不会的。
如果要遍历一个数组,我们可以如下:
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
$.each([1, 4, 9], function(index, value) {
	alert(value);
});
</script>

使用each代替循环没有问题。
但对于break和continue的支持是又怎么样的呢?

2.break的等效写法
比如我要找到数组里第一个偶数,使用for循环如下:
<script>
var arr = [1, 2, 3, 4, 5];
for (var i = 0; i < arr.length; i++) {
	var value = arr[i];
	if (value % 2 == 0) {
		alert(value);	
		break;
	}
}
</script>

使用each方法可以使用如下:
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
var arr = [1, 2, 3, 4, 5];
$.each(arr, function(index, value) {
	if (value % 2 == 0) {
		alert(value);
		return false;
	}
});
</script>


3.continue的等效写法:
比如要找到所有的偶数的可以使用如下for语句:
<script>
var arr = [1, 2, 3, 4, 5];
for (var i = 0; i < arr.length; i++) {
	var value = arr[i];
	if (value % 2 == 1) {
		continue;
	}
	alert(value);
}
</script>

其对于的continue的等效写法是什么呢?
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
var arr = [1, 2, 3, 4, 5];
$.each(arr, function(index, value) {
	if (value % 2 == 1) {
		return;
	}
	alert(value);
});
</script>

只需要return即可。不一定要返回true。

return表示结束当前函数,那么自然相当于在for循环里结束本次循环。
return什么都是表示continue的意思,但false除外,
return false表示break的意思,是结束整个循环的意思。

4.源码分析
为什么呢?
我们要看看each的源码(来自zepto):
$.each = function (elements, callback) {
	var i, key
	if (likeArray(elements)) {
		for (i = 0; i < elements.length; i++)
			if (callback.call(elements[i], i, elements[i]) === false) return elements
	} else {
		for (key in elements)
			if (callback.call(elements[key], key, elements[key]) === false) return elements
	}
	return elements
}

$.each的实现思路很简单,
首先判断elements是否是类数组;
如果是类数组,那么就按照下标来遍历;
如果是对象,就按照属性遍历。

其中核心代码是下面这句话:
callback.call(elements[key], key, elements[key]) === false
这里面用了“三等号”来做判断,如果callback的返回结果是false那么就结束循环。
如果结束循环,自然就是break的意思。

为啥是用“三等号”呢?
因为一个函数不写return的话,也相当于return undefined的,
而undefined == false是为真的。
而用了三等号,从而实现了break和continue的区分。不得不说这句代码很巧妙。

each方法的补充。
1.第一个参数可以是类数组可以是对象
<p>My name is Laoyao</p>
<p>I love JavaScript</p>
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
var ps = document.querySelectorAll('p');
var text = [];
$.each(ps, function(i, p) {
	text.push(p.innerText);
});
alert(text.join('. '));
</script>


2.this的指向问题

callback.call(elements[i], i, elements[i])
可以看出callback中的this指向的是键值对中的“值”:
<p>My name is Laoyao</p>
<p>I love JavaScript</p>
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
var text = [];
$.each($('p'), function(i, p) {
	text.push(this.innerText);
});
alert(text.join('. '));
</script>


3.对其api的吐槽
callback传递的参数的顺序是按“键”和“值”的顺序来的。
个人觉得是把比较重要的“值”放在了第二个参数位置上,不是太好的。

此时,不由得让人想起[].forEach方法。其callback参数顺序是value,index, array。
如果我要弹出数组中的元素,可以用:
<script>
[1, 4, 9].forEach(alert);
</script>

而使用each的话,只能添一层匿名函数了。
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
$.each([1, 4, 9], function(key, value) {
	alert(value);
});
</script>

当然这不是大问题,只是个人对这个顺序不太习惯罢了。

5.$.fn.each
我们对jq的了解知道,还有个$().each方法:
<p>My name is Laoyao</p>
<p>I love JavaScript</p>
<script src="http://cdn.bootcss.com/zepto/1.2.0/zepto.js"></script>
<script>
var text = [];
$('p').each(function(i, p) {
	text.push(this.innerText);
});
alert(text.join('. '));
</script>

其源码(zepto)大致如下:
var $ = function() {};
$.fn = $.prototype = {};
$.fn.each = function(callback) {
	[].every.call(this, function(el, index) {
		return callback.call(el, index, el) !== false;
	});
	return this;
};

里面使用的[].every表示:
看看每次callback是不是都不是false,如果是false的话也结束遍历。
如改成for语句,那么便跟之前$.each的类数组很类似。

6.likeArray的源码
至此$.each的方法也算是研究明白了。
送佛送到西,下来我们来看看zepto中怎么判断类数组的:
function likeArray(obj) {
	var length = !!obj && 'length' in obj && obj.length;
	var	type = $.type(obj);
	return 'function' != type 
			&& !isWindow(obj) 
			&& ('array' == type 
				|| length === 0 
				|| (typeof length == 'number' 
					&& length > 0 
					&& (length - 1) in obj));
}

zepto没有把此方法暴露出来。
用了一堆与或非,仔细看下来应该能看懂。

什么是类数组?(请参考《JavaScript权威指南》161页)
有length属性,并能按下标访问的对象。
数组本身是,arguments是,NodeList是,字符串也是。
但是window对象有length属性(表示当前窗口中frames的数量),
函数也有length属性(表示函数形参的个数),一般不认为他们是类数组。

7.判断类型方法的实现
因为likeArray中用了判断类型的方法,最后顺便也说一说其相关知识点
var class2type = {};
var toString = class2type.toString;
'Boolean Number String Function Array Date RegExp Object Error'.split(' ').forEach(function(name) {
	class2type["[object " + name + "]"] = name.toLowerCase();
})
function type(obj) {
	return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"
}
console.log(class2type)
//$.type = type;

type函数核心还是使用的{}.toString方法。
源码中用字符串来存储数据的方法也值得学习。

有了type函数之后,其他的指定类型判断函数就简单了:
function isFunction(value) {
	return type(value) == "function"
}

function isWindow(obj) {
	return obj != null && obj == obj.window
}

function isDocument(obj) {
	return obj != null && obj.nodeType == obj.DOCUMENT_NODE
}

function isObject(obj) {
	return type(obj) == "object"
}

function isPlainObject(obj) {
	return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype
}

具体的就不说了。

8.总结
至此由$.each串起来的知识点都已经总结完,
其实内容来说都是很简单的,本文算是zepto的部分源码分析了。
其中break和continue的等价用法还是需要掌握的。
至于源码都不是很难的东西,尤其是后面的判断类型的方法,
判断是不是window和document的实现都是经典的实现。

本文完。
z
给个赞 26 人点赞
收藏 44 人收藏
评论 已有 12 条评论;以下用户言论只代表其个人观点,不代表 前端网(QDFuns) 的观点或立场。
登录 以后才能发表评论
最新评论
陈陈陈大文
陈陈陈大文2016-11-22 09:18:1512F
习惯用forEach fitler等原生方法了。。。
举报 支持 (0) 回复 (0)
1063363934
10633639342016-10-31 14:46:1811F
膜拜啊,三十七万阅读量
举报 支持 (0) 回复 (0)
阿波罗D波
阿波罗D波2016-10-20 14:11:5810F
老九
举报 支持 (0) 回复 (0)
蓝波乃
蓝波乃2016-10-19 13:18:279F
小八~~
举报 支持 (0) 回复 (0)
zhou88588
zhou885882016-10-19 11:31:008F
每次把$().each中匿名函数的参数index,value和.map,.forEach中匿名函数的value,index搞混了   吐血啊 emoticon
举报 支持 (0) 回复 (0)
529814565
5298145652016-10-19 09:45:067F
小七?
举报 支持 (0) 回复 (0)
一寸心
一寸心2016-10-18 22:04:176F
小六emoticon
举报 支持 (0) 回复 (0)
vickymouse
vickymouse2016-10-18 16:37:115F
小五
举报 支持 (0) 回复 (0)
brucelyy
brucelyy2016-10-18 13:30:364F
老四emoticon
举报 支持 (0) 回复 (0)
极乐网
极乐网2016-10-18 12:57:123F
第三个,楼上继续!支持老姚,支持优质原创内容~
举报 支持 (0) 回复 (0)
前端的路上有我
前端的路上有我2016-10-18 09:13:402F
第二个~
举报 支持 (0) 回复 (0)
DarkNight
DarkNight2016-10-17 19:16:091F
哈哈 第一个
举报 支持 (0) 回复 (0)
老姚 老姚 作者

与自己为敌,与自己为友,一边深挖思想,一边埋葬自己。

作者最新