可迭代对象和类数组
目录
前言
for...of遍历对象工作原理
一、可迭代对象
几个相关定义
可迭代对象
Symbol.iterator
进一步了解底层知识,显示调用迭代器
二、类数组(array-like)
定义
与可迭代对象的区别
三、Array.from()
前言
基础知识:Iterables,推荐阅读关于可迭代对象的解释
在ES6中,对于一些内建对象我们可以使用for...of来遍历,比如数组、字符串、Set、Map等,其原理是for...of会自动调用这些内置对象的内置Symbol.iterator方法,此方法会返回一个迭代器(iterator),迭代器中会包含一个next方法,通过next返回的字段done来决定遍历结束。
for...of遍历对象工作原理
- 当 for..of 循环启动时,它会自动调用对象上的symbol.iterator方法(如果没找到,就会报错)。这个方法必须返回一个 迭代器(iterator) —— 一个有 next 方法的对象。
- 从此开始,for..of 仅适用于这个被返回的对象(迭代器)。
- 当 for..of 循环希望取得下一个数值,它就调用这个对象的 next() 方法。
- next() 方法返回的结果的格式是 {done: Boolean, value: any},当 done=true 时,表示迭代结束,否则 value 是下一个值。
一、可迭代对象
几个相关定义
可迭代对象 :iterables
使对象可迭代的内置 symbol: Symbol.iterator
迭代器 :iterator
可迭代对象
- 可迭代对象是实现了
Symbol.iterator
方法的对象。 - 可迭代对象是数组的泛化,这个概念允许我们在for..of循环中使用任何对象。
- 数组、字符串、对象等内置对象都是可迭代对象。
Symbol.iterator
设一个range对象,帮助我们掌握可迭代概念,range对象代表一个数组区间,这个对象并不是数组,但看上很适合使用for..of。
let range = {
from :1,
to: 5
}
//我们希望for..of运行如下
//for(let num of range)...num=1,2,3,4,5
为了使range对象可迭代(for...of可运行),我们为其添加一个名为 Symbol.iterator
的方法。该方法在 for..of 时会被调用,完整实现如下
let range = {
from: 1,
to: 5
};
// 1. for..of 调用首先会调用这个方法,它返回迭代器对象(iterator object)
range[Symbol.iterator] = function() {
// 2. 接下来,for..of 仅与此迭代器一起工作,要求它提供下一个值
return {
current: this.from,
last: this.to,
// 3. next() 在 for..of 的每一轮循环迭代中被调用
next() {
// 4. 它将会返回 {done:.., value :...} 格式的对象
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// 现在它可以运行了!
for (let num of range) {
alert(num); // 1, 然后是 2, 3, 4, 5
}
从上可以看出,可迭代对象(此例中是range) 和其进行迭代的对象(symbol.iterator创建的对象)是分开的,即可迭代对象的核心功能-关注点分离。
进一步了解底层知识,显示调用迭代器
let str = "Hello";
// 和for...of做同样的事情
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // outputs characters one by one
}
上面这个例子中,我们直接调用str[Symbol.iterator]() 来创建一个迭代器,并在while中使用next()获取值,根据值判断是否继续循环以及每次循环执行的操作。
二、类数组(array-like)
定义
类数组是有索引和 length
属性的对象,所以它们看起来很像数组。
let arrayLike = { // has indexes and length => array-like
0: "Hello",
1: "World",
length: 2
};
// Error (no Symbol.iterator)
for (let item of arrayLike) {}
与可迭代对象的区别
- 可迭代对象有symbol.iterator方法。
- 两者不互斥:字符串即是可迭代的(for..of 对它们有效),又是类数组的(它们有数值索引和 length 属性)。
- 两者不一定兼有:一个可迭代对象也许不是类数组对象。反之亦然,类数组对象可能不可迭代。例如,上面例子中的 range 是可迭代的,但并非类数组对象,因为它没有索引属性,也没有 length 属性。
三、Array.from()
Array.from
(obj [, mapFn, thisArg]) 接受一个对象,检查它是一个可迭代或类数组对象,然后创建一个新数组,并将该对象的所有元素复制到这个新数组。我们从其获取一个“真正的”数组,然后我们就可以对其调用数组方法。
- 对于类数组,会忽略掉length属性
- 对于可迭代对象,会获取其本身所有属性
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike);
// arr [ 'Hello', 'World' ]
//range是上面有symbol.iterator的例子
let arr_range = Array.from(range);
console.log("arr_range",arr_range)
//[1,2,3,4,5]
具体详解见专门文章