全网最完整的JavaScript迭代器(遍历器)整理长文,强烈建议收藏!!!

写在最前:js中包含的迭代器(遍历器)比较多,博主认为整理在一块一起学习,进行对比,有助于记忆,便整理出这么一篇长文,文章有点长,强烈建议收藏,反复查阅!

目录

1.定义

2.js中内置的迭代器(语句篇)

Iterator

for...in

for...of

for await ... of

3.js中内置的迭代器(函数篇)

forEach()

map()

every()

some()

find()

findIndex()

filter()

reduce()

reduceRight()


1.定义

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

JavaScript中内置的有关迭代器的使用非常多,接下来我们来看看都有哪些?

2.js中内置的迭代器(语句篇)

以下为迭代器语句篇,其中包括Iterator、for...in、for...of、for await ... of。

  • Iterator

Iterator 是 ES6 引入的一种新的迭代机制,是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。

使用示例:

    let arr = ['hello','been']
    const iterator = arr[Symbol.iterator]();
    console.log(iterator.next()); // {value: "hello", done: false}
    console.log(iterator.next()); // {value: "been", done: false}
    console.log(iterator.next()); // {value: undefined, done: true}

由上我们可以看出,迭代的过程如下:

  1. 通过 Symbol.iterator 创建一个迭代器,指向当前数据结构的起始位置;
  2. 随后通过 next 方法进行向下迭代指向下一个位置, next 方法会返回当前位置的对象,对象包含了 value 和 done 两个属性, value 是当前属性的值, done 用于判断是否遍历结束;
  3. 当 done 为 true 时则遍历结束。

  • for...in

for...in语句以任意顺序遍历一个对象的除Symbol以外的可枚举属性。

使用示例

    Object.prototype.writeBlog = function () {
        console.log("hello world");
    }
    let obj = {
        age: 22,
        name: 'been'
    };
    for ( key in obj) {
        console.log(key);
    }

由于for...in会遍历到原型上继承来的属性,导致输出结果为:age  name  writeBlog

当我们不需要遍历到继承的属性时,可以使用hasOwnProperty() 进行判断,如下:

    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            console.log(key);
        }
    }
    // 输出:age  name


  • for...of

for...of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。ES6 新引入的迭代器,并且支持新的迭代协议。

使用示例:

    let arr = ['hello','been'];
    for (let val of arr) {
        console.log(val);
    }
    //输出:
    // hello
    // been

    let set = new Set();
    set.add('hello');
    set.add('been');
    for (let val of set) {
        console.log(val);
    }
    //输出:
    // hello
    // been

以上遍历数组和集合差不多,都是输出属性值。而遍历Map对象则是输出[key,value],如下

    let map = new Map();
    map.set(0, "zero");
    map.set(1, "one");
    for (let item of map) {
        console.log(item);
    }
    // 输出:
    // [0,"zero"]
    // [1,"one"]

    for (let [key,val] of map) {
        console.log(`${key}:${val}`);
    }
    // 输出:
    // 0:"zero"
    // 1:"one"

遍历Map对象得到的值为数组,下标0和1分别为key和value,所以我们可以数组解构赋值方式对应赋值。

注:for...of不能遍历object对象,为什么呢?

    let obj = {
        age: 22,
        name: 'been'
    };
    for (let key of obj) {
        console.log(key);
    }
    // 报错 Uncaught TypeError: obj is not iterable

因为for...of语句用在可迭代对象上,即需要实现迭代器Iterator。而Object对象没有实现这个Symbol.iterator接口,使得它无法被for...of遍历。

怎么让对象可以被for...of 遍历?缺啥就补啥咯,代码如下:

    Object.prototype[Symbol.iterator] = function*() {
        let index = 0;
        let arr = Object.entries(this);
        let length = arr.length;
        while (true) {
            if (index >= length) {
                return false
            } else {
                let key = arr[index] && arr[index][0];
                let val = arr[index] && arr[index][1];
                let result = { [key]: val };
                index++;
                yield result 
            }
        }
    };

我们给Object的原型绑了Symbol.iterator接口,用的是ES6引入的Generator函数,这时再用for...of遍历obj时,则输出:


  • for await ... of

