js数组之indexOf/filter/forEach/map/reduce详解

编辑注:这篇文章讲到数组的5个使用方法,目前我比较常用的有indexOf和forEach,其它则很少见,这些属性熟记于心能够给你平时的编码带来意想不到的方便,有点可惜的是在IE9以下都不支持这些方法,不过如果你是在移动端和现代浏览器上则不需要考虑这些兼容,另外微信小程序也支持这种写法。

在ES5中,一共有9个Array方法 http://kangax.github.io/compat-table/es5/

注* 九个方法

Array.prototype.indexOf
Array.prototype.lastIndexOf
Array.prototype.every
Array.prototype.some
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.reduceRight

我将挑选5种方法,我个人认为是最有用的,很多开发者都会碰到。

1) indexOf

indexOf()方法返回在该数组中第一个找到的元素位置,如果它不存在则返回-1。

不使用indexOf时

var arr = ['apple','orange','pear'],
found = false;

for(var i= 0, l = arr.length; i< l; i++){
if(arr[i] === 'orange'){
found = true;
}
}

console.log("found:",found);

使用后

var arr = ['apple','orange','pear'];

console.log("found:", arr.indexOf("orange") != -1);

2) filter

该filter()方法创建一个新的匹配过滤条件的数组。

不用 filter() 时

var arr = [
{"name":"apple", "count": 2},
{"name":"orange", "count": 5},
{"name":"pear", "count": 3},
{"name":"orange", "count": 16},
];

var newArr = [];

for(var i= 0, l = arr.length; i< l; i++){
if(arr[i].name === "orange" ){
newArr.push(arr[i]);
}
}

console.log("Filter results:",newArr);

用了 filter():

var arr = [
{"name":"apple", "count": 2},
{"name":"orange", "count": 5},
{"name":"pear", "count": 3},
{"name":"orange", "count": 16},
];

var newArr = arr.filter(function(item){
return item.name === "orange";
});


console.log("Filter results:",newArr);

3) forEach()

forEach为每个元素执行对应的方法

var arr = [1,2,3,4,5,6,7,8];

// Uses the usual "for" loop to iterate
for(var i= 0, l = arr.length; i< l; i++){
console.log(arr[i]);
}

console.log("========================");

//Uses forEach to iterate
arr.forEach(function(item,index){
console.log(item);
});

forEach是用来替换for循环的

4) map()

map()对数组的每个元素进行一定操作(映射)后,会返回一个新的数组,

不使用map

var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];

function getNewArr(){

var newArr = [];

for(var i= 0, l = oldArr.length; i< l; i++){
var item = oldArr[i];
item.full_name = [item.first_name,item.last_name].join(" ");
newArr[i] = item;
}

return newArr;
}

console.log(getNewArr());

使用map后

var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}];

function getNewArr(){

return oldArr.map(function(item,index){
item.full_name = [item.first_name,item.last_name].join(" ");
return item;
});

}

console.log(getNewArr());

map()是处理服务器返回数据时是一个非常实用的函数。

5) reduce()

reduce()可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。

说实话刚开始理解这句话有点难度,它太抽象了。

场景: 统计一个数组中有多少个不重复的单词

不使用reduce时

var arr = ["apple","orange","apple","orange","pear","orange"];

function getWordCnt(){
var obj = {};

for(var i= 0, l = arr.length; i< l; i++){
var item = arr[i];
obj[item] = (obj[item] +1 ) || 1;
}

return obj;
}

console.log(getWordCnt());

使用reduce()后

var arr = ["apple","orange","apple","orange","pear","orange"];

function getWordCnt(){
return arr.reduce(function(prev,next){
prev[next] = (prev[next] + 1) || 1;
return prev;
},{});
}

console.log(getWordCnt());

让我先解释一下我自己对reduce的理解。reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)。假设函数它有个传入参数,prev和next,index和array。prev和next你是必须要了解的。

一般来讲prev是从数组中第一个元素开始的,next是第二个元素。但是当你传入初始值(initialValue)后,第一个prev将是initivalValue,next将是数组中的第一个元素。

比如:

/*
* 二者的区别,在console中运行一下即可知晓
*/

var arr = ["apple","orange"];

function noPassValue(){
return arr.reduce(function(prev,next){
console.log("prev:",prev);
console.log("next:",next);

return prev + " " +next;
});
}
function passValue(){
return arr.reduce(function(prev,next){
console.log("prev:",prev);
console.log("next:",next);

prev[next] = 1;
return prev;
},{});
}

console.log("No Additional parameter:",noPassValue());
console.log("----------------");
console.log("With {} as an additional parameter:",passValue());

