ES6 - iterator 和 for..of(详解)

1.iterator(遍历器)的概念

  • javascript 原有的表示“集合”的数据结构主要是数组(Array)和对象(Object),ES6新增了Set和Map数据结构,用户可以组合使用它们,因此就需要一种统一的接口机制来处理所有不同的数据结构。

  • iterator就产生,它是一种接口,为各种不同的数据结构提供统一的访问机制,然和数据结构只要部署iterator接口,就可以完成遍历操作。

  • iterator的作用有三个
    1.为各种数据结构提供一个统一的,简单的访问接口
    2.使得数据结构的成员可以按某种次序排列
    3.ES6穿凿了一个全新的遍历命令 - for – of 循环

  • iterator的遍历过程如下
    1.创建一个只针对想,指向当前数据结构的起始位置。也就是说,遍历器对象本质上就是一个只针对象。
    2.第一此调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
    3.第二次调用指针对象的next方法,指针就只想数据结构的第二个成员。
    4.不断地调用只针对想next方法,知道他只想数据结构的结束位置。

  • 每次调用next方法,都会返回数据结构当前的成员信息。返回一个value和done两个属性的对象。
    其中value为当前成员的值,done为一个布尔值,表示遍历是否结束。

2.模拟next返回值的方法

{
            //模拟next方法返回值的例子
            let it = makeIterator(['a', 'b']);

            it[Symbol.isConcatSpreadable] = true; //让对象展开,方便我们再控制台查看。

            function makeIterator(array) {
                let nextIndex = 0;
                return {
                    next() {
                        return nextIndex < array.length ? {
                            value: array[nextIndex++],
                            done: false
                        } : {
                            value: undefined,
                            done: true
                        }

                        //三目运算符,根据下标返回相应的数据。
                    }
                }
            }

            console.log(it.next()); //{value: "a", done: false}
            console.log(it.next()); //{value: "b", done: false}
            console.log(it.next()); //{value: undefined, done: true}
        }

3.默认的iterator接口

  • iterator接口的母的时为了所有数据结构提供一种统一的访问机制,即for…of…循环,当使用for…of循环遍历某种数据结构的时候,该循环会自动去寻找iteratir接口。

  • 数据结构只要部署了iterator接口就是可遍历的,

  • 一个数据结构只要具备Symbol.iterator属性就可以被认为时可遍历的。

 [Symbol.iterator]属性

        {
            let obj = {
                [Symbol.iterator]() {
                    return {
                        next() {
                            return {
                                value: 1,
                                done: true
                            }
                        }
                    }
                }
            }
        }

        上述的对象obj因为具有Symbol.iterator属性,执行这个属性会返回一个遍历器对象
        该对象的根本特征就是具有next方法,每次调用next方法都会返回一个当前成员的信息对象,具有value和done两个属性。
  • ES6的有些数据结构是具有iterator接口,不用处理就可以用for…of循环。
    Array,Map,Set,String,函数arguments对象,NodeList对象。
ES6的有些数据结构是具有iterator接口,不用处理就可以用for...of循环。

        数组的Symbol.iterator属性
        {
            let arr = ['a','b','c'];
            let iter = arr[Symbol.iterator]();
            //调用对象里面的next()

            console.log(iter.next());   //{value: "a", done: false}
            console.log(iter.next());   //{value: "b", done: false}
            console.log(iter.next());   //{value: "c", done: false}
            

            // 我们也可以直接用for...of循环遍历arr数组
            for(let i of arr){
                console.log(i)
                //a b c
            }
        }


        上面的代码中,变量arr是一个数组,器原生具有遍历器接口,部署在arr的Symbol.iterator属性上,所以调用
        这个属性就会得到遍历器对象。


        为对象添加iterator接口(例子)
        {
            let testObj = {
                data:['hello','world'],
                name:'kjh',
                [Symbol.iterator](){
                    let self = this;
                    let index = 0;
                    return {
                        next(){
                            if(index < self.data.length){
                                return {
                                    value:self.data[index++],
                                    done:false
                                };
                            }
                            else{
                                return {
                                    value:undefined,done:true
                                }
                            }
                        }
                    }
                }
            };
            let iter = testObj[Symbol.iterator]();
            console.log(iter.next());//{value: "hello", done: false}
            console.log(iter.next());//{value: "world", done: false}
            console.log(iter.next());//{value: undefined, done: true}
        }
         

         类数组对象调用数组的Symbol.iterator就可以实现for...of遍历
         {
            let arrLike = {
                '0':1,
                '1':2,
                length:2,
                [Symbol.iterator]:Array.prototype[Symbol.iterator]
            };

            for(let item of arrLike){
                console.log(item);  
                //1
                //2
            }
         }


         普通对象调用数组的方法并无效果(控制台都没有输出结果)
         {
            let arrLike = {
                '0':1,
                '1':2,
                [Symbol.iterator]:Array.prototype[Symbol.iterator]
            };

            for(let item of arrLike){
                console.log(item); 
            } 
         }


        这是因为类似数组的对象存在(数值键名和length属性),部署iterator接口可以直接使用数组的iterator方法。
        

4.调用Iterator接口的场合

结构赋值
        对数组和Set结构进行解构赋值的时候,会默认调用Symbol.iterator方法。
        {
            let set = new Set().add('a').add('b').add('c');

            let [x,y] = set;
            console.log(x,y);  //x = 'a'  y = 'b'


            let [first,...rest] = set;
            console.log(first,rest);  
            //a (2) ["b", "c"]
        }
        


        扩展运算符
        扩展运算符(...)也会默认使用默认的Iterator接口
        {
            //例1:
            var str = 'hello';
            console.log([...str]);
            //(5) ["h", "e", "l", "l", "o"]


            //例2:
            let arr = ['b','c'];
            console.log(['a',...arr,'d']);
            //(4) ["a", "b", "c", "d"]
        }


        其他场合
        yield   for...of()   Array.from()    Map()  Set()  weakMap()  weakSet()  Promise.all()   Promise.race()

