我是如何学会停止循环并爱上迭代器的
让我们都从繁忙的生活中抽出一点时间,回顾过去,想想我们浪费了多少时间来修复那些问题for loop。如果你的内存返回的是一个空数组,不妨看看这个。(剧透一下!它会将数组中两个相邻元素相加,并将第一个元素与最后一个元素配对。)
for (var i = 0, len = grid.length, j = len - 1, p1, p2; i < len; j = i++) {
p1 = grid[i];
p2 = grid[j];
sum += p1 + p2;
}
问题在于for loop
我认为最大的问题在于它们功能过于强大。你很容易配置错误,for loop这种配置乍一看似乎有效,但最终会在生产环境中引发严重问题。
简而言之,它的表面积
for loop非常大,因此会吸引很多昆虫。
功能替代方案
90% 的情况下,你总能把那个老旧的符号转换for loop成美观的斜杠.map。.reduce这种方法出错的情况非常少。
for (var i = 0; i < array.length; i++) {
array[i] = array[i] * 2;
}
array = array.map(o => o * 2);
可迭代方法
摆脱for loop旧设备时,功能性替代方案应该是你的首选,但有时你还是想坚持使用旧设备:
- 你认为性能至关重要。for 循环仍然是最快的,但优势非常小。
- 你想
break;/continue;轻松脱离循环。 - 某些复杂的对象(
Sets,,)并没有直接遍历它们的方法Map。NodeList
聪明的 ECMAScript 开发者们知道这些限制,而且可能还有更多限制,所以他们在 ES2015 中提出了一个迭代协议。
协议究竟是什么?
JavaScript 没有像其他语言那样的正式协议。你可以把它理解为一种约定俗成的做法,就像 Node 程序员喜欢遵循错误优先回调机制一样。
引入可迭代对象
JavaScript 中到处都是可迭代对象,你可能一直在不知不觉中使用它们。任何带有Symbol.iterator属性的对象都是可迭代的。
让我们来看最简单的可迭代对象——字符串!
str = 'hello world';
iterator = str[Symbol.iterator](); // StringIterator {}
iterator.next(); // gives `h`
...
...
iterator.next(); // gives `d`
iterator.next(); // gives `undefined` as no more string left.
JavaScript 中的字符串类型内置了迭代协议,这意味着我们现在可以说字符串是可迭代的。
Symbol 和 Symbol.iterator 分别是什么?
这个话题本身就值得写一篇文章,但简而言之,它Symbol解决了如何在对象中添加一个不希望与现有属性冲突的属性的问题。访问MDN 文档了解更多信息。
所以以后就不用再放
@@@@@\_my\_hidden\_property\_物品了!
Symbol.iterator是一个全局可用的常量,任何人都可以使用它来实现迭代协议。因此,您可以使用它来使您自己的对象实现迭代。
如何为我的自定义对象实现迭代协议?
class Rand {
[Symbol.iterator] () {
let count = 0;
return {
next: () => ({
value: count++,
done: count > 5
})
};
}
}
var rand = new Rand();
var iterator = rand[Symbol.iterator]();
iterator.next();// {value: 0, done: false}
iterator.next();// {value: 1, done: false}
// ..
iterator.next();// {value: 5, done: false}
iterator.next();// {value: undefined, done: true}
别被语法难倒。让我们来详细分析一下这个例子:
[Symbol.iterator] ()这种看起来有点奇怪的语法其实是 ES2015 中动态初始化属性的一种新方式。(点击此处了解更多信息。)- 该
Symbol.iterator方法必须返回一个对象{ next }(请记住,这完全是一种约定/协议)。我们称这个对象为 `Object`iterator。(下一节将详细介绍) .next()每次调用时,它都会简单地增加计数,当计数超过某个阈值时,它会切换done到另一个状态。truecount5
iterable和有什么区别iterator?
请跟我重复:
- Iterable是实现了迭代协议的对象。
string,,,都是可迭代对象!ArraySetMap
class Rand {
[Symbol.iterator] () { // Rand has `Symbol.iterator` method, hence it is an iterable!
let count = 0;
return { // The return value is called an `iterator`
next: () => ({
value: count++,
done: count > 5
})
};
}
}
- 迭代器是可迭代对象返回的对象
[Symbol.iterator]()。- 它包含有关当前迭代位置以及接下来要提供的值的有用状态信息。
- 任何迭代器都必须有一个
.next方法(记住,这都是一种约定俗成的做法),该方法用于获取下一个值。 - 该方法返回的对象
.next()必须是{value, done},其中value是当前值,done表示迭代是否结束。
var iterator = rand[Symbol.iterator](); // I am an iterator
iterator.next(); // {value: 0, done: false}
iterator.next(); // {value: 1, done: false}
...
iterator.next(); // {value: 4, done: false}
iterator.next(); // {value: undefined, done: true}
我能从这个复杂的流程中获得什么?
Array如果你在自定义对象中启用迭代,或者使用任何 JavaScript 内置的可迭代对象(例如`int`、 ` int` 或string` int`),你就能免费获得很多超能力。MapSet
1. 超能力:传播它
Rand还记得我们刚才定义的那个类吗?由于它是一个可迭代对象,因此它继承了扩散运算符的强大功能。访问MDN 文档了解更多关于扩散运算符的信息。
var rand = new Rand();
var myArray = [...rand]; // [0, 1, 2, 3, 4]
// string can also be used since it is an iterable
[..."kushan"]; // ["k", "u", "s", "h", "a", "n"]
注意:我们没有这样做[...rand[Symbol.iterator]()],因为...需要一个iterable而不是iterator。
2. 超能力:使用 Array.from
Array.from(rand); // [0, 1, 2, 3, 4]
Array.from("kushan"); // ["k", "u", "s", "h", "a", "n"]
3. 超能力:for 循环
for of是 ES2015 中引入的一种新的循环机制,它只支持可迭代对象。它会自动调用 `for` 循环Symbol.iterator,在后台存储迭代器,并.next为你调用 `for` 循环。当迭代器返回 `null` 时,循环就会停止{done:true}。
for(const v of rand) {
console.log(v);
}
/*Output*/
// 0
// 1
// ..
// 4
与此不同
spread operator,for of循环可以同时接受iterable或iterator。
var map = new Map([['a', 1], ['b', 2]]);
map[Symbol.iterator];// map is iterable because it has the `Symbol.iterator` key
// `for of` loop understands `iterable`
for (const [key, val] of map) {
console.log(key, val); // 'a', 1
}
// `for of` loop also understands iterators
var iterator = map[Symbol.iterator](); // returns an iterator
for (const [key, val] of iterator) {
console.log(key, val); // 'a', 1
}
// .keys() is a part of `Map` api
var keyIterator = map.keys(); // returns an iterator
for (const key of keyIterator) {
console.log(key); // 'a'
}
// .values() is a part of `Map` api
var valueIterator = map.values(); // returns an iterator
for (const val of valueIterator) {
console.log(val); // 1'
}
4. 超级大国:解构
这是我最喜欢的演示 ES 强大功能的例子之一iterables。ES2015 引入了解构赋值,它建立在可迭代对象之上。你只能对可迭代对象进行解构!
// array is iterable
[a, b] = [10, 20]; // a=10, b=20
// our custom class rand is Destructable :P
[a, b, c] = rand; // a = 0, b = 1, c = 2
// you can do fancy things like
[a, ...b] = rand; // a = 0, b = [1, 2, 3, 4]
概括
请分享一下你使用过程中还获得了哪些其他的免费超能力iterables。希望这篇文章能帮助你理解iterables。iterators
别忘了看看我之前的文章。
如果你喜欢这篇文章,请分享这篇文章,让更多人看到。
文章来源:https://dev.to/kepta/how-i-learned-to-stop-looping-and-love-the-iterator-463j