each介绍

jQuery 的 each 方法,作为一个通用遍历方法,可用于遍历对象和数组。

语法为:

  1. jQuery.each(object, [callback])

回调函数拥有两个参数:第一个为对象的成员或数组的索引,第二个为对应变量或内容。

  1. // 遍历数组
  2. $.each( [0,1,2], function(i, n){
  3. console.log( "Item #" + i + ": " + n );
  4. });
  5.  
  6. // Item #0: 0
  7. // Item #1: 1
  8. // Item #2: 2
  1. // 遍历对象
  2. $.each({ name: "John", lang: "JS" }, function(i, n) {
  3. console.log("Name: " + i + ", Value: " + n);
  4. });
  5. // Name: name, Value: John
  6. // Name: lang, Value: JS

退出循环

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

  1. $.each( [0, 1, 2, 3, 4, 5], function(i, n){
  2. if (i > 2) return false;
  3. console.log( "Item #" + i + ": " + n );
  4. });
  5.  
  6. // Item #0: 0
  7. // Item #1: 1
  8. // Item #2: 2

第一版

那么我们该怎么实现这样一个 each 方法呢?

首先,我们肯定要根据参数的类型进行判断,如果是数组,就调用 for 循环,如果是对象,就使用 for in 循环,有一个例外是类数组对象,对于类数组对象,我们依然可以使用 for 循环。

更多关于类数组对象的知识,我们可以查看《JavaScript专题之类数组对象与arguments》

那么又该如何判断类数组对象和数组呢?实际上,我们在《JavaScript专题之类型判断(下)》就讲过jQuery 数组和类数组对象判断函数 isArrayLike 的实现。

所以,我们可以轻松写出第一版:

  1. // 第一版
  2. function each(obj, callback) {
  3. var length, i = 0;
  4.  
  5. if ( isArrayLike(obj) ) {
  6. length = obj.length;
  7. for ( ; i < length; i++ ) {
  8. callback(i, obj[i])
  9. }
  10. } else {
  11. for ( i in obj ) {
  12. callback(i, obj[i])
  13. }
  14. }
  15.  
  16. return obj;
  17. }

中止循环

现在已经可以遍历对象和数组了,但是依然有一个效果没有实现,就是中止循环,按照 jQuery each 的实现,当回调函数返回 false 的时候,我们就中止循环。这个实现起来也很简单:

我们只用把:

  1. callback(i, obj[i])

替换成:

  1. if (callback(i, obj[i]) === false) {
  2. break;
  3. }

轻松实现中止循环的功能。

this

我们在实际的开发中,我们有时会在 callback 函数中用到 this,先举个不怎么恰当的例子:

  1. // 我们给每个人添加一个 age 属性,age 的值为 18 + index
  2. var person = [
  3. {name: 'kevin'},
  4. {name: 'daisy'}
  5. ]
  6. $.each(person, function(index, item){
  7. this.age = 18 + index;
  8. })
  9.  
  10. console.log(person)

这个时候,我们就希望 this 能指向当前遍历的元素,然后给每个元素添加 age 属性。

指定 this,我们可以使用 call 或者 apply,其实也很简单:

我们把:

  1. if (callback(i, obj[i]) === false) {
  2. break;
  3. }

替换成:

  1. if (callback.call(obj[i], i, obj[i]) === false) {
  2. break;
  3. }

关于 this,我们再举个常用的例子:

  1. $.each($("p"), function(){
  2. $(this).hover(function(){ ... });
  3. })

虽然我们经常会这样写:

  1. $("p").each(function(){
  2. $(this).hover(function(){ ... });
  3. })

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

回到第一种写法上,就是因为将 this 指向了当前 DOM 元素,我们才能使用 $(this)将当前 DOM 元素包装成 jQuery 对象,优雅的使用 hover 方法。

所以最终的 each 源码为:

  1. function each(obj, callback) {
  2. var length, i = 0;
  3.  
  4. if (isArrayLike(obj)) {
  5. length = obj.length;
  6. for (; i < length; i++) {
  7. if (callback.call(obj[i], i, obj[i]) === false) {
  8. break;
  9. }
  10. }
  11. } else {
  12. for (i in obj) {
  13. if (callback.call(obj[i], i, obj[i]) === false) {
  14. break;
  15. }
  16. }
  17. }
  18.  
  19. return obj;
  20. }

性能比较

我们在性能上比较下 for 循环和 each 函数:

  1. var arr = Array.from({length: 1000000}, (v, i) => i);
  2.  
  3. console.time('for')
  4. var i = 0;
  5. for (; i < arr.length; i++) {
  6. i += arr[i];
  7. }
  8. console.timeEnd('for')
  9.  
  10. console.time('each')
  11. var j = 0;
  12. $.each(arr, function(index, item){
  13. j += item;
  14. })
  15. console.timeEnd('each')

这里显示一次运算的结果:

从上图可以看出,for 循环的性能是明显好于 each 函数的,each 函数本质上也是用的 for 循环,到底是慢在了哪里呢?

