目录

什么是遍历器?什么是可迭代对象?什么是迭代?什么是可枚举属性?

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 方法,直到它指向数据结构的结束位置。

arrayList: [
    {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())

img-1
img-2

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<len; i++){
    console.log(arr[i], JSON.parse(JSON.stringify(arr)))
    if(arr[i] == '02'){
        arr.push('04')
    }
}
// 输出:01 ['01', '02', '03']
// 输出:02 ['01', '02', '03']
// 输出:03 ['01', '02', '03', '04']

// 我在循环中,有个判断,循环到02时,向数组内追加了新的成员。
// 从打印的结果上可以看到,成员是成功追加进去的。可是为什么没有循环出来捏。
// 原因就是因为,获取数组长度,写在了语句1里面,而语句1,只执行一次
// 当执行获取数组长度时,数组的长度就是3,所以整个循环只跑了3轮

let arr = ['01','02','03'];
for(let i = 0; i < arr.length; i++){
    console.log(arr[i], JSON.parse(JSON.stringify(arr)))
    if(arr[i] == '02'){
        arr.push('04')
    }
}
// 输出:01 ['01', '02', '03']
// 输出:02 ['01', '02', '03']
// 输出:03 ['01', '02', '03', '04']
// 输出:04 ['01', '02', '03', '04']

// 而这种写法,是在每次轮循时,都去重新获取数组的长度。

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 的本质区别是什么?

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