好久没写文章了,今天写一篇。
本文研究一下$.each相关的总总。
本文的结构
1.简介
对于each方法,应该没有同学不会的。
如果要遍历一个数组,我们可以如下:
使用each代替循环没有问题。
但对于break和continue的支持是又怎么样的呢?
2.break的等效写法
比如我要找到数组里第一个偶数,使用for循环如下:
使用each方法可以使用如下:
3.continue的等效写法:
比如要找到所有的偶数的可以使用如下for语句:
其对于的continue的等效写法是什么呢?
只需要return即可。不一定要返回true。
return表示结束当前函数,那么自然相当于在for循环里结束本次循环。
return什么都是表示continue的意思,但false除外,
return false表示break的意思,是结束整个循环的意思。
4.源码分析
为什么呢?
我们要看看each的源码(来自zepto):
$.each的实现思路很简单,
首先判断elements是否是类数组;
如果是类数组,那么就按照下标来遍历;
如果是对象,就按照属性遍历。
其中核心代码是下面这句话:
如果结束循环,自然就是break的意思。
为啥是用“三等号”呢?
因为一个函数不写return的话,也相当于return undefined的,
而undefined == false是为真的。
而用了三等号,从而实现了break和continue的区分。不得不说这句代码很巧妙。
each方法的补充。
1.第一个参数可以是类数组可以是对象
2.this的指向问题
从
3.对其api的吐槽
callback传递的参数的顺序是按“键”和“值”的顺序来的。
个人觉得是把比较重要的“值”放在了第二个参数位置上,不是太好的。
此时,不由得让人想起[].forEach方法。其callback参数顺序是value,index, array。
如果我要弹出数组中的元素,可以用:
而使用each的话,只能添一层匿名函数了。
当然这不是大问题,只是个人对这个顺序不太习惯罢了。
5.$.fn.each
我们对jq的了解知道,还有个$().each方法:
其源码(zepto)大致如下:
里面使用的[].every表示:
看看每次callback是不是都不是false,如果是false的话也结束遍历。
如改成for语句,那么便跟之前$.each的类数组很类似。
6.likeArray的源码
至此$.each的方法也算是研究明白了。
送佛送到西,下来我们来看看zepto中怎么判断类数组的:
zepto没有把此方法暴露出来。
用了一堆与或非,仔细看下来应该能看懂。
什么是类数组?(请参考《JavaScript权威指南》161页)
有length属性,并能按下标访问的对象。
数组本身是,arguments是,NodeList是,字符串也是。
但是window对象有length属性(表示当前窗口中frames的数量),
函数也有length属性(表示函数形参的个数),一般不认为他们是类数组。
7.判断类型方法的实现
因为likeArray中用了判断类型的方法,最后顺便也说一说其相关知识点
type函数核心还是使用的{}.toString方法。
源码中用字符串来存储数据的方法也值得学习。
有了type函数之后,其他的指定类型判断函数就简单了:
具体的就不说了。
8.总结
至此由$.each串起来的知识点都已经总结完,
其实内容来说都是很简单的,本文算是zepto的部分源码分析了。
其中break和continue的等价用法还是需要掌握的。
至于源码都不是很难的东西,尤其是后面的判断类型的方法,
判断是不是window和document的实现都是经典的实现。
本文完。
本文研究一下$.each相关的总总。
本文的结构
- 1.简介
- 2.break的等效写法
- 3.continue的等效写法
- 4.源码分析
- 5.$.fn.each
- 6.likeArray的源码
- 7.判断类型方法的实现
- 8.总结
1.简介
对于each方法,应该没有同学不会的。
如果要遍历一个数组,我们可以如下:
使用each代替循环没有问题。
但对于break和continue的支持是又怎么样的呢?
2.break的等效写法
比如我要找到数组里第一个偶数,使用for循环如下:
使用each方法可以使用如下:
3.continue的等效写法:
比如要找到所有的偶数的可以使用如下for语句:
其对于的continue的等效写法是什么呢?
只需要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.第一个参数可以是类数组可以是对象
My name is Laoyao
I love JavaScript
2.this的指向问题
从
callback.call(elements[i], i, elements[i])可以看出callback中的this指向的是键值对中的“值”:
My name is Laoyao
I love JavaScript
3.对其api的吐槽
callback传递的参数的顺序是按“键”和“值”的顺序来的。
个人觉得是把比较重要的“值”放在了第二个参数位置上,不是太好的。
此时,不由得让人想起[].forEach方法。其callback参数顺序是value,index, array。
如果我要弹出数组中的元素,可以用:
而使用each的话,只能添一层匿名函数了。
当然这不是大问题,只是个人对这个顺序不太习惯罢了。
5.$.fn.each
我们对jq的了解知道,还有个$().each方法:
My name is Laoyao
I love JavaScript
其源码(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的实现都是经典的实现。
本文完。