我们经常会用到循环,截止到ES6,我们总共有4种语法用于循环,但是它们各自的功能和使用场景却有很大区别。接下来我们讲对这4种语法一一讲解。首先我们来看一下有哪4种:
1: for(let i = 0; i < number; i ++){...}
2: for(let key in object){...}
3: for(let value of iterable){...}
4: forEach(){...}
接下来我们一个一个地看:
1: for()
语法:
for(let i = 0; i < number, i ++){...};
for最简单,这里不作过多讲解。
2: for...in
语法:
for(let key in object){...}
for...in以任意顺序来遍历一个对象除了Symbole类型外的可枚举属性。
这简单的一句话传达了太多信息,有如下几点:
1: 它的遍历是无序的
2: 它遍历的是对象的属性名,而不是属性对应的值
3: 它只遍历可枚举属性
4: 在可枚举属性里面,不遍历Symbol类型
还有一点需要注意的是,
5: for...in不仅会遍历自己的属性,也会遍历继承的属性。
关于以上几个小点我们先来做一下知识的延伸,以便能更好地理解:
什么是无序遍历?
无序遍历是不能保证输出的数据顺序和放入的数据顺序是一样的,所以有可能一样,也有可能不一样。
什么样的属性是可枚举的?
首先可枚举属性是指那些内部 “可枚举(enumerable)” 标志设置为 true 的属性。
数据的枚举性遵循一下2条规律:
1: 直接通过赋值和属性初始化的属性,默认enumerable == true,是可枚举的
2: 通过Object.defineProperty定义的属性,默认enumerable == false,则不可枚举;如果想要可枚举,需要手动改enumerable:true
for...in的代码例子:
let obj = {name: 'nana', age: 20};
for(let key in obj){
console.log(`${key}`); // name, age
}
在for...in中对对象属性进行修改会怎样?
在MDN的页面上明确指出,我们在for...in的循环中,最好不要对属性进行添加,删除(通过delete),修改(通过Object.defineProperty)操作,因为在循环中进行这些操作都无法保证得到一个确定的结果。
不要把for...in用于Array
在可迭代对象一文了解到,数组可以看作是以下标为key的对象;我们现在又知道了for...in用于遍历对象的key,那for...in是否可以用于遍历Array呢?答案是:不要这么做。虽然你这么做了并不会又语法错误,但是有一点很重要:下标的有序性对于Array来说是核心,但是我们前面说到for...in的遍历是无序的。所以,不要拿for...in用于遍历Array。
3: for...of
for...of用来遍历可迭代对象的元素的值,而不是属性。
我们可以去复习一下什么是可迭代对象,简单的说来有以下几种:
1: ES6内建的可迭代对象:Array,TypedArray,Map, Set,String
2: Array-like object:arguments, nodeList
3: 用户自己创建的可迭代对象(用生成器函数创建的可迭代对象)
特别注意的一点是:
4: weakSet和weakMap都是不可迭代对象,所以不能使用for...of
我们现在来看一下for...of用于一个Array的例子:
let colors = ['red', 'green', 'yellow'];
for(let color of colors){
console.log(color); // 'red' 'green' 'yellow'
}
for...of用于自创建可迭代对象的例子:
function* numbers() {
yield 1;
yield 2;
yield 3;
}
for(let color of numbers()){
console.log(color); // 1 2 3
}
4: forEach()
我们先来看一下forEach( )的解释:
The forEach() method executes a provided function once for each array element.
我们来对上面的句子说明以下:
1: forEach()只能用于Array,不能用于其他的可迭代对象
2: forEach()接收一个function作为参数
3: 这个function会为数组里面的每一个元素都执行一次
看一下的语法:
forEach(function (value, index, array) {}, thisArg);
value: 当前轮询的元素的值
index: 当前轮询的元素的下标
array: 被轮询的array本身
thisArg: 在这个回调函数中的this值
关于forEach()方法,有几点需要注意:
1: forEach()总是返回undefined,这于map()和reduce()不同
我们可以看下面一个对比:
let numbers = [1, 2, 3];
let newNumbers1 = numbers.forEach(function (value, index, numbers) {
return value + 1;
});
console.log(newNumbers1); //undefined
let newNumbers2 = numbers.map(function (value, index, numbers) {
return value + 1;
})
console.log(newNumbers2); //[2, 3, 4]
2: forEach()要遍历的范围在对第一个元素遍历之前便已经确定
意思就是说:一旦Array对第一个元素调用forEach()里面的回调函数,这之后添加的新元素,并不会在此轮被遍历到。虽然数组已经发生了变化。可以看下面的例子:
let numbers = [1, 2, 3];
numbers.forEach(function (value, index, numbers) {
console.log(value); // 1, 2, 3
numbers.push(4);
});
console.log(numbers);//[1, 2, 3, 4, 4, 4]
以上的代码,我们在forEach()里面添加元素,但是总共也还是只遍历了1,2,3这三个元素。虽然在轮询的过程中,我们的numbers就已经发生了变化。
但是,如果在轮询中修改了Array,某些元素可能会被轮询跳过:
let numbers = [1, 2, 3, 4];
numbers.forEach((value, index, numbers)=>{
console.log(value);
if(value === 2){
numbers.shift(); //1 2 4
}
});
console.log(numbers);// [2, 3, 4]
3: 对于没有初始化的元素,forEach()不会对其调用callback函数
let count = 0;
let numbers = [1, 2, , 3];
numbers.forEach(function (value, index, numbers) {
console.log(value); //1 2 3
count ++;
});
console.log(count); //3
console.log(numbers.length); //4
numbers有四个元素,所以它的lenght为4,但是其中一个元素为空,我们看到count只加了3次,因为对于空元素,callback并不会被调用。
4: 在forEach()里面不能跳出轮询
而可以跳出轮询的遍历有以下几个:
1: for循环
2: for...in/for...of
3: Array.prototype.every()
4: Array.prototype.some()
5: Array.prototype.find()
6: Array.prototype.findIndex()
最后来总结以下for...in, for...of, forEach( )的区别:
1: for...in用于对象,遍历对象的key,不可用于Array
2: for...of用于可迭代对象,遍历的是元素的value,(常用于Array,Map等)
3: forEach()用于Array,不可直接用于其他类型数据
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。