虽说已经看了n遍相关博文,但在实际操作中各种循环的操作实在是令人摸不着头脑,系统整理一回【拍桌】
简单 for 循环一般只适用于数组,最为常见的一种写法:
var arr = [1, 2, 3];
for (var i = 0, len = arr.length; i < len; i++) {...}
在数组长度不变时,用len
保存数组长度进行循环效率更佳。
另外,如需在遍历中进行改变长度的删除操作,一般采用倒序遍历:
var arr = [1, 2, 3];
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] === 1)
arr.splice(i, 1);
}
console.log(arr); // [2, 3]
for (key in object) {...}
key
:每次迭代时,将不同的属性名分配给变量
object
:被迭代枚举其属性的对象
首先咱的确可以用 for-in 来遍历数组
var arr = [1, 2, 3];
for (index in arr) {
console.log(`arr[${index}]: ${arr[index]}`);
}
// chrome中的输出结果
// arr[0]: 1
// arr[1]: 2
// arr[2]: 3
但是注意,for-in 遍历的是 对象以及其原型链上可枚举属性(String
),而不是数组的索引(Number
),且迭代顺序依赖于执行环境,并不一定按某种顺序访问元素,数组在js中也是一个对象,属性并非是Number
类型而是String
类型。length
属性值未被遍历也只是因为它不是可枚举属性。
因此我们做出一点魔改:
var arr = [1, 2, 3];
arr.arrFeeling = 'fucked up';
Array.prototype.arrayFeeling = 'thats good';
Object.prototype.objFeeling = 'emmmmm';
for (key in arr) {
console.log(`${key + 1}: ${arr[key]}`);
}
// chrome中的输出结果
// 01: 1
// 11: 2
// 21: 3
// arrFeeling1: fucked up
// arrayFeeling1: thats good
// objFeeling1: emmmmm
可以看出,这里的结果已经他喵的和我们预想的数组遍历差了很多很多【震惊】,因此从某种意义上讲, for-in 遍历的初衷是遍历对象中的属性,传统的数组遍历并不适合用for-in。
但是也正得益于这个特性,for-in遍历可用于遍历稀疏数组:
var arr = [];
arr[999] = 1;
for (key in arr) {
console.log(`${key}: ${arr[key]}`);
}
// chrome中的输出
// 999: 1
此处的 arr.length
为1000,如果用简单for循环来输出,会遍历1000次(输出999个undefined
),而使用for-in只会遍历一次。如果担心输出原型链上的属性,可以利用hasOwnProperty
方法。
简单的例子:
var arr = [null, 2, 3];
arr[5] = 5;
arr['die'] = 'die!' // 不会被遍历到
arr.forEach(function(data) {
console.log(data);
});
// null
// 2
// 3
// 5
forEach 是Array的方法,会为数组中含有效值(也就是说,会跳过数组空位)(包括值为null)的每一索引项执行一次传入的回调函数。回调函数会被依次传入三个参数:
需要注意的是,forEach 不会在迭代前创建数组的副本,因此可以改变原数组,若迭代时数组长度改变:
var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
console.log(word);
if (word === "two") {
words.shift();
}
});
// one
// two
// four
forEach 的一大缺点:一旦开始就无法跳出,因此ES5中提供了一些其他类似的方法:
简单的例子:
var arr = [1, 2, 3];
for (let val of arr) {
console.log(val);
}
// 1
// 2
// 3
for-of 乍看之下和forEach的功能好像重叠了,但是作为ES6新支持的语法,自然有一些优点:
Array
, Map
, Set
, String
, TypedArray
,arguments 对象等等,注意不包括Object对象,如果你想迭代Object对象,for-in循环更加适合此外,for-of与上面一大串forEach, map等有一个很大的区别:不会跳过数组空位:
let arr = ['holy', 'shit'];
arr.forEach(x => console.log(x));
// holy
// shit
for (let x of arr) {
console.log(x);
}
// holy
// undefined
// shit
最后回头看一下,循环遍历这个操作其实需要考虑很多:
undefined
,原型链)以及参数类型(Number
, String
)因此弄清楚他们之间的分别还是有必要的吧科科。ES6还添加了个叫iterable的玩意儿,也与迭代密切相关,不是很懂先坑了【逃】