Jamey's Jamey's
首页
导航站
  • 学习专栏

    • 《HTML》笔记
    • 《CSS》笔记
    • 《JavaScript》笔记
    • 《Vue》笔记
    • 《Git》笔记
    • 《规范》笔记
    • 《软技能》笔记
    • 《面试》笔记
    • 《持续集成&交付&部署》笔记
  • 踩坑专栏

    • 《Element-UI 实践系列》笔记
    • 《移动端 实践系列》笔记
    • 《综合》笔记
  • 配置专栏

    • 《环境系列》笔记
  • 极空间

    • Docker
  • 影视

    • movie
  • 编辑器笔记

    • 开发编辑器
  • 浏览器笔记

    • Chrome
  • Mac笔记

    • Mac
  • 跨界学习

    • 运营
  • 破解合集

    • 破解
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于

Jamey

首页
导航站
  • 学习专栏

    • 《HTML》笔记
    • 《CSS》笔记
    • 《JavaScript》笔记
    • 《Vue》笔记
    • 《Git》笔记
    • 《规范》笔记
    • 《软技能》笔记
    • 《面试》笔记
    • 《持续集成&交付&部署》笔记
  • 踩坑专栏

    • 《Element-UI 实践系列》笔记
    • 《移动端 实践系列》笔记
    • 《综合》笔记
  • 配置专栏

    • 《环境系列》笔记
  • 极空间

    • Docker
  • 影视

    • movie
  • 编辑器笔记

    • 开发编辑器
  • 浏览器笔记

    • Chrome
  • Mac笔记

    • Mac
  • 跨界学习

    • 运营
  • 破解合集

    • 破解
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于
  • 深入系列

  • 专题系列

    • 写在前面
    • JavaScript 专题之跟着 underscore 学防抖
    • JavaScript 专题之跟着 underscore 学节流
    • JavaScript 专题之数组去重
    • JavaScript 专题之类型判断(上)
    • JavaScript 专题之类型判断(下)
    • JavaScript 专题之深浅拷贝
    • JavaScript 专题之从零实现 jQuery 的 extend
    • JavaScript 专题之如何求数组的最大值和最小值
    • JavaScript 专题之数组扁平化
    • JavaScript 专题之学 underscore 在数组中查找指定元素
    • JavaScript 专题之 jQuery 通用遍历方法 each 的实现
      • 一. each 介绍
      • 二. 退出循环
      • 三. 第一版
      • 四. 中止循环
      • 五. this
      • 六. 性能比较
    • JavaScript 专题之如何判断两个对象相等
    • JavaScript 专题之函数柯里化
    • JavaScript 专题之偏函数
    • JavaScript 专题之惰性函数
    • JavaScript 专题之函数组合
    • JavaScript 专题之函数记忆
    • JavaScript 专题之递归
    • JavaScript 专题之乱序
    • JavaScript 专题之解读 v8 排序源码
  • underscore系列

  • ES6系列

  • 模块化

  • 正则表达式

  • 单元测试

  • 微前端

  • 实用函数

  • Rollup

  • 解决方案

  • 《JavaScript》笔记
  • 专题系列
Jamey
2021-10-29
目录

JavaScript 专题之 jQuery 通用遍历方法 each 的实现

# JavaScript 专题之 jQuery 通用遍历方法 each 的实现

# 一. each 介绍

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

语法为:

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

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

// 遍历数组
$.each( [0,1,2], function(i, n){
  console.log( "Item #" + i + ": " + n );
});

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

# 二. 退出循环

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

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

// Item #0: 0
// Item #1: 1
// Item #2: 2
1
2
3
4
5
6
7
8

# 三. 第一版

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

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

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

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

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

// 第一版
function each(obj, callback) {
  var length, i = 0;

  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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 四. 中止循环

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

我们只用把:

callback(i, obj[i]);
1

替换成:

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

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

# 五. this

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

// 我们给每个人添加一个 age 属性,age 的值为 18 + index
var person = [
  {name: 'kevin'},
  {name: 'daisy'}
]
$.each(person, function(index, item){
  this.age = 18 + index;
})

console.log(person)
1
2
3
4
5
6
7
8
9
10

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

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

我们把:

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

替换成:

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

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

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

虽然我们经常会这样写:

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

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

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

所以最终的 each 源码为:

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
20

# 六. 性能比较

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

var arr = Array.from({length: 1000000}, (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');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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

javascript_03-18_01

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

我们再看一个例子:

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]);
  }
}

var arr = Array.from({length: 1000000}, (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');
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

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

javascript_03-18_02

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

有舍有得吧。

#JavaScript 专题
上次更新: 2022/07/01, 17:34:19
JavaScript 专题之学 underscore 在数组中查找指定元素
JavaScript 专题之如何判断两个对象相等

← JavaScript 专题之学 underscore 在数组中查找指定元素 JavaScript 专题之如何判断两个对象相等→

Theme by Vdoing | Copyright © 2017-2023 Jamey | blog 闽ICP备19022664号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式