Symbol.iterator 迭代器

205 阅读3分钟

之前一直知道JS 里有个数据类型是SymbolSymbol()返回一个永远不会重复的数据,即(Symbol() === Symbol() //==> false;)。 之后在项目中不怎么使用,所以就不怎么熟悉这个基本类型,后来在学习Ts的时候,了解到迭代器和生成器的时候,看到了这个熟悉又陌生的数据类型,所以就浅浅的了解下这个数据属性以及他的用法。

Symbol.iterator 迭代器

官方定义:Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for...of循环使用。

  • 若一个对象拥有迭代行为,比如在 for...of 中会循环一些值,那么那个对象便是一个可迭代对象。一些内置类型,如 Array,String,Set或 Map 拥有默认的迭代行为,而Object则本身没有迭代的能力,则是不可迭代对象
  • 但是如果想要给一个Object或者Class添加可迭代属性或者想要改变Array原有的迭代规则,则需要手动添加,再使用for...of获取迭代结果。

1. 可迭代对象

  1. 若一个对象拥有迭代行为,比如在 for...of 中会循环一些值,那么那个对象便是一个可迭代对象。一些内置类型,如 Array,String,Set或 Map 拥有默认的迭代行为。
  2. 用于可迭代对象的语法:for...of 循环、展开语法、yield*解构语法
for (let value of ["a", "b", "c"]) {
  console.log(value);
}
// "a"
// "b"
// "c"

[..."abc"]; // ["a", "b", "c"]

function* gen() {
  yield* ["a", "b", "c"];
} 

gen().next(); // { value: "a", done: false } // 生成器函数的返回值本身也是可迭代的。

[a, b, c] = new Set(["a", "b", "c"]);
a; // "a"

2. 添加迭代器属性的两种方式

2.1 迭代器函数

  1. 返回一个 包含 next属性函数的对象,
  2. next函数 返回一个 对象 {value: 你需要在for..of中使用的结果, done: Boolean},
  3. donevalue一起出现时,就是迭代器的返回值, 当 donetrue时,则表示迭代器到了最后一个值
function iteratorFun() {
    return {
        next:() => {
            //...函数内部的迭代操作...
            return {
                value: '',  // for...of 中返回的结果。
                done: true or false 
            }
        }
    }
}
// 迭代过程可以看作为
let it = iteratorFun();
let res = it.next();
while(!res.done) {
    console.log(res.value); // ...... 返回你的迭代值
    res = it.next();
}

2.2 生成器函数

  1. 生成器函数其实就是一个迭代器的另一种比较简便的表达,无需手动调节done的状态变化来终止迭代, 它允许你定义一个非连续执行的函数作为迭代算法,即(function* it(){ yield 1; yield 2; } // 1, 2 分别为两次迭代执行的结果)
  2. function* 表示这是一个生成器函数,这个函数返回一个生成器作为特殊迭代器,迭代器调用next()方法来进行迭代;当遇到关键字yield时,则返回迭代结果。可以多次调用next()方法,每次都返回一个新的生成器,而每一个生成器只能迭代一次。
  3. 生成器函数比迭代器更加的简洁易懂一些,功能上是一致的。
function* iteratorFun() {
    //...函数内部的迭代操作...
    yield '';  // for...of 中返回的结果。
    return ''; // 用return 来终结这个生成器。
}

3. 应用

3.1 给Object对象添加迭代器。

let orgObj = {
    x:1,
    y:2,
    z:3
};
// 1. 迭代器方式
let orgObj1 = {
    x:1,
    y:2,
    z:3,
    [Symbol.iterator]() {
        let index = 0;
        let keys = Object.keys(this);
        let values = Object.values(this);
        return {
            next:() => {
                index++;
                return {
                    value: [keys[index - 1],values[index -1]],
                    done: index > keys.length
                }
            }
        }
    }
};
// 2. 生成器方式
let orgObj2 = {
    x:1,
    y:2,
    z:3,
    *[Symbol.iterator]() {
        let index = 0;
        while(i < Object.keys(this).length) {
            let k = Object.keys(this)[i];
            let v = Object.values(this)[i];
            yield [k, v];
            i++;
        }
    }
}
for(const objArr of orgObj1) {
    console.log(objArr); 
    /**
      *['x', 1]
      *['y', 2]
      *['z', 3]
      */
}
// 打印结果
console.log("扩展:::", [...orgObj1], [...orgObj2]); // 两个都是 [['x',1],['y',2],['z',3]]

console.log(orgObj1, orgObj2); 
    /**
       orgObj1: {
            x:1,
            y:2,
            z:3,
            [Symbol(Symbol,iterator)]:[Function: [Symbol.iterator]]
            },
            orgObj2: {
            x:1,
            y:2,
            z:3,
            [Symbol(Symbol,iterator)]:[GeneratorFunction: [Symbol.iterator]]
            }
      */

3.2 给class添加迭代属性。

interface cusObj {
    [a:string]: string|number
}
class CustomObj {
    private data: cusObj;
    constructor(data:cusObj) { this.data = data };
    [Symbol.iterator]() {
        let i = 0;
        return {
            next: () => {
                if( i < Object.keys(this.data).length) {
                    return {value: [k, this.data[k]], done: false};
                } else {
                    return {value:"", done:true};
                }
            }
        }
    }
}
let m = new CustomObj({a:1,b:3});
for(const k of m) {
    console.log("custom K:::",k); 
    /*
    * ['a',1]
    * ['b',3]
    */
}
console.log("custom m:::", [...m]); // [['a',1],['b',3]]