介绍

jq 有一个 each 方法,是一个通用遍历方法。可用于遍历对象和数组。

语法

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

callback 有两个参数:第一个是对象的属性名或者是数组的索引,第二个为对应的值。

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

ES5 提供的 forEach 方法不能跳出循环,但 jq 的 each 方法可以。

  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. // Item #0: 0
  6. // Item #1: 1
  7. // Item #2: 2

实现 each

根据参数类型的不同选择不用的循环语句,数组就调用 for 循环,对象就调用 for in 循环。对于类数组对象也选择 for 循环。

可以利用在 《javascript 类型判断(plainObject …》中讲到的 isArrayLike 方法。

  1. function each(obj, callback) {
  2. let length, i = 0;
  3. // 类数组对象就使用 for 循环
  4. if (isArrayLike(obj)) {
  5. length = obj.length;
  6. for (; i < length; i++) {
  7. callback(i, obj[i]);
  8. }
  9. } else {
  10. // 对象使用 for in 循环
  11. for (i in obj) {
  12. if (obj.hasOwnProperty(i)) {
  13. callback(i, obj[i]);
  14. }
  15. }
  16. }
  17. return obj;
  18. }

支持终止循环

  1. callback(i, obj[i])

替换成

  1. if (callback(i, obj[i]) === false) break;

支持 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. })

  1. if (callback(i, obj[i]) === false) break;

替换为

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

最终代码

  1. function each(obj, callback) {
  2. let length, i = 0;
  3. // 类数组对象就使用 for 循环
  4. if (isArrayLike(obj)) {
  5. length = obj.length;
  6. for (; i < length; i++) {
  7. if(callback.call(obj[i], i, obj[i]) === false) break;
  8. }
  9. } else {
  10. // 对象使用 for in 循环
  11. for (i in obj) {
  12. if (obj.hasOwnProperty(i)) {
  13. if(callback.call(obj[i], i, obj[i]) === false) break;
  14. }
  15. }
  16. }
  17. return obj;
  18. }

性能比较

  1. const arr = Array.from({length: 1000000}, (v, i) => i);
  2. console.time("for");
  3. let i = 0, valueOne = 0;
  4. for (; i < arr.length; i++) {
  5. valueOne += arr[i];
  6. }
  7. console.timeEnd("for");
  8. console.time("each");
  9. let valueTwo = 0;
  10. each(arr, function (index, item) {
  11. valueTwo += item;
  12. });
  13. console.timeEnd("each");

结果

image.png

我们都知道 each 也是使用 for 循环,但为什么时间会差这么多了?它们的唯一区别是有没有使用 call 方法,可见 call 方法是造成性能损失的原因。

参考:
[1] JavaScript专题之jQuery通用遍历方法each的实现
[2] forEach
[3] for in