JavaScript中数组的迭代方法详解(一)

JavaScript中数组的迭代方法详解(一)

目前为止,学习了很多的数组方法,其中迭代方法就有很多,这些方法都很像,到底有什么区别?

1、for

最常用的循环, 虽然性能堪忧但可优化,基本程序代码如下。

var arr = ['a','b','c','d'];
for (var i = 0; i < arr.length; i = i + 1){
     //do something to each member
}

这段代码在每次循环都会获取一下数组的长度,理论上会降低代码的性能,尤其是当这个arr不是一个数组,而是一个HTMLCollection对象的时候。

优化如下:

//只定义一个变量即可
var arr = []; 
for(var i = arr.length - 1; i > -1; i--){ 
     //do something to each member
} 

为了区别for…in和for遍历数组的不同,用for举个栗子:

var arr = new Array();

arr[0] = "A0";
arr[2] = "C3";
arr[4] = "D5";

for (var i = 0, len = arr.length; i < len; i++){
    console.log(arr[i]);
}

循环结果:

用for会把数组中的每一项都循环出来,即使其中某一项没有定义也会用undefined表示。

2、for…in

for…in可以用于遍历数组和对象。

from MDN
for…in 循环只遍历可枚举属性。像 Array 和 Object 使用内置构造函数所创建的对象都会继承自 Object.prototype 和 String.prototype 的不可枚举属性,例如 String 的 indexOf() 方法或者 Object 的 toString 方法。循环将迭代对象的所有可枚举属性和从它的构造函数的 prototype 继承而来的(包括被覆盖的内建属性)。

说实在的看完上面的内容,不太明白什么意思,接着往下看。

//语法
for(var item in arr|obj){ //do something to each member }

用for…in 的循环方法完成 用for完成的栗子:

var arr = new Array();

arr[0] = "A0";
arr[2] = "C3";
arr[4] = "D5";

for (var i in arr){
    console.log(arr[i]);
}

循环结果:

for...in 循环数组

通过这个例子可以得出,for…in实际是遍历对象的可枚举属性。

再看个栗子:

var arr = [1, 2, 3];
arr.name = "Hello world";
var index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}

循环结果:

for...in 循环数组

这个例子看到for-in循环访问了新增的”name”属性,因为for-in遍历了对象的所有属性,而不仅仅是“索引”。同时需要注意的是,此处输出的索引值,即 “0”、 “1”、 “2”不是Number类型的,而是String类型的,因为其就是作为属性输出,而不是索引。

那是不是说不在Array对象中添加新的属性,就可以只输出数组中的内容了呢?

看栗子:

Array.prototype.fatherName = "Father";
var arr = [1, 2, 3];
arr.name = "Hello world";
var index;
for(index in arr) {
    console.log("arr[" + index + "] = " + arr[index]);
}

循环结果:

for...in 循环数组

从这个例子可以看到,答案是否定的。因为for-in不仅仅遍历array自身的属性,其还遍历array原型链上的所有可枚举的属性。

那是不是完全就不能用for…in循环数组呢?也不完全是,再看个例子:

var key;
var arr = [];
arr[0] = "a";
arr[100] = "b";
arr[10000] = "c";
for(key in arr) {
    if(arr.hasOwnProperty(key)  &&    
       /^0$|^[1-9]\d*$/.test(key) &&    
       key <= 4294967294               
       ) {
        console.log(arr[key]);
    }
}

循环结果:

for...in 循环数组

因为for-in只会遍历存在的实体,上面的例子中,for-in 遍历了3次(遍历属性分别为”0”、 “100”、 “10000”的元素,普通for循环则会遍历10001次)。所以,只要处理得当,for-in 在遍历Array中元素也能发挥巨大作用。

它的正确使用方式应该如下所示:

var obj = {a:1, b:2, c:3};
    
for (var prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

特点

  • “索引值”是String类型
  • 循环出的值不一定是按顺序的
  • 遍历对象可枚举的属性
  • 可遍历出对象原型中的属性

应用场景

通过上面的几个例子可以发现如果不是特殊情况,最好不用for-in遍历Array,其更适合遍历对象。

3、forEach

from MDN
forEach() 方法对数组的每个元素执行一次提供的函数(回调函数),那些已删除(使用 delete 方法等情况)或者从未赋值的项将被跳过(不包括那些值为 undefined 或 null 的项)。

//语法
[].forEach(function(value, index, array){
   // do some
})

callback参数
1. value(当前值)---数组中正在处理的当前元素
2. index(索引)  ---数组中正在处理的当前元素的索引
3. array        ---正在应用forEach()数组

来个例子

var array = [1, 2, 3];
delete array[1]; 
console.log(array);     //===>[1, undefined × 1, 3]

array.forEach(function(value){
    console.log(value);
})                     //===>1, 3

通过这个例子就能明白前MDN中说的那些已删除(使用 delete 方法等情况)或者从未赋值的项将被跳过(不包括那些值为 undefined 或 null 的项)。

var arr = [];
arr[0] = "a";
arr[3] = "b";
arr[10] = "c";
arr.name = "Hello world";
arr.forEach(function(data, index, array) {
    console.log(data, index, array);
});

这个例子看出:

  • 不用重新声明index和遍历的元素
  • index是Number 类型
  • forEach 不会遍历到数组原型链上的属性

一张图来说明forEach, 加深一下记忆:

特点

  • 数组中有几项,传进去的匿名函数就执行几次
  • forEach无法使用break,continue跳出循环,使用return时,效果和在for循环中使用continue一致

应用场景

在数组中,想让其中的每个元素都干同一件事的时候,就用forEach

4、map

这里的map不是“地图”的意思,而是指“映射”,也就是原数组被“映射”成对应新数组。

from MDN
map() 方法返回一个由原数组中的每个元素调用一个指定方法后的返回值组成的新数组。

map方法会给原数组中的每个元素都按顺序调用一次callback函数。callback每次执行后的返回值包括 undefined)组合起来形成一个新数组。callback函数只会在有值的索引上被调用;那些从来没被赋过值或者使用delete删除的索引则不会被调用。

//语法
[].map();

callback的参数:
1. 数组元素   ==>value
2. 元素索引   ==>index
3. 原数组本身 ==>array

[].map(function(value, index, array) {
    // ...
});

为了理解先上个例子:

Array(10).map(function(){return "A"})

上面的代码你觉的结果是什么?是不是10个A?

运行结果:

这个例子就理解了,map按照索引的升序,对数组里存在的每个元素调用一次callbackfn,并用结果构造一个新数组。callbackfn只被实际存在的数组元素调用;它不会被缺少的数组元素调用。

var arr = [1, 2, 3];
var newArr = arr.map(function(ele) {
    return "element" + ele;
});
console.log(newArr); ===>["element1", "element2", "element3"]
console.log(arr);    ===>[1, 2, 3]

特点:

  • map方法不改变原数组
  • map方法在遍历过程中进行修改,生成一个新的数组
  • map和forEach很相似,都是用来遍历数组中的每一项的值,区别是map的回调函数中支持return 返回值, 可 return 就意味着可以把这一项变成另外的值,却不会影响原数组,仅是克隆了一个。

增加一下记忆

最后符上一张性能测试结果图:

Js中几种常用数组遍历方式分析比较工具

从上面的性能测试图可以看出,for…in循环最慢。优化后的普通for循环最快

数组迭代性能从高到低依次是:for > forEach > for…of > map > for…in


剩下的几个新增的数组方法在二期继续介绍。