5. for … of 循环

  • 一个数据结构只要部署了Symbol.iterator属性,就被视为具有Iterator接口,就可以用for…of循环遍历他的成员。
  • 也就是说,for…of循环内部调用的时数据结构的Symbol.iterator方法。
  • for… of…循环可以使用的范围包括数组,Set和Map结构,某些类似数组的对象。

6.字符串的Iterator接口

 字符串是一个类似数组的对象,也具有原生Iterator接口
        {
            let someString = 'hi';
            console.log(typeof someString[Symbol.iterator]); 
            //function

            let iter = someString[Symbol.iterator]();
            console.log(iter.next());  //{value: "h", done: false}
            console.log(iter.next());  //{value: "i", done: false}
            console.log(iter.next());  //{value: undefined, done: true}
        }

        //上面的代码中,调用Symbol.iterator方法返回一个遍历器对象,在上面可以调用next方法实现对于字符串的遍历。

        var str = new String('hi');
        console.log([...str]);
        //(2) ["h", "i"]


        iterator接口与Gennerator函数
        遍历器对象的return(),throw()
        (学完generator函数再回顾)

7. 数组

 {   
            //数组原生具备iterator接口(默认部署了Symbol.iterator属性)
            const arr = ['red','green','blue'];

            for(let v of arr){
                console.log(v); //red  green  blue
            }

            let obj = {};
            obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

            for(let k of obj){
                console.log(k);  //red  green  blue
            }

        }

        
        //空对象obj部署了数组arr的Symbol.iterator属性,结果Obj的for...of循环产生了与arr完全一样的结果。


        {
            //for... in... 和 for...of...
            let arr = ['a','b','c','d'];
            arr.foo = 'hello';

            //for...in  只能循环获取对象的键名
            for(let a in arr){
                console.log(a);   //0  1  2  3  foo
            }

            //for...of  只能换取键值
            for(let b of arr){
                console.log(b);   //a  b  c  d
            }
            // for ... of 循环不会返回数组arr的foo属性。

        }

8.Set和Map

 {
            Set和Map结构具有Iterator接口,可以直接使用for...of循环
             let set = new Set(['name','age','qq']);
             for(let i of set){
                 console.log(i);
                 //name
                 //age
                 //qq
             }
            set结构遍历返回的是一个值

            let map = new Map();
            map
                .set("name",'kjh')
                .set('age',18)
                .set('qq','861918672')

            for(let k of map){
                console.log(k);
                //(2) ["name", "kjh"]
                //(2)["age", 18]
                //(2) ["qq", "861918672"]
            }
            //map结构遍历返回的是一个数组,成员为map成员的键名和键值。
        }

9.类似数组的对象 (字符串,DOM NodeList对象,arguments对象)

 {
            let str = 'hello';
            for(let i of str){
                console.log(i) 
                // h e l l o
            }


            //arguments
            function printArgs(){
                for(let i of arguments){
                    console.log(i)
                }
            }
            printArgs('a','b');  
            //a  b

            //如上说过不具有数字键名和length属性的普通类数组对象不能使用数组的遍历接口
            //一个方法就是使用Array.from 将其转换为数组
            let arrLike = {
                '0':'kjh',
                '1':18,
                length:2
            }

            for(let i of Array.from(arrLike)){
                console.log(i) 
                //kjh  18 
            }
        }

10.对象

     //对于普通的对象,for...of不能直接使用,必须部署iterator接口,但是这样的情况下,for..in依旧可以使用
        {
            let obj = {
                name:'kjh',
                age:18,
                qq:'861923'
            }

            for(let i in obj){
                console.log(i);
                //name  age  qq
            }

            for(let i of obj){
                console.log(i);
                //obj is not iterable
            }


            //解决方法,给对象部署iterator接口

            //对象提供了Object.keys()  Object.values()  Object.entries()  方法

            for(let i of Object.keys(obj)){
                console.log(i);
                //name
                //age
                //qq
            }

            for(let i of Object.values(obj)){
                console.log(i);
                //kjh
                //18
                //861923
            }

            for(let i of Object.entries(obj)){
                console.log(i);
                //(2) ["name", "kjh"]
                //(2) ["age", 18]
                //(2) ["qq", "861923"]
            }

        }

11.计算生成的数据结构

  有些数据结构时再现有数据结构的基础上计算生成的,比如ES6的数组,set和Map都部署了一下3个方法

        + entries()  返回一个遍历器对象,遍历键名键值
        + keys()  返回一个遍历器对象,遍历键名
        + values()  返回一个遍历器对象,遍历所有的键值

        {
            // 举个例子
            let arr = ['a','b','c'];

            for(let i of arr.keys()){
                console.log(i)   //0 1 2
            }

            for(let i of arr.values()){
                console.log(i)   //a b  c
            }

            for(let i of arr.entries()){
                console.log(i);
                //(2) [0, "a"]
                //(2) [1, "b"]
                //(2) [2, "c"]
            }
        }

12. for…in 和 for…of 的优缺点

    1.for...in 不适合遍历数组,因为for...in循环以字符串作为键名
    2.for...in 循环会拿到原型上的键名

    for...of
    1.for...of 有for .. in 的优点,没有他的缺点
    2.可以配合break continue  return使用
发布了85 篇原创文章 · 获赞 16 · 访问量 6114

猜你喜欢

转载自blog.csdn.net/qq_43955202/article/details/104230038
今日推荐