资讯专栏INFORMATION COLUMN

JavaScript数组迭代(遍历)方法

light / 1194人阅读

摘要:正文和中新增的的数组迭代方法如下其中,是新增的,其余都是新增的。指数组后,返回过滤后的新数组。它的参数跟方法是一样的所有数组成员依次执行回调函数,直到找出第一个返回值为的成员,然后返回该成员。

前言

ES5和ES6中新增了不少东西,对于数组而言,新增了不少迭代方法,让我们可以抛弃for循环,更方便的写JS代码。

正文

ES5和ES6中新增的的数组迭代方法如下:

forEach

map

filter

some

every

reduce / reduceRight

find / findIndex

其中,find / findIndex是ES6新增的,其余都是ES5新增的。所以这些方法对低版本IE都不兼容。
接下来我们看看这些方法如何使用并在低版本IE进行兼容。

forEach

forEach方法是这些方法里面最基本的一个方法,它的作用是对数组的每个元素执行一次提供的函数
例如:

var arr = [1, 2, 3];

arr.forEach(function (element, index, array) {
  console.log(element, index, array)
})

//output
1 0 [1, 2, 3]
2 1 [1, 2, 3]
3 2 [1, 2, 3]

forEach方法中的callback函数会被依次传入三个参数:

数组当前项的值

数组当前项的索引

数组对象本身

forEach方法还可以传入第二个参数,这个参数是可选的。如果给forEach传递了第二个参数,callback函数里的this将指向这个参数。如果没有传入第二个参数,则this指向全局对象(在浏览器是为window),严格模式下是undefined

var arr = [1, 2, 3];
var obj = {name: "zhang"};

arr.forEach(function (element, index, array) {
  console.log(element, index, array, this)
}, obj)

// output
1 0 [1, 2, 3] {name: "zhang"}
2 1 [1, 2, 3] {name: "zhang"}
3 2 [1, 2, 3] {name: "zhang"}

下面我们用forEach写一个稍显完整的例子了,数组求和:

var sum = 0;

[1, 2, 3].forEach(function (element, index, array) {
  console.log(array[index] == element); // true
  sum += item;
});

console.log(sum); // 6

最后我们对低版本IE进行一下拓展,是这个方法就有更好的兼容性,代码如下:

Array.prototype.forEach = Array.prototype.forEach || function(fn, context){
  for (var k = 0, length = this.length; k < length; k++) {
    if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) {
      fn.call(context, this[k], k, this);
    }
  }
}
map

map方法的作用就是将原数组按照一定的规则映射成一个新的数组。再将其返回,是返回一个新的数组,而不是将原数组直接改变。使用方法和参数都跟forEach相似。
下面是一个数值求平方的例子:

var data = [1, 2, 3];

var arrayOfSquares = data.map(function (element) {
  return element * element;
});

console.log(arrayOfSquares); //[1, 4, 9]

callback需要有return值,如果没有,就像下面这样:

var data = [1, 2, 3];

var arrayOfSquares = data.map(function (element) {
  element * element;
});

console.log(arrayOfSquares); // [undefined, undefined, undefined]

最后我们对map方法进行一下拓展:

Array.prototype.map = Array.prototype.map || function (fn, context) {
  var arr = [];
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {      
      arr.push(fn.call(context, this[k], k, this));
    }
  }
  return arr;
};
filter

filter为“过滤”、“筛选”的意思。指数组filter后,返回过滤后的新数组。用法和参数跟map差不多。
与map方法不同的是,filter方法的callback函数需要返回弱等于truefalse的值。如果为true,则通过,否则,不通过。
例如:

var arr = [0, 1, 2, 3];

var newArr = arr.filter(function (element, index, array) {
  return e;
})

var newArr2 = arr.filter(function (element, index, array) {
  return e>=2; 
})

console.log(newArr); // [1, 2, 3]
console.log(newArr2); // [2, 3]

下面是对filter方法的拓展:

Array.prototype.filter = Array.prototype.filter || function (fn, context) {
  var arr = [];
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      fn.call(context, this[k], k, this) && arr.push(this[k]);
    }
  }
  return arr;
};
some

some方法是只要数组中的某个值,符合你给定的判断条件就返回true;否则,返回false。用法和参数跟前面的方法一样。
例如:

