之前一直知道JS 里有个数据类型是
Symbol,Symbol()返回一个永远不会重复的数据,即(Symbol() === Symbol() //==> false;)。 之后在项目中不怎么使用,所以就不怎么熟悉这个基本类型,后来在学习Ts的时候,了解到迭代器和生成器的时候,看到了这个熟悉又陌生的数据类型,所以就浅浅的了解下这个数据属性以及他的用法。
Symbol.iterator 迭代器
官方定义:
Symbol.iterator为每一个对象定义了默认的迭代器。该迭代器可以被for...of循环使用。
- 若一个对象拥有迭代行为,比如在
for...of中会循环一些值,那么那个对象便是一个可迭代对象。一些内置类型,如Array,String,Set或Map拥有默认的迭代行为,而Object则本身没有迭代的能力,则是不可迭代对象- 但是如果想要给一个
Object或者Class添加可迭代属性或者想要改变Array原有的迭代规则,则需要手动添加,再使用for...of获取迭代结果。
1. 可迭代对象
- 若一个对象拥有迭代行为,比如在
for...of中会循环一些值,那么那个对象便是一个可迭代对象。一些内置类型,如Array,String,Set或Map拥有默认的迭代行为。- 用于可迭代对象的语法:
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 迭代器函数
- 返回一个 包含
next属性函数的对象,- next函数 返回一个
对象 {value: 你需要在for..of中使用的结果, done: Boolean},done和value一起出现时,就是迭代器的返回值, 当done为true时,则表示迭代器到了最后一个值
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 生成器函数
生成器函数其实就是一个迭代器的另一种比较简便的表达,无需手动调节done的状态变化来终止迭代, 它允许你定义一个非连续执行的函数作为迭代算法,即(function* it(){ yield 1; yield 2; } // 1, 2 分别为两次迭代执行的结果)function*表示这是一个生成器函数,这个函数返回一个生成器作为特殊迭代器,迭代器调用next()方法来进行迭代;当遇到关键字yield时,则返回迭代结果。可以多次调用next()方法,每次都返回一个新的生成器,而每一个生成器只能迭代一次。- 生成器函数比迭代器更加的简洁易懂一些,功能上是一致的。
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]]