原文地址: colintoh.com

如何兼容IE浏览器,看看这篇:让ie以及老版本w3c浏览器 也支持ES5的 数组对象的 几个新增方法.

写了简单注释. 具体用法 请参考 ES5 手册 .

// 模拟ES5 Array.prototype.forEach
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(f, oThis) {
if (!f || f.constructor != Function.toString()) return;
oThis = oThis || window;
for (var i = 0, len = this.length; i < len; i++) {
f.call(oThis, this[i], i, this); //p1 上下文环境 p2 数组元素 p3 索引 p4 数组对象
}
}
}
//模拟 ES5 Array.prototype.filter
if (!Array.prototype.filter) {
Array.prototype.filter = function(f, oThis) {
if (!f || f.constructor != Function.toString()) return;
oThis = oThis || window;
var a = [];
for (var i = 0, len = this.length; i < len; i++) {
if (f.call(oThis, this[i], i, this)) a.push(this[i]);
}
return a;
}
}
//模拟 ES5 Array.prototype.map
if (!Array.prototype.map) {
Array.prototype.map = function(f, oThis) {
if (!f || f.constructor != Function.toString()) return;
oThis = oThis || window;
var a = [];
for (var i = 0, len = this.length; i < len; i++) {
a.push(f.call(oThis, this[i], i, this));
}
return a;
}
}
//模拟 ES5 Array.prototype.every
if (!Array.prototype.every) {
Array.prototype.every = function(f, oThis) {
if (!f || f.constructor != Function.toString()) return;
oThis = oThis || window;
for (var i = 0, len = this.length; i < len; i++) {
if (!f.call(oThis, this[i], i, this)) return false;
}
return true;
}
}
//模拟 ES5 Array.prototype.some
if (!Array.prototype.some) {
Array.prototype.some = function(f, oThis) {
if (!f || f.constructor != Function.toString()) return;
oThis = oThis || window;
for (var i = 0, len = this.length; i < len; i++) {
if (f.call(oThis, this[i], i, this)) return true;
}
return false;
}
}

要重点说一说的 indexOf lastIndexOf 两个方法 .. 我只是实现了一个简单版本.但修复了 一点点小问题 . 先看代码

//模拟 ES5 Array.prototype.indexOf方法.并修复ff等其他实现 indexOf方法的浏览器中值类型于引用类型比较相等性一律返回false问题
Array.prototype.indexOf = function(obj) {
for (var i = 0, len = this.length; i < len; i++) {
if (compare(this[i], obj)) return i;
}
return -1;
}
//模拟 ES5 Array.prototype.lastIndexOf方法.并修复ff等其他实现 indexOf方法的浏览器中值类型于引用类型比较相等性一律返回false问题
Array.prototype.lastIndexOf = function(obj) {
for (var i = this.length - 1; i >= 0; i--) {
if (compare(this[i], obj)) return i;
}
return -1;
}

是的. 注释已经写的明白.  我这里之所以 用这个方法 覆盖了 ES5 的方法原因在于

//比较对象是否于参数obj 相等..
function compare(obj1, obj2) {
if (obj1 == null || obj2 == null) return (obj1 === obj2);
return (obj1 == obj2 && obj1.constructor.toString() == obj2.constructor);
}

这个 compare方法 . 他解决了一个什么问题呢? 对了! 就是相等性判断. 因为我发现  原始版本的相等性判断 居然会认为

1!=new Number(1)  即 假如数组中存在一个 number对象 而我用 number 直接量去做比较 即使本应该相等. 也会返回false

这于 javascript  引用类型 于值类型做 相等性运算时  会调用引用类型 即对象的 valueOf方法 然后再去做比较 是相违背的.

说明 javascript 1.6 在实现 indexOf方法时 相等性判断 他简单的用了 === 即严格相等判断 …

那么我写的compare 方法 的作用即 解决这个问题. 让相等性判断 遵循 javascript的原始规则…

说到这里 不得不提一下  老外 在判断 某个对象 为某特定类型时. 会使用 Object.porototyp.toString.call(this) 来做比较. 目的是为了防止

如 [1,2,3].constructor!=iframe.contentWindow.Array 这类情况…

其实大可不必那么麻烦 .  我们只需要调用 两边的构造器对象 的其中一个的toString方法 返回一个 值类型的 string 伪对象即可.

这样 另外一边的引用类型 也会 自动调用 valueOf方法 于字符串做比较.. 何必 搞的那么麻烦呢?

 

最后. 恩 总体来说 我非常喜欢 forEach  map filter 等等方法 . 好用的很. 由于其他几个方法 都是类似的. 所以 一并写出来了!


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流