目录
什么是遍历器?什么是可迭代对象?什么是迭代?什么是可枚举属性?
for 循环
while 循环
do... while 循环
for ... in 循环
for ... of 循环
.forEach 循环
.map() 循环
最后,罗列了几个问题点:
什么是遍历器?什么是可迭代对象?什么是迭代?什么是可枚举属性?
遍历器(Iterator)的诞生:
最初 JS 里用来表示“集合”的数据结构,有数组( Array )和对象( Object ),但在 ES6 中添加了 Map 和 Set 。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是 Map , Map 的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
就这样 遍历器(Iterator)的诞生了
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
遍历器(Iterator) 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费。
可迭代对象:
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性,或者说,一个数据结构只要具有 Symbol.iterator 属性,就可以认为是“可遍历的”(iterable)。(见图 img-1)。原生具备 Iterator 接口的数据结构如下。【Array, Map, Set, String, TypedArray, 函数的 arguments 对象, NodeList 对象】
迭代:
迭代是递归的一种特殊形式,是迭代器提供的一种方法,默认情况下是按照一定顺序逐个访问数据结构成员。迭代也是一种遍历行为。
可枚举属性:
在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的 enumerable 值决定的。可枚举性决定了这个属性能否被 for…in 查找遍历到。
迭代器:
Symbol.iterator 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个.next() 遍历器。
每一次调用 next 方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含 value 和 done 两个属性的对象。其中, value 属性是当前成员的值, done 属性是一个布尔值,表示遍历是否结束。(看下面代码并见图 img-2)
Iterator 的遍历过程:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的 next 方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的 next 方法,直到它指向数据结构的结束位置。
img-1 img-2arrayList: [ {name: '小明', age: 12}, {name: '小花', age: 10}, {name: '小黑', age: 14} ] const iterator = this.arrayList[Symbol.iterator](); console.log('iterator =>', iterator) // 模拟循环 console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next()) console.log(iterator.next())
for 循环
for 是最早出现的循环,也是最常用的,它是一种循环机制,能够满足大部分的遍历。主要是依靠角标来获取数组内成员。
位置 作用 详解 是否必要 ① 初始化 循环变量 只在循环开始前执行一次,后面轮循环时就不会被执行了 可省略(见 for 例子2) ② 定义循环条件 每轮循环都会执行条件判断,结果为 true,接着执行下一轮循环
直到为 false 时结束整个 for 循环。
可省略(见 for 例子3) ③ 更新初始化的变量 当处于这个时间节点时(当前这轮循环结束后,
进入下一轮循环条件判断之前)
执行的 更新初始化变量(这句话应该能看的懂吧!)
可省略(见 for 例子4) ④ 循环执行的代码块 简单介绍(注意):
适用于复杂的处理
再循环中 可以利用角标来删除元素,追加元素,修改元素值
它是一种循环机制
三个语句都可省略,但【;】号必须要有
循环体中可以使用 break(跳出循环)和 continue(阻止当前轮循环继续往下执行,并且进入下一轮循环)
可以循环 数组、对象(见 for例子5)、字符串
当省略②时:别忘记在循环体内使用 break,否则循环永远不会结束!
当省略③时:别忘记在循环体内更新 ①,否则可能会造成永远不会结束!
// 语法 结构
for (语句 1; 语句 2; 语句 3) {
循环 执行的代码块...
}
// for 例子 1
for (let i=0; i<3; i++) {
console.log(i)
}
// for 例子2
// 可省略语句1(比如在循环开始前已经设置了值时)
// 适用场景,i的初始化值是动态获取的
let i = 2;
for (; i<3; i++) {
console.log(i)
}
// for 例子3
// 可省略语句2,省略后必须在循环内提供 break 让循环结束。
// 否则循环就无法停下来。这样有可能令浏览器崩溃。
for (let i=0; ; i++) {
console.log(i)
if(i == 5){
break ;
}
}
// for 例子4
// 可省略语句3,写在循环体内。
for (let i=0; i<3; ) {
console.log(i)
i++
}
// for 例子5
// 三个语句都省略
let i = 0; // 语句1,设置在外面
for (; ; ) {
if(i == 5){
break ; // 语句2,添加条件,避免出现永久循环
}
console.log(i)
i ++; // 语句3,设置在里面
}
// for 例子6
// 通过使用 ES6中新增的 Object.keys() 方法 帮助for可以循环对象
let obj = {
name:'小明'
age: 12,
}
let keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++){
console.log(obj[keys[i]])
}
记住这种两种写法特点,在进行一些特殊情况时的数据处理会对你很有用
// for 特殊写法
// 不知道这种写法,同学们有没有见过
// 将获取数组长度的竟然放在了语句1里面
// 我在文章的上面讲过,语句1,执行一次对吧
// 那么这种写法,它的好处以及坏处,大家看打印结果,你就会明白的
let arr = ['01','02','03'];
for(let i = 0, len = arr.length; i
while 循环
while 循环只要指定条件结果为 true,循环就可以一直执行代码块。
简单介绍(注意):
别忘记更新条件中所用变量的值,否则循环永远不会结束!
当①结果为true时进入循环
循环体中可以使用 break(跳出循环)
// 语法结构
while (条件){
需要执行的代码
}
// 该循环永远不会结束,这可能导致浏览器崩溃。
while(true){
console.log('加菲猫!')
}
const arr = ['1','2',undefined,'3','','4'];
let i = 0;
while(arr[i]){
console.log(arr[i])
i = i + 1;
}
// 输出: 1
// 输出: 2
const arr = ['1','2','3','4'];
let i = 0;
while(arr[i]){
console.log(arr[i])
i = i + 1;
}
// 输出: 1
// 输出: 2
// 输出: 3
// 输出: 4
do... while 循环
do... while 循环是 while 循环的变体。该循环会在判断条件是否为真之前执行一次代码块,然后如果条件为真的话,就会重复这个循环。
简单介绍(注意):
它具有 即使条件的结果为 false,也至少循环一次的特点
别忘记更新条件中所用变量的值,否则循环永远不会结束!
循环体中可以使用 break(跳出循环)
// 语法结构
do
{
需要执行的代码
}
while (条件);
// do while 例子1
const arr = [1,2,3,4];
let i = 0;
let num = 0;
do
{
num = num + arr[i]; // 数组内成员的和
i++ ;
}while (arr[i]);
console.log(num) // 10
//数组中的【0,null,false,undefined, 空字符串】 当做false处理
// do while 例子2
const arr = [null,1,2,3,4];
let i = 0;
let num = 0;
do
{
console.log(arr[i])
i++ ;
}while (arr[i]);
// 角标 0 的位置,我故意放置了一个 null
// 因为先执行的 do 内的代码块 由(i=0) => 变(i=1)
// 所以当第一次进入 while 条件判断时, i变成了1, (arr[1]) => (1)
for ... in 循环
for ... in 是在 ES5 中新增的,遍历所有可枚举的属性(包括原型上的),最好只用来循环对象。
简单介绍(注意):
我觉得它的设计初衷就是循环对象,所以推荐大家用它时最好就是遍历对象
用于 遍历对象的所有可枚举属性、字符串、数组(最好不要使用for...in循环数组)
遍历 数组时 语句①为数组的角标,并且它的 index 索引是字符串型数字
遍历 数组时 循环顺序有可能不是按照实际数组的内部顺序,不推荐使用 通过for ... in循环出来的角标
可以使用 break(跳出循环)和 continue(阻止当前轮循环继续往下执行,并且进入下一轮循环)
// 语法结构
for(let 成员 in 对象){
循环的代码块
}
// for in 例子1
const json = {
name: '加菲猫',
sex: '男',
age: '8',
};
for(let item in json){
console.log('item =>', item , json[item])
}
// 输出:item => name 加菲猫
// 输出:item => sex 男
// 输出:item => age 8
for ... of 循环
for...of 是在 ES6 中新增的 语句,它遍历一个可迭代的对象(不明白可迭代对象,看文章顶部有关可迭代对象的介绍)
简单介绍(注意):
用于遍历可迭代对象 以及 字符串
想要在 for ...of 中得到数组的索引,需要使用 array.entries() 方法(见 for of 例子2)
可以使用 break(跳出循环)和 continue(阻止当前轮循环继续往下执行,并且进入下一轮循环
// for of 例子1
const arr = [1,2,3];
let item;
for(item of arr){
console.log('item =>', item)
}
// 输出:item => 1
// 输出:item => 2
// 输出:item => 3
// for of 例子2
// 使用 entries() 方法后,语句1 在每次遍历中就会得到一个数组,格式如下 下方配有截图
// 输出:index => (2) [0, 1]
// 输出:index => (2) [1, 2]
// 输出:index => (2) [2, 3]
const arr = [1,2,3];
let item,index;
for([index,item] of arr.entries()){
console.log('index =>', index , 'item =>', item)
}
// 注意,这种情况下,角标在第一个位置,元素在第二个位置
// 输出:index => 0 item => 1
// 输出:index => 1 item => 2
// 输出:index => 2 item => 3
// for of 例子3
// for of 中使用解构
const arr = [
{name: '小A', age: 12},
{name: '小B', age: 10},
{name: '小C', age: 14},
];
let ages = 0, age = 0;
for({age} of arr){
ages = ages + age;
}
console.log('总年龄 =>', ages)
// 输出:总年龄 => 36
.forEach 循环
forEach 是 ES5 提出的,挂载在可迭代对象原型上的方法。forEach是一个遍历器,负责遍历可迭代对象。
位置 详解 是否必要 ① 当前元素 必须 ② 当前元素的角标。 可省略 ③ 当前被遍历的数组对象
可省略 ④ 回调函数中的this指向,默认为undefined 可省略 简单介绍(注意):
用于遍历 可迭代对象 以及 字符串forEach() 对于空数组是不会执行回调函数的。
forEach() 中想要跳出循环 可以使用 try/catch。(见 forEach 例子1)
forEach() 中不建议修改正在遍历的可迭代对象内的元素值,避免出现低级错误。
// 语法结构
array.forEach(function(currentValue, index, arr), thisValue)
// forEach 例子1
try{
const arr = [1,2,3,4];
arr.forEach((item,index,arr) => {
console.log('item =>', item)
if(item == 2){
throw new Error('11') // 抛出异常
}
})
}catch(e){
if (e.message !== "11") throw e;
}
console.log('啦啦啦')
// 输出:item => 1
// 输出:item => 2
// 输出:啦啦啦
// forEach 例子2
// 遍历对象
const json = {
name: '加菲猫',
age: 10
}
const kyes = Object.keys(json);
console.log('kyes =>', kyes)
kyes.forEach((item)=>{
console.log(item, json[item])
})
// 输出:kyes => ['name', 'age']
// 输出:name 加菲猫
// 输出:age 10
.map() 循环
.map() 和 forEach 一样都是 ES5 提出的 ,.map() 方法是挂载在 Array 对象的原型上的 。 基本用法跟 forEach 方法类似。
位置 详解 是否必要 ① 当前元素 必须 ② 当前元素的角标。 可省略 ③ 当前被遍历的数组对象
可省略 ④ 回调函数中的this指向,默认为undefined 可省略 简单介绍(注意):
.map() 只能遍历 Array对象.map() 它有返回值(它返回一个新的数组),新数组中的元素 为 原始数组元素 在回调函数内处理后的值。
.map() 不会对空数组进行检测。
.map() 当数组中的值为基本数据类型时不会改变原始数组(当数组内的元素为对象会被改变)。(见 map 例子1)
// 语法结构
array.map(function(currentValue,index,arr), thisValue)
// map 例子1
const arr = [1,2,3];
const hh = arr.map((item, index, arr) =>{
item = item + 10;
return item;
})
console.log('hh =>', JSON.parse(JSON.stringify(hh))) // 新数组
console.log('arr =>', JSON.parse(JSON.stringify(arr))) // 原数组
// 输出:hh => [11, 12, 13]
// 输出:arr => [1, 2, 3]
// map 例子2
const arr = [{
id: 1,
name: '加菲猫',
},{
id: 2,
name: '欧弟',
}];
const home = arr.map((item, index, arr) =>{
return item.name;
})
/*
home 格式如下:
[
'加菲猫',
'欧弟'
]
*/
最后,罗列了几个问题点:
1. for...in 与 for...of 的区别,for in 和 for of 打印 item 输出的分别是什么?
const arr = [1,2,3,4,5]
for(let item in arr)
for(let item of arr)
2. for 循环和 forEach 的本质区别是什么?