for await...of 语句会在异步或者同步可迭代对象上创建一个迭代循环,包括 String,Array,Array-like 对象(比如arguments 或者NodeList),TypedArray,Map, Set和自定义的异步或者同步可迭代对象。其会调用自定义迭代钩子,并为每个不同属性的值执行语句。像await表达式一样,这个语句只能在 async function内使用。

虽然说for await ... of在异步或同步都可用,但一般用于异步的情况,同步用for ... of。

使用示例:

Symbol.asyncIterator 可指定了一个对象的默认异步迭代器。如果一个对象设置了这个属性,它就是异步可迭代对象,可用于for await...of循环,代码如下:

    // 创建异步可迭代对象
    const asyncIterable = {
        [Symbol.asyncIterator]() {
            return {
                i: 0,
                next() {
                    if (this.i < 2) {
                        return Promise.resolve({
                            value: this.i++,
                            done: false
                        });
                    }

                    return Promise.resolve({
                        done: true
                    });
                }
            };
        }
    };
    // 执行迭代
    (async function () {
        for await (num of asyncIterable) {
            console.log(num);
        }
    })();

    // 输出:
    // 0
    // 1

async function * 可用来创建异步生成器,其中已经实现了异步迭代器协议, 所以可以用for await ... of迭代。代码如下:

    // 创建异步生成器
    async function* asyncGenerator() {
        var i = 0;
        while (i < 2) {
            yield i++;
        }
    }
    // 执行迭代
    (async function() {
        for await (num of asyncGenerator()) {
            console.log(num);
        }
    })();
    // 输出:
    // 0
    // 1


3.js中内置的迭代器(函数篇)

  • forEach()

forEach() 方法对数组的每个元素执行一次给定的函数。

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const arr = [1,2,3,4];
    const res = arr.forEach((v,i,a)=>{
        arr[i] = v*2;
    })
    console.log(arr,res);
    // 输出:[2, 4, 6, 8] undefined

我们发现,原数组中每个元素都结果回调函数的操作之后,都变为原来的俩倍;forEach是没有返回值的。

注意: 除了抛出异常以外,没有办法中止或跳出 forEach() 循环。如果在forEach中使用return,只能达到continue的效果,代码如下:

    const arr = [1,2,3,4];
    arr.forEach((val,i,eachArr)=>{
        if(val===2){
            return
        }
        eachArr[i] = val*2;
    })
    console.log(arr);
    // 输出:[2, 2, 6, 8]

由上可见,forEach()不适合需要中止或跳出循环的情况


  • map()

map() 方法创建一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const arr = [1,2,3,4];
    const res = arr.map(v=> v*2)
    console.log(arr,res);
    // 输出:
    // [1, 2, 3, 4] 
    // [2, 4, 6, 8] 

我们发现,原数组没有改变,返回的数组为原来的俩倍;map()中修改val值,不会修改到原数组,而是修改返回的新数组;

如果不使用返回的新数组,可以用forEach()或for...of替代

注意: 和forEach()一样,除了抛出异常以外,没有办法中止或跳出 map() 循环。如果在map中return没有内容时,会让新数组对应值为undefined,代码如下:

    const arr = [1, 2, 3, 4];
    const res = arr.map(v => {
        if(v === 2){
            return
        }
        return v * 2
    })
    console.log(res);
    // 输出: [2, undefined, 6, 8]


  • every()

every()方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const result1 = arr.every(v=>{
        console.log(v);
        return v<=4
    })
    console.log(result1);
    // 输出:1 2 3 4 true


    const result2 = arr.every(v=>{
        console.log(v);
        return v>2
    })
    console.log(result2);
    // 输出:1 false

从上面俩个输出对比,可以发现:every 在迭代过程中,当callback返回有false,将会立即返回 false,不再继续迭代。相反地,当每一个元素执行都返回 true,every会返回 true

和map()一样,every()中操作val值,不会改变原数组。代码如下:

    const arr = [1,2,3,4];
    const res = arr.every(v=>{
        v=v*2
        return v>1
    })
    console.log(res,arr);
    // 输出:
    // true  [1, 2, 3, 4]


  • some()