我们再看一个例子:

  1. function each(obj, callback) {
  2. var i = 0;
  3. var length = obj.length
  4. for (; i < length; i++) {
  5. value = callback(i, obj[i]);
  6. }
  7. }
  8.  
  9. function eachWithCall(obj, callback) {
  10. var i = 0;
  11. var length = obj.length
  12. for (; i < length; i++) {
  13. value = callback.call(obj[i], i, obj[i]);
  14. }
  15. }
  16.  
  17. var arr = Array.from({length: 1000000}, (v, i) => i);
  18.  
  19. console.time('each')
  20. var i = 0;
  21. each(arr, function(index, item){
  22. i += item;
  23. })
  24. console.timeEnd('each')
  25.  
  26. console.time('eachWithCall')
  27. var j = 0;
  28. eachWithCall(arr, function(index, item){
  29. j += item;
  30. })
  31. console.timeEnd('eachWithCall')

这里显示一次运算的结果:

each 函数和 eachWithCall 函数唯一的区别就是 eachWithCall 调用了 call,从结果我们可以推测出,call 会导致性能损失,但也正是 call 的存在,我们才能将 this 指向循环中当前的元素

jQuery通用遍历方法each的实现的更多相关文章

  1. jQuery的遍历方法

    1.jQuery中的map使用方法 <!DOCTYPE html> <html> <head> <style>p { color:red; }</ ...

  2. JQuery 的遍历方法 $.each

    博主呢最近在公司实习,发现公司基本上都会统一代码风格,今天看到还有很多事用JQuery写的js 请求Ajax与后台进行数据交互的方式 当我看到$each 遍历时 然我想起我学JQuery的时候 于是复 ...

  3. jQuery使用(十一):jQuery实例遍历与索引

    each() children() index() 一.jQuery实例遍历方法each() jQuery实例上的each()方法规定要运行的函数,并且给函数传入两个参数:index,element. ...

  4. jquery中siblings方法配合什么方法一起使用

    siblings() 获得匹配集合中每个元素的同胞,通过选择器进行筛选是可选的.接下来通过本文给大家介绍jQuery siblings()用法实例详解,需要的朋友参考下吧 siblings() 获得匹 ...

  5. jQuery的end() 方法

    定义和用法 end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态. 语法 .end() 详细说明 大多数 jQuery 的遍历方法会操作一个 jQuery 对象实例,并生成一 ...

  6. JS数组与对象的遍历方法大全

    本文简单解析各种数组和对象属性的遍历方法: 原生for循环.for-in及forEach ES6 for-of方法遍历类数组集合 Object.key()返回键名的集合 jQuery的$.each() ...

  7. jQuery三——筛选方法、事件

    一.jquery常用筛选方法 以下为jquery的常用筛选方法: 代码示例如下: <!DOCTYPE html> <html lang="en"> < ...

  8. jQuery通用的全局遍历方法$.each()用法实例

    1.jQuery通用的全局遍历方法$.each()用法 2. test.json文件代码: 3. html代码 4.jQuery代码 <script src="jquery-1.3.1 ...

  9. Jquery中each的三种遍历方法

    Jquery中each的三种遍历方法 $.post("urladdr", { "data" : "data" }, function(dat ...

随机推荐

  1. 关于docker的scratch镜像与helloworld

    关于docker的scratch镜像与helloworld 参考:https://hub.docker.com/_/scratch?tab=description 参考:https://segment ...

  2. FineUI 模板列动态删除方法

    本来这是asp.net写法,跟fineui一点关系都没有,但是还是有人不会写不会查找.还是做个分享吧.    <f:TemplateField runat="server"  ...

  3. Tkint中Label&Button&Scale的使用

    top.geometry()设定窗口的初始大小 scale.set()设定滑块的初始值 scale.get()获取滑块变化的值 控件通过回调函数与其他控件进行通信(Label控件中的文本会受到Scal ...

  4. Windows10下QT5.13.2安装mingw64/MYSQL8.0驱动

    开始之前,先将编译器的路径添加到系统环境变量. 我的QT所以sql驱动是在下面这个目录中(大家在自己Qt的安装目录找到对应的文件夹就行,下面的路径也是如此), E:\qt\5.13.2\mingw73 ...

  5. pytest_命令行传参

    前言 命令行参数是根据命令行选项将不同的值传递给测试函数,比如平常在cmd执行"pytest --html=report.html",这里面的”--html=report.html ...

  6. win10下更新anaconda和pip源

    第一步:更新anaconda源. anaconda的官方源太慢,推荐清华源:https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/ 软件下载也可以在这个 ...

  7. 阿里巴巴 Java 开发手册 (十二)安全规约

    1. [强制]隶属于用户个人的页面或者功能必须进行权限控制校验. 说明:防止没有做水平权限校验就可随意访问.修改.删除别人的数据,比如查看他人的私信 内容.修改他人的订单. 2. [强制]用户敏感数据 ...

  8. C# vb .net实现装饰边框效果滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的装饰边框效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第 ...

  9. 在docker容器上如何实现代码的版本管理

    之前在一台centos7的虚拟机上部署了docker并运行了三个容器给开发写代码用,写代码肯定会涉及到版本控制管理. 开始建议是开发在容器中写代码,然后通过docker commit的方式将其保存为i ...

  10. mybatis 变更xml文件目录

    mybatis的xml默认读取的是resources目录,这个目录是可以变化的.我习惯于将mapper文件和xml放到一起或相邻目录下. 如图: 具体操作: 以mybatis-plus为例 boots ...