function isBigEnough(element, index, array) {
  return element >= 4;
}
var passed = [1, 2, 3].some(isBigEnough);
var passed2 = [1, 2, 3, 4].some(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

下面是some方法的拓展:

Array.prototype.some = Array.prototype.some || function (fn, context) {
  var passed = false;
  if (typeof fn === "function") {
       for (var k = 0, length = this.length; k < length; k++) {
      if (passed === true) break;
      passed = !!fn.call(context, this[k], k, this);
    }
  }
  return passed;
};
every

every方法与some方法相对,every方法是数组中的所有值都符合你给定的判断条件的时候才会返回true,否则就返回false
例如:

function isBigEnough(element, index, array) {
  return element >= 3;
}
var passed = [2, 3, 4].every(isBigEnough);
var passed2 = [3, 4, 5].every(isBigEnough);

console.log(passed); // false
console.log(passed2); // true

every方法拓展如下:

Array.prototype.every = Array.prototype.every || function (fn, context) {
  var passed = true;
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (passed === false) break;
      passed = !!fn.call(context, this[k], k, this);
    }
  }
  return passed;
};
reduce / reduceRight

reduce / reduceRight 方法比上面的几种方法要复杂一些;它的语法如下:

array.reduce(callback,[initialValue])

其中callback可以依次接受四个参数:

accumulator上一次调用回调返回的值,或者是提供的初始值(initialValue

currentValue数组中正在处理的元素

currentIndex数组中正在处理的元素索引,如果提供了initialValue ,从0开始;否则从1开始。

array数组对象本身

reduce / reduceRight 方法中,第二个参数(initialValue)是可选的;其值用于第一次调用callback的第一个参数。
我们先来看一个例子:

var sum = [1, 2, 3].reduce(function(a, b) {
    return a + b;
});
console.log(sum); // 6

下面我们来看看reduce是如何运行的
例如执行下面这段代码:

var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
});
console.log(sum);

// output
0 1 1 [0, 1, 2, 3, 4]
1 2 2 [0, 1, 2, 3, 4]
3 3 3 [0, 1, 2, 3, 4]
6 4 4 [0, 1, 2, 3, 4]
10

从上面的输出结果可以看出callback被调用四次,每次调用的参数和返回值如下表:

callback accumulator currentValue currentIndex array return
第一次调用 0 1 1 [0, 1, 2, 3, 4] 1
第二次调用 1 2 2 [0, 1, 2, 3, 4] 3
第三次调用 3 3 3 [0, 1, 2, 3, 4] 6
第四次调用 6 4 4 [0, 1, 2, 3, 4] 10

上面是没有传入第二个参数(initialValue)的情况,那传入第二个参数又是怎么样的呢?

var sum = [0,1,2,3,4].reduce(function(accumulator, currentValue, currentIndex, array){
  console.log(accumulator, currentValue, currentIndex, array)
  return accumulator + currentValue;
}, 10);
console.log(sum);

// output
10 0 0 [0, 1, 2, 3, 4]
10 1 1 [0, 1, 2, 3, 4]
11 2 2 [0, 1, 2, 3, 4]
13 3 3 [0, 1, 2, 3, 4]
16 4 4 [0, 1, 2, 3, 4]
20

传入第二个参数后callback调用了五次,每次调用的参数和返回值如下表:

callback accumulator currentValue currentIndex array return
第一次调用 10 0 0 [0, 1, 2, 3, 4] 10
第二次调用 10 1 1 [0, 1, 2, 3, 4] 11
第三次调用 11 2 2 [0, 1, 2, 3, 4] 13
第四次调用 13 3 3 [0, 1, 2, 3, 4] 16
第五次调用 16 4 4 [0, 1, 2, 3, 4] 20

从上面的情况可以看出:不提供initialValue ,reduce方法会从索引1的地方开始执行callback方法,跳过第一个索引。提供 initialValue,从索引0开始。
同时,是否提供initialValue对于回调函数第一次执行时,accumulatorcurrentValue的取值有两种情况:调用reduce时提供initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;没有提供initialValueaccumulator取数组中的第一个值,currentValue取数组中的第二个值。

reduceRight与reduce类似,不同之处在于它是从最后一个值开始计算的。

那么我们该如何拓展一个reduce / reduceRight方法:

Array.prototype.reduce = Array.prototype.reduce || function (callback, initialValue ) {
  var previous = initialValue, k = 0, length = this.length;
  if (typeof initialValue === "undefined") {
    previous = this[0];
    k = 1;
  }
     
  if (typeof callback === "function") {
    for (k; k < length; k++) {
      this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
    }
  }
  return previous;
};


Array.prototype.reduceRight = Array.prototype.reduceRight || function (callback, initialValue ) {
    var length = this.length, k = length - 1, previous = initialValue;
    if (typeof initialValue === "undefined") {
        previous = this[length - 1];
        k--;
    }
    if (typeof callback === "function") {
       for (k; k > -1; k-=1) {          
          this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
       }
    }
    return previous;
  };
find / findIndex