some() 方法测试数组中是不是至少有1个元素通过指定函数的测试。它返回一个布尔值。

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const arr = [1,2,3,4];
    const result1 = arr.some(v=>{
        console.log(v);
        return v<=4
    })
    console.log(result1);
    // 输出:
    // 1 true

    const result2 = arr.some(v=>{
        console.log(v);
        return v>4
    })
    console.log(result2);
    // 输出:
    // 1 2 3 4 false

从上面俩个输出对比,可以发现:some在迭代过程中,当callback返回有true,将会立即返回 true,不再继续迭代。相反地,当每一个元素执行都返回 false,some会返回 false

和every()一样,some()中操作val值,不会改变原数组。代码如下:

    const res = arr.some((v,i,a)=>{
        v=v*2
        return v<2
    })
    console.log(res,arr);
    // 输出:
    // false [1, 2, 3, 4]


  • find()

    find()方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const arr = [5, 12, 130, 44];
    const found1 = arr.find(v =>{ 
        console.log(v);
        return v > 10
    });
    console.log(found1);
    // 输出:5 12 12

    const found2 = arr.find(v =>{ 
        console.log(v);
        return v > 130
    });
    console.log(found2);
    // 输出:5 12 130 44 undefined

从上面俩个输出对比,可以发现:find在迭代过程中,当callback返回有true,将会立即返回 符合值,不再继续迭代。相反地,当每一个元素执行都返回 false,find会返回 undefined。


  • findIndex()

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。

findIndex()和find()用法类似,区别:前者返回索引,后者返回值。所以不再赘述。


  • filter()

  filter()方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 

参数描述

callback(

currentValue

,index

,array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
currentValue 必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
thisArg可选。当执行回调函数 callback 时,用作 this 的值。

使用示例:

    const arr = [5, 12, 8, 130];
    const filterArr = arr.filter(v => v > 10);
    console.log(filterArr,arr);
    // 输出: [12, 130]  [5, 12, 8, 130]

我们发现,新数组中的元素都为回调函数返回true的元素,而且原数组不会改变


  • reduce()

reduce() 方法对数组中的每个元素执行回调函数(顺序从左到右),将其结果汇总为单个返回值。

参数描述

callback(

accumulator,

currentValue,

index,

array

)

必需。为数组中每个元素执行的函数。

该函数参数:

参数描述
accumulator必需。累加值。是上一次调用回调时返回的累积值或initialValue(第一次)。
currentValue必需。当前元素。
index 可选。当前元素的索引。
array 可选。正在操作的数组。
initialValue可选。初始值:作为第一次调用 callback函数时的accumulator值。 如果没有提供,则使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

使用示例:

    // reduce() 
    const arr = [1, 2, 3, 4];
    let log = [];
    const reducer = (accumulator, currentValue, index , arr) => {
        log.push({
            accumulator,
            currentValue,
            index , 
            arr:arr.join(',')
        });
        return accumulator + currentValue
    };

    // 1 + 2 + 3 + 4 没有初始值
    const result1 = arr.reduce(reducer)
    console.table(log);
    console.log("返回:",result1);

为方便查看执行过程中各参数变化,我们用console.table打印结果如下:

我们发现,当没有初始值initialValue时,各变量会有以下规则:

  1. 首次执行时,accumulator为数组第一个值,往后都是前一次执行的返回结果;
  2. currentValue和index都是从第二个元素开始;
  3. arr原数组不改变;
  4. 返回值为最后一次调用回调结果。

再来看看,在同一个回调函数,设置初始值initialValue的情况,代码如下:

    log = [];
    const result2 = arr.reduce(reducer,5);
    console.table(log);
    // 5 + 1 + 2 + 3 + 4 有初始值
    console.log("返回:",result2);

打印结果如下:

我们发现,当设置初始值initialValue时,各变量会有以下规则:

  1. 首次执行时,accumulator为初始值initialValue,往后都是前一次执行的返回结果;
  2. currentValue和index都是从第一个元素开始;
  3. arr原数组不改变;
  4. 返回值为最后一次调用回调结果。


  • reduceRight()

reduceRight() 方法对数组中的每个元素执行回调函数(顺序从右到左),将其结果汇总为单个返回值。

reduceRight()和reduce()用法类似,主要是执行顺序不同:前者从右到左,后者从左到右。所以不再赘述。


非常感谢您的耐心阅读,整理不易,喜欢点赞、评论、收藏、关注哦,您是最棒的!

  • 12
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值