Wechat search [Great Relocation to the World], I will share with you the front-end industry trends, learning methods, etc. as soon as possible.
This article GitHub https://github.com/qq449245884/xiaozhi has been included, there are complete test sites, materials and my series of articles for interviews with first-line manufacturers.
Lazy evaluation
Lazy evaluation
often translated as "lazy evaluation" or "lazy evaluation", which refers to evaluating the value of an expression only when it is actually needed.
The opposite of ---4d4777b6e199e320b0efc05f1d55b700 惰性求值
is 及早求值(eager evaluation)
Early evaluation, also known as 贪婪求值
(greedy evaluation) or strict evaluation, is the evaluation of most traditional programming languages Strategy.
The benefits of taking full advantage of the features of 惰性求值
are mainly reflected in the following two aspects:
- Avoid unnecessary calculations and bring performance improvements.
- Save space and make infinite loop data structures possible.
iterator
Iterators in ES6 enable lazy evaluation and creation of user-defined sequences of data. Iteration is a mechanism for iterating over data. An iterator is a pointer used to traverse the elements of a data structure (called Iterable
), yielding a pointer to a sequence of values.
An iterator is an object that can be iterated over. It abstracts the data container so that it behaves like an iterable object.
Iterators do not compute the value of each item when instantiated, and only generate the next value when requested. This is very useful, especially for large datasets or sequences with infinite elements.
iterable object
Iterables are data structures whose elements are expected to be publicly accessible. A lot of objects in JS are iterable, they may not be very noticeable, but if you examine carefully, you will find the characteristics of iteration:
- new Map([iterable])
- new WeakMap([iterable])
- new Set([iterable])
- new WeakSet([iterable])
- Promise.all([iterable])
- Promise.race([iterable])
- Array.from([iterable])
There also needs to be an iterable object, otherwise, it will throw a type error, for example:
-
for ... of
-
...
(expand operator)
const [a, b, ..] = iterable
(destructuring assignment) -
yield*
(Generator)
There are already many built-in iterables in JavaScript:
String
, Array
, TypedArray
, Map
, Set
。
iterative protocol
Iterators and iterables follow 迭代协议
.
A protocol is a set of interfaces and specifies how to use them.
Iterators follow the iterator protocol, and iterables follow the iterable protocol.
iterable protocol
To make an object iterable, it must implement an iterator method via Symbol.iterator
which is a factory for iterators.
With TypeScript, the iterable protocol looks like this:
interface Iterable {
[Symbol.iterator]() : Iterator;
}
Symbol.iterator]()
is a parameterless function. Calling it on an iterable means we can access the iterable by this
which can be a regular function or a generator function.
iterator protocol
The iterator protocol defines standard methods for producing sequences of values.
In order for an object to be an iterator, it must implement the next()
method. Iterators can implement the return()
method, which we will discuss later in this article.
With TypeScript, the iterator protocol looks like this:
interface Iterator {
next() : IteratorResult;
return?(value?: any): IteratorResult;
}
IteratorResult
is defined as follows:
interface IteratorResult {
value?: any;
done: boolean;
}
-
done
notifies the consumer whether the iterator has been used,false
indicates that there are still values to be generated,true
indicates that the iterator has ended. -
value
can be any JS value, it is the value displayed to the consumer.
When done
is true
, ---e333ba7329e5dbcbff36eb14be317d81 value
can be omitted.
combination
Iterators and iterable objects can be represented by the following diagram:
case
The basic knowledge has been introduced, and then, let's deepen our image with some examples.
range iterator
Let's start with a very basic iterator, the createRangeIterator
iterator.
We manually call it.next()
to get the next IteratorResult
. The last call returned {done:true}
, which means that the iterator is now used and no longer yields any value.
function createRangeIterator(from, to) {
let i = from;
return {
next() {
if (i <= to) {
return { value: i++, done: false };
} else {
return { done: true };
}
}
}
}
const it = createRangeIterator(1, 3);
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
iterable range iterator
Earlier in this article, I mentioned that some statements in JS require an iterable object. So our previous example will not work when used with the for ... of
loop.
But creating objects that conform to 迭代器
and 可迭代协议
is very easy.
function createRangeIterator (from, to) {
let i = from
return {
[Symbol.iterator] () {
return this
},
next() {
if (i <= to) {
return { value: i++, done: false }
} else {
return { done: true }
}
}
}
}
const it = createRangeIterator(1, 3)
for (const i of it) {
console.log(i)
}
infinite sequence iterator
Iterators can represent sequences of unlimited size because they only compute values when needed.
Be careful not to use the spread operator ( ...
) on infinite iterators, JS will try to consume the iterator and since the iterator is infinite it will never end. So your app will crash because memory is exhausted 😱
The same is true for the for ... of
loop, so make sure to exit the loop:
function createEvenNumbersIterator () {
let value = 0
return {
[Symbol.iterator] () {
return this
},
next () {
value += 2
return { value, done: false}
}
}
}
const it = createEvenNumbersIterator()
const [a, b, c] = it
console.log({a, b, c})
const [x, y, z] = it
console.log({ x, y, z })
for (const even of it) {
console.log(even)
if (even > 20) {
break
}
}
close iterator
We mentioned earlier that iterators can optionally use the return()
method. Use this method when the iterator has not iterated until the end, and let the iterator clean up.
for ... of
The loop can terminate the iteration earlier by:
- break
- continue
- throw
- return
function createCloseableIterator () {
let idx = 0
const data = ['a', 'b', 'c', 'd', 'e']
function cleanup() {
console.log('Performing cleanup')
}
return {
[Symbol.iterator]() { return this },
next () {
if (idx <= data.length - 1) {
return { value: data[idx++], done: false }
} else {
cleanup()
return { done: true }
}
},
return () {
cleanup()
return { done: true }
}
}
}
const it = createCloseableIterator()
for (const value of it) {
console.log(value)
if (value === 'c') {
break
}
}
console.log('\n----------\n')
const _it = createCloseableIterator();
for (const value of _it) {
console.log(value);
}
- If you know that the iterator has ended, manually call the
cleanup()
function. - If done abruptly,
return()
works and cleans up for us.
💥 Extra Content
If you've made it this far, let's take a look at some extras.
combiner
A combinator is a function that combines existing iterables together to create a new iterable.
Therefore, we were able to create many utility functions. What about map
or filter
? Take a look at the code below and take a minute to understand it.
function createEvenNumbersIterator() {
let value = 0;
return {
[Symbol.iterator]() {
return this;
},
next() {
value += 2;
return { value, done: false };
}
}
}
function map(fn, iterable) {
const iter = iterable[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
const n = iter.next();
if (!n.done) {
return { value: fn(n.value), done: false };
} else {
return { done: true };
}
}
}
}
function filter(fn, iterable) {
const iter = iterable[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
const n = iter.next();
if (!n.done) {
if (fn(n.value)) {
return { value: n.value, done: false };
} else {
return this.next();
}
} else {
return { done: true };
}
}
}
}
function take(n, iterable) {
const iter = iterable[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
if (n > 0) {
n--;
return iter.next();
} else {
return { done: true };
}
}
}
}
function cycle(iterable) {
const iter = iterable[Symbol.iterator]();
const saved = [];
let idx = 0;
return {
[Symbol.iterator]() {
return this;
},
next() {
const n = iter.next();
if (!n.done) {
saved[idx++] = n.value;
return { value: n.value, done: false };
} else {
return { value: saved[idx++ % saved.length], done: false };
}
}
}
}
function collect(iterable) {
// consumes the iterator
return Array.from(iterable);
}
const evenNumbersIterator = createEvenNumbersIterator();
const result = collect( // 7. and collect the result
filter( // ⬆️ 6. keep only values higher than 1
val => val > 1, map( // ⬆️ 5. divide obtained values by 2
val => val / 2, take( // ⬆️ 4. take only six of them
6, cycle( // ⬆️ 3. make an infinite cycling sequence of them
take( // ⬆️ 2. take just three of them
3, evenNumbersIterator // ⬆️ 1. infinite sequence of even numbers
)
)
)
)
)
);
console.log(result);
That's a whole bunch of code, and soon we'll see how to refactor all of this using generators and functional programming concepts. Stay tuned, and keep an eye out for my follow-up article, we still have a lot to talk about.
comminicate
If you have dreams and dry goods, you can search for [Great Move to the World] on WeChat and pay attention to this Shawanzhi who is still washing dishes in the early hours of the morning.
This article GitHub https://github.com/qq449245884/xiaozhi has been included, there are complete test sites, materials and my series of articles for interviews with first-line manufacturers.
The bugs that may exist in editing cannot be known in real time. In order to solve these bugs afterwards, a lot of time is spent on log debugging. By the way, here is a useful BUG monitoring tool , Fundebug .
Author: MelkorNemesis Translator: Front-end Xiaozhi Source: medium
Original: https://medium.com/@MelrNemesis/javascript-lazy-evaluation-iterables-iterators-e0770a5de96f
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。