JavaScript中几种遍历方法浅析
前端入门菜鸟,对于平常用的几种遍历方法的掌握有点模糊,空闲之余总结一下遍历方法,将几种遍历方法进行区分并掌握。
1. for循环
- 可以中断循环
- 原数据会被改变
- 对象和数组都使用,伪数组(集合)也适用
for(var i = 0; i < array.length; i++){
//return; // 函数执行被终止
//break; // 循环被终止
continue; // 循环被跳过
}
关于for循环,有以下几点需要注意
- for循环中的 i 在循环结束之后任然存在与作用域中,为了避免影响作用域中的其他变量,可使用函数自执行的方式(匿名函数)将其隔离起来;
- 尽量避免 for( var i = 0; i < array.length; i++) 的方式,这样的数组长度每次都被计算,效率低于上面的方式。可以使用for(var i = 0, len = array.length ; i < len; i++) 的方式,提高代码的利用率
2. for…in 遍历
适用于数组和对象(大部分情况下用来遍历对象)
- 遍历数组时,i 代表索引值
- 遍历对象时,i 代表 key 值,(遍历不到Symbol类型属性)
- 也可遍历集合(基本不用),i 代表索引值
var array = [1, 2, 3, 4]
for(var i in array){
console.log(i)
}
注意点:
- 遍历数组时,i 值的类型为String类型,不是Number类型,使用此方法进行数组元素计算时,需要转换类型
- 如果在数组原型对象上有方法,此时for…in方法不适用,比如:
Array.prototype.show = function(value){//函数已被简化
return right;
}
var array = [1, 2, 3, 4];
for(var i in array){
console.log(array[i]);
}
这段代码就是在上边的代码的基础上,为Array做了一下扩充。很简单,只是添加了一个用于搜索的函数(这个函数已被我简化成和上边那样)。但是我们来看运行会出现什么情况:
1
2
3
4
f (value){//函数已被简化
return right;
}
输出的结果中,多出了一行,这一行是一个函数,不是我们定义在数组中的值。到此这个问题就出来了, 对于使用for…in可能出现的错误,有两种方式可避免:
- 不使用for…in,老老实实使用普通for循环形式。
- 在for-in循环中增加一个hasOwnProperty的判断,如下:
Array.prototype.show = function(value){//函数已被简化
return right;
}
var array = [1, 2, 3, 4];
for(var i in array){
if(!array.hasOwnProperty(i)){
continue
}
console.log(array[i]);
}
控制台输出结果为:
1
2
3
4
总之,for-in是被设计来遍历普通的“键值对”对象的,不适用于数组上
3. forEach 遍历
- 只能用来遍历数组,其他类型(对象,集合)均不可以
- 代码执行效率比for循环快
- 不改变原数组
- 遍历不能停止,即不能使用break,continue跳出循环,或者使用return结束
var arr=[9, 8, 7, 6, 5, 4]
arr.forEach(function(item,index,arr){
console.log(value);//9 8 7 6 5 4
})
参数 | 描述 |
---|---|
item | 必选,当前循环元素 |
index | 可选,当前元素索引 |
arr | 可选,当前元素所属的数组对象 |
4. for…of 遍历
- 可遍历数组和类数组
- 不可遍历对象
- 可以使用break,continue和return
- 遍历循环的是数组中的每一项元素
var arr = ['kobe','james','durant','curry'];
arr.name = "数组";
for(var key in arr){
console.log(key +': ' + arr[key]);
}
console.log('-----------分割线-----------');
for(var item of arr){
console.log(item);
}
输出结果为:
上图对于for…in 和 for…each作了简单的对比, 对于数组中新添加的name属性, for…in遍历了出来,而for…of不会遍历出来,算是使用过程中需要注意的一个点吧
另: ES6 为字符串添加了遍历器接口,使得字符串可以被for…of循环遍历
for (let codePoint of 'foo') {
console.log(codePoint)
}
// "f"
// "o"
// "o"
5. 明显减少代码量的一些高级遍历方法如下所示:
reduce() 、reduceRight() 、filter() 、map() 、every() 、some()
5.1 reduce() 和 reduceRight()
语法:
arr.reduce(callback,[initialValue])
callback (执行数组中每个值的函数,包含四个参数)
1、previousValue (必选,上一次调用回调返回的值,或者是提供的初始值(initialValue))
2、currentValue (虚悬,数组中当前被处理的元素)
3、index (可选,当前元素在数组中的索引)
4、array (可选,调用 reduce 的数组)
initialValue (作为第一次调用 callback 的第一个参数。)
注意: 如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function(prev, cur, index, arr) {
console.log(prev, cur, index);
return prev + cur;
})
console.log(arr, sum);
打印结果:
reduce是从左到右处理(从第一个成员到最后一个成员),reduceRight则是从右到左(从最后一个成员到第一个成员)
5.2 map() 和 filter()
map() 和 filter()两种方法的语法极为相似,语法结构均为
array.map(callback, [thisObject]);
array.filter(callback, [thisObject]);
callback接收三个参数
1. item (必选, 当前元素的值)
2. index (可选,当前元素的索引值)
3. arr (可选,当前元素属于的数组对象)
thisObject (可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值。
如果省略了thisValue,或者传入 null、undefined,那么回调函数的 this 为
全局对象)
filter方法用于过滤数组成员,满足条件的成员组成一个新数组返回。它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组
map方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回,不会改变原数组
[1, 2, 3].map(function(item, index, arr) {
return item * index;
});
// [0, 2, 6]
[1, 2, 3, 4, 5].filter(function (item, index, arr) {
return index % 2 === 0;
}); // [1, 3, 5]
注意点:map(),foreach(),filter()循环的共同之处:
1. foreach,map,filter循环中途是无法停止的,总是会将所有成员遍历完。
2. 他们都可以接受第二个参数,用来绑定回调函数内部的this变量,将回调函数内部的this对象,指向第二个参数,间接操作这个参数(一般是数组)
不同之处:
forEach循环没有返回值;map,filter循环有返回值
5.3 some() 和 every()
这两种一般用来循环遍历,统计数组是否满足某个条件
- 遍历数组;
- 三个参数分别是item,index,arr(当前元素,索引,数组本身);
- 返回的都是布尔值
它们接受一个函数作为参数,所有数组成员依次执行该函数。该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值。
some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false
var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >= 3;
});
// true
而every方法则相反,所有成员的返回值都是true,整个every方法才返回true,否则返回false。两相比较,some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false.
var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >= 3;
});
// false
总结: some()只要有一个是true,便返回true;而every()只要有一个是false,便返回false.
这两种方法在实际开发时,实用性很强,比如一些复选框之类的操作,大大减少代码量
6. Object.key() 和 Object.getOwnPropertyNames()
Object.keys() 方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继承的)所有属性名,且只返回可枚举的属性。
var obj = {
p1: 123,
p2: 456
};
Object.keys(obj) // ["p1", "p2"]
Object.getOwnPropertyNames() 方法与Object.keys() 类似,也是接受一个对象作为参数,返回一个数组,包含了该对象自身的所有属性名。但它能返回不可枚举的属性。
var a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
上面代码中,数组的length属性是不可枚举的属性,所以只出现在Object.getOwnPropertyNames方法的返回结果中。
由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。
var obj = {
p1: 123,
p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2