本文旨在分析理解 Iterators。 Iterators 是 JS中的新方法,可以用来循环任意集合。 在ES6中登场的Iterators。因其可被广泛使用,并且已在多处场景派上用场,
我们将从概念上理解迭代器是什么,以及在何处使用它们和示例。我们还将看到它在JS 中的一些实现。
假设有这样数组
const myFavouriteAuthors = [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
];
在某些情况下,希望返回数组中的所有单独值,以便在屏幕上打印它们、操作它们或对它们执行某些操作。
如何处理? 简单方法就是使用 for, while, for-of 方法。
如下:
现在,假设你拥有一个自定义数据结构来保存所有authors
myFavouriteAuthors 是一个对象,它包含另一个对象 allAuthors。allAuthors 包含三个数组,其中包含 fiction、scienceFiction 和 fantasy。
现在,如果要求你循环遍历 myFavouriteAuthors 以获得所有的author,你的方法是什么? 你可能会尝试一些循环组合来获得所有数据。
但是,如果你这样做了 ——
for (let author of myFavouriteAuthors) {
console.log(author)
}
// TypeError: {} is not iterable
你将得到一个类型错误,说明该对象不可迭代。让我们看看什么是可迭代的,以及如何使对象可迭代。
在上一节中看到了问题,从我们的自定义对象中获取所有的author 是不容易的。我们需要某种方法,通过它我们可以有序地获取内部数据。
我们在 myFavouriteAuthors 中添加一个返回所有作者的方法 getAllAuthors。如:
这是一个简单的方法。它帮我们完成了获取所有author的功能。但是,这种实现可能会出现一些问题:
getAllAuthors 的名称非常具体。如果其他人正在创建自己的 myFavouriteAuthors,他们可能会将其命名为retrieveAllAuthors。
作为开发人员,我们总是需要知道返回所有数据的特定方法,在本例中,它被命名为getAllAuthors。
getAllAuthors 返回的是字符串数组,如果另一个开发人员以这种格式返回一个对象数组,该怎么办:
[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]
开发人员必须知道返回所有数据的方法的确切名称和返回类型。
如果我们规定方法的名称和它的返回类型是固定不变的呢?
让我们将这个方法命名为 --- iteratorMethod
ECMA 也采取了类似的步骤来标准化在定制对象上循环的过程。但是,ECMA 没有使用名称 iteratorMethod,而是使用名称 Symbol.iterator。
Symbols 提供的名称是唯一的,不能与其他属性名称冲突。同时,Symbol.iterator 返回一个名为迭代器的对象,这个迭代器将拥有一个名为next的方法,该方法将返回一个具有键值为 value 和 done 的对象。
值键 value 包含当前值,它可以是任何类型的,done 是布尔值,它表示是否获取了所有的值。
下图可以帮助建立可迭代对象、迭代器和next之间的关系,这种关系称为迭代协议。
根据Axel Rauschmayer博士的《探索JS》一书:
可迭代是一种数据结构,它希望使其元素对外部可访问,通过实现一个关键字是Symbol.iterator的方法来实现,该方法是迭代器的工厂,也就是说,它将创建迭代器。
迭代器是一个指针,用于遍历数据结构的元素,我们将使用computed property语法来设置这个键,如下:
因此,正如我们在上一节学到的,我们需要实现一个名为Symbol.iterator的方法
在第4行,我们创建迭代器。它是一个定义了next方法的对象。next方法根据step变量返回值。在第25行,我们检索iterator,27 行,我们调用next方法,直到 done的值为 true。
这正是for-of循环中发生的事情,for-of接受一个迭代器,并创建它的迭代器,它会一直调用next(),直到 done为 true。
JS 中的很多对象都是可迭代的。它们可能不是很好的察觉,但是如果仔细检查,就会发现迭代的特征:
Arrays and TypedArrays
Strings —— 遍历每个字符或Unicode代码点
Maps —— 遍历其键-值对
Sets —— 遍历元素
arguments —— 函数中类似数组的特殊变量
dom elements (Work in Progress)
JS中使用迭代的其他一些结构是:
for-of -- for-of 循环需要一个可迭代的对象,否则,它将抛出一个类型错误。
for (const value of iterable) { ... }
数组解构 -- 由于可迭代性,会发生析构。让我们来看看:
const array = ['a', 'b', 'c', 'd', 'e'];
const [first, ,third, ,last] = array;
等价于:
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value
扩展操作符(…)
const array = ['a', 'b', 'c', 'd', 'e'];
const newArray = [1, ...array, 2, 3];
等价于:
const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true; nextValue = iterator.next()) {
newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)
Promise.all 和 Promise.race 接受可迭代对象
Maps 和 Sets
下面是一个实现,它使myFavouriteAuthors 具有可迭代性:
const myFavouriteAuthors = {
allAuthors: {
fiction: [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss'
],
scienceFiction: [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein'
],
fantasy: [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett'
],
},
[Symbol.iterator]() {
// 获取数组中的所有作者
const genres = Object.values(this.allAuthors);
// 存储当前类型和索引
let currentAuthorIndex = 0;
let currentGenreIndex = 0;
return {
// Implementation of next()
next() {
// 根据当前的索引获取对应的作者信息
const authors = genres[currentGenreIndex];
// 当遍历完数组 authors是,oNotHaveMoreAuthors 为 true
const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
if (doNothaveMoreAuthors) {
// 加一继续访问下一个
currentGenreIndex++;
// 重置
currentAuthorIndex = 0;
}
// 如果所有 genres 都遍历完了结,那么我们需要告诉迭代器不能提供更多的值。
const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
if (doNotHaveMoreGenres) {
return {
value: undefined,
done: true
};
}
// 如果一切正常,从当genre 返回 作者和当前作者索引,以便下次,下一个作者可以返回。
return {
value: genres[currentGenreIndex][currentAuthorIndex++],
done: false
}
}
};
}
};
for (const author of myFavouriteAuthors) {
console.log(author);
}
console.log(...myFavouriteAuthors)
通过本文获得的知识,你可以很容易地理解迭代器是如何工作的,这种逻辑可能有点难以理解。因此,理解这个概念的最佳方法是多多敲死代码,多多验证!
箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。
js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD
ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...
ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明
ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构
ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。
Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...
ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。
Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。
ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!