find方法用于找出第一个符合条件的数组成员。它的参数跟forEach方法是一样的;所有数组成员依次执行回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。
例如:

var value = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 9;
});
var value2 = [1, 5, 10, 15].find(function(element, index, array) {
  return element > 20;
});

console.log(value); // 10
console.log(value2); // undefined

findIndex方法和find相似;不过它返回数组中符合条件的元素的索引。如果所有成员都不符合条件,则返回-1。

var value = [1, 5, 10, 15].findIndex(function(element, index, array) {
  return element > 9;
});
var value2 = [1, 5, 10, 15].findIndex(function(element, index, array) {
  return element > 20;
});

console.log(value); // 2
console.log(value2); // -1

对于不支持find / findIndex方法的浏览器,我们可以自己实现一个:

Array.prototype.find = Array.prototype.find || function (fn, context) {
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (fn.call(context, this[k], k, this)) {
        return this[k];
      }
    }
  }
  return undefined;
};


Array.prototype.findIndex = Array.prototype.findIndex || function (fn, context) {
  if (typeof fn === "function") {
    for (var k = 0, length = this.length; k < length; k++) {
      if (fn.call(context, this[k], k, this)) {
        return k;
      }
    }
  }
  return -1;
};
最后

上面的兼容实现不知道对不对,欢迎大家指正。
参考资料:
https://developer.mozilla.org...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83680.html

相关文章

  • JavaScript 设计模式(五):迭代器模式

    摘要:文章内容分两部分前半部分为迭代器模式概念后半部分为中迭代器上半部分开始迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。下半部分开始的迭代器迭代器等同于遍历器。执行该函数,会返回一个遍历器对象。 showImg(https://segmentfault.com/img/bVbuyaZ?w=800&h=600); 文章内容分两部分: 前半部分为 迭...

    zhou_you 评论0 收藏0
  • JavaScript数组遍历迭代方法介绍

    摘要:中一共有五种数组遍历迭代方法,它们都会对数组中每个元素执行一些业务,且都不会修改原数组,这些方法包括如果该函数任意一项返回,则返回,如果全部返回则最终返回如果该函数每一项都返回,则返回,否则返回会返回一个新数组,该数组是由满足条件的任意项组 ES5中一共有五种数组遍历(迭代)方法,它们都会对数组中每个元素执行一些业务,且都不会修改原数组,这些方法包括: 1、some() 如果该函数任意...

    Pikachu 评论0 收藏0
  • ES6语法之可迭代协议和迭代器协议

    摘要:有两个协议可迭代协议和迭代器协议。为了变成可迭代对象,一个对象必须实现或者它原型链的某个对象必须有一个名字是的属性迭代器协议该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。 ECMAScript 2015的几个补充,并不是新的内置或语法,而是协议。这些协议可以被任何遵循某些约定的对象来实现。有两个协议:可迭代协议和迭代器协议。 可迭代协议 可迭代协议允许 JavaScri...

    sarva 评论0 收藏0
  • JavaScript对象遍历

    摘要:简单说就是一个有一个函数,这个函数执行的返回值一定是一个对象,对象有两个属性标记迭代是否结束,标记这次迭代的结果值。 引言 遍历对象是平常工作中很常见的一个操作,几乎是日常操作,但是遍历对象真的是一件很容易的事情么,显然不是的。 常用的方式 for...in for (variable in object) {...} 这个是一个很常见的用法,相信每个人顺手都可以写出来。但是这里需要主...

    Seay 评论0 收藏0
  • [前端漫谈_1] 从 for of 聊到 Generator

    摘要:数据的层级意味着迭代数据结构并提取它的数据。对于技术人而言技是单兵作战能力,术则是运用能力的方法。在前端娱乐圈,我想成为一名出色的人民艺术家。 聊聊 for of 说起 for of 相信每个写过 JavaScript 的人都用过 for of ,平时我们用它做什么呢?大多数情况应该就是遍历数组了,当然,更多时候,我们也会用 map() 或者 filer() 来遍历一个数组。 但是就...

    miqt 评论0 收藏0
  • JavaScript iterator 设计模式

    摘要:迭代器模式就是按照顺序访问一个对象中元素,而不用暴露该对象的内部组成。迭代器模式就是将这个迭代实现从业务中分离出来。外部迭代器外部迭代器必须显式地请求才会迭代下一个元素。 迭代器模式就是按照顺序访问一个对象中元素,而不用暴露该对象的内部组成。迭代器模式就是将这个迭代实现从业务中分离出来。 但实际开发中我们并不将他当成一个设计模式。 前瞻后顾 说起迭代器,想必对ES6有了解的同学应该不会...

    CocoaChina 评论0 收藏0

发表评论

0条评论

light

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<