Iterator Iterator
1. What is an traverser
- An Iterator is an interface.
Provides a unified access mechanism for various data structures
- Any data structure is deployed
The Iterator interface
, can complete the traversal operation, saidTo traverse
(iterable) - The Iterator interface is used for ES6
for... of
Use. When using the for… When the of loop iterates over some data structure, the loop automatically looks for the Iterator interface.
2. How is the traverser interface implemented
ES6 specifies that the default Iterator interface is deployed on the symbol. Iterator property, which is a function that returns an Iterator object.
Traverser object characteristics: with the next method, each call to the next method will return an information object representing the current member, with value and done properties.
Relationships: Implement the Iterator interface -> deploy the symbol. Iterator property -> This method returns the Iterator object -> traversable -> for... Of traversal
ES6 also provides 11 built-in Symbol values that point to methods used internally in the language to implement. ES6 hasn’t been exposed to developers before. The symbol. iterator property is a special js value of type Symbol that points to the object’s default traverser method
- An example of a custom Iterator interface
let obj = {
[Symbol.iterator]: function () {
let i = 0;
return {
next: function () {
if (i < 3) {
i++;
return {
value: i,
done: false}; }else {
return {
value: undefined.done: true}; }}}; }};for (let i of obj) {
console.log(i); // 1 2 3 (note that done is true, the for loop terminates, does not get this return result!)
}
Copy the code
- Simulate the next operation process of traverser to traverse the number group
function makeIterator(array) {
let nextIndex = 0;
return {
next: function () {
return nextIndex < array.length
? { value: array[nextIndex++], done: false}, {value: undefined.done: true}; }}; }let it = makeIterator(["a"."b"]);
it.next(); // { value: "a", done: false }
it.next(); // { value: "b", done: false }
it.next(); // { value: undefined, done: true }
Copy the code
But you can’t for… Of traversal, because it is not deployed on the symbol. iterator property.
3. Simple understanding: Iteration protocol
Iterative agreement
- Iterable protocol: Allows JS objects to define their iterative behavior, that is, to become iterable
- Iterable: the @@iterator method must be implemented, accessible through the constant symbol. iterator
- Iterator protocol: Implements a next() method with the following semantics
- Next () method: returns an object with value, done attributes
The @@ Symbol is exposed in JS through the Symbol constructor, for example, @@toprimitive is exposed as symbol.toprimitive
Data structures with Iterator interfaces
- Some ES6 data structures have the Iterator interface natively (with the symbol. Iterator property deployed) that returns an Iterator object
- Can be used for… The of loop traverses
- But there are some data structures that are not deployed (such as objects)
A data structure that has an Iterator interface natively:
- Array
- String
- Map
- Set
- TypedArray
- Array-like objects: Arguments objects for functions, NodeList objects
- The Generator Generator
1. Array.prototype[Symbol.iterator]()
- The array native has a traverser interface, deployed on the symbol.iterator property. Calling this property yields the traverser object
- The traverser interface of an array returns only properties with numeric indexes
let arr = ["a"."b"."c"];
let it = arr[Symbol.iterator](); // Array Iterator {}
it.next(); // { value: 'a', done: false }
Array.prototype[Symbol.iterator](); Array Iterator {}
// Only attributes with numeric indexes are returned
let arr = [3.5.7];
arr.foo = "hello";
for (let i of arr) {
console.log(i); // "3", "5", "7"
}
Copy the code
- The new method in ES6 also returns an Array Iterator
Array.prototype.keys() Traversal of key names array.prototype. values() traversal of key values array.prototype. entries() traversal of key value pairs
let array1 = ["a"."b"."c"];
let iterator = array1.entries();
for (let [key, value] of iterator) {
console.log([key, value]);
// [0,'a']
// [1,'b']
// [2,'c']
}
// Manual traversal is also possible
iterator.next();
Copy the code
2. String.prototype[Symbol.iterator]()
A string is also an array-like object that also natively has an Iterator interface. The default iterator for a String returns each code point of the String in turn.
let someString = "hi";
let i = someString[Symbol.iterator](); // String Iterator {}
i.next(); // {value:'h', done:false}
i.next(); // {value:'i', done:true}
i.next(); // {value:undefined, done:true}
Copy the code
Example: Override the native symbol. iterator method and modify the iterator behavior
let str = new String("hi");
[...str]; // ["h", "i"]
// Redefining the symbol. iterator method will affect the behavior of the original built-in syntax structure!
str[Symbol.iterator] = function () {
return {
// Return the string "bye" only once
next: function () {
if (this._first) {
this._first = false;
return { value: "bye".done: false };
} else {
return { done: true}; }},_first: true}; }; [...str];// ["bye"]
Copy the code
3. An array-like object
- DOM NodeList objects are array-like objects with a traversal interface that can be traversed directly
The arguments object for the function
Also like an array of objects, with traversal interface, can be directly traversal- For the other
An array-like object
An easy way to deploy the Iterator interface is to refer directly to the symbol. Iterator methodThe symbol. iterator method for arrays
. butAn ordinary object deploys the symbol. iterator method of an array, with no effect
.
// arguments object for the function
(function () {
for (let argument of arguments) {
console.log(argument); / / 1 2 3
}
})(1.2.3);
// 2. NodeList object
let nodeList = document.querySelectorAll("div");
for (let item of nodeList) {
console.log(item); // Prints the page's div element
}
// 3. array like object
let arrLikeObj = {
0: "aaa".1: "bbb".length: 2};for (let item of arrLikeObj) {
console.log("item:", item); ArrLikeObj is not iterable
}
// deploy the Iterator interface: 1) set the Symbol. Iterator method 2) array. from method to convert it to an Array
arrLikeObj[Symbol.iterator] = Array.prototype[Symbol.iterator];
for (let item of arrLikeObj) {
console.log(item); // aaa bbb
}
/ / or
for (let item of Array.from(arrLikeObj)) {
console.log(item); // aaa bbb
}
Copy the code
4. Objects (no native deployed traverser)
- For objects that do not deploy the Iterator interface, you need to deploy the Symbol. Iterator property. The of loop traverses.
let obj = {
data: ["hello"."world"],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false}; }else {
return { value: undefined.done: true}; }}}; }};for (let item of obj) {
console.log(item); // "hello" "world"
}
Copy the code
- Why does the object have no traverser interface deployed?
Here are the reasons for thinking:
- Use for… Of traverses the default behavior of the object. There are many situations to consider, such as own property or property on the prototype, whether enumerable, traversing different combinations of key/value, etc. It is difficult to define the default behavior
- ES6 natively provides the Map structure, which improves the usage of objects, and can also be used
keys()/values()/entries()
Complete various forms of traversal. The traversal order of a Map is the insertion order.
So how to achieve object traversal, give us a lot of available scenes: ↓↓↓
for (const k of Object.keys(obj)) ... // enumerable own keys
for (const [k, v] of Object.entries(obj)) ... // enumerable own [key, value]
for (const k of Object.getOwnPropertyNames(obj)) // all own keys
for (const s of Object.getOwnPropertySymbols(obj)) // all own symbols
for (const k of Reflect.ownKeys(obj)) // all own keys (include symbo
Copy the code
Why are Objects not Iterable in JavaScript?
5. Map and Set structures
- Instances of Set and Map(new Map()), natively with the Iterator interface
- The traversal is done in the order in which each member is added to the data structure
- The three instance methods of Map and Set, which also return traversers, can all use for… Of traversal
- Map.prototype.keys()/values()/entries()
- Set.prototype.keys()/values()/entries()
- The Set structure traverses, returning a value; The Map structure traverses, returning an array whose two members are the key name and key value of the current Map member.
// 1. Set instance
let set = new Set([1.2.3.3]); / / Set (3) {1, 2, 3}
for (let i of set) {
console.log(i); / / 1 2 3
}
// The instance method returns an traverser
set.values(); / / SetIterator {1, 2, 3}
for (let i of set.values()) {
console.log(i); / / 1 2 3
}
// 2. Map instance
let map = new Map([["name".1],
["age".2]]);// Map(2) {"name" => 1, "age" => 2}
for (let i of map) {
console.log(i); // [name,1] [age,2]
}
// The instance method returns an traverser
map.keys(); // MapIterator {name,age}
for (let i of map.keys()) {
console.log(i); // name age
}
map.entries(); // MapIterator {"name" => 1, "age" => 2}
for (let i of map.entries()) {
console.log(i); / / [name, 1] [age, 2])
}
Copy the code
- The default traverser interface for a Set is its values method; The default traverser interface for a Map is its Entries method. This means that you can omit the values/entries method and simply use for… Of loops through Set/ Map.
Set.prototype[Symbol.iterator] === Set.prototype.values; // true
Map.prototype[Symbol.iterator] === Map.prototype.entries; // true
Copy the code
6. Use generators
1. The basic
- The Generator function is a state machine that encapsulates multiple internal states
- Executing the Generator function returns one
Traverser object
Can traverse each state inside the Generator function in turn - Characteristics of the
- There is an asterisk between the function keyword and the function name
- Use yield expressions inside the function body to define different internal states (yield: “output”)
- When a Generator function is called, it does not execute and returns not the result of the function’s operation, but instead
Returns a pointer object to the internal state (traverser object)
. The next method of the traverser object must be called to move the pointer to the next state
function* helloWorldGenerator() {
yield "hello";
yield "world";
return "ending";
}
let hw = helloWorldGenerator();
hw.next(); // { value: 'hello', done: false }
hw.next(); // { value: 'world', done: false }
hw.next(); // { value: 'ending', done: true }
hw.next(); // { value: undefined, done: true }
Copy the code
2. Relationship with the Iterator interface
Since a Generator function is an iterator Generator function (which returns an iterator object), you can assign Generator to the Symbol. Iterator property of the object, thus making the object have an Iterator interface.
var myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
// The myIterable object has an Iterator interface and can be... Operator traversal
[...myIterable]; / / [1, 2, 3]
Copy the code
for… The of loop automatically iterates over the Iterator generated when the Generator function is running, and there is no need to call the next method.
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1, 2, 3, 4, 5
Copy the code
3. Traversal objectreturn()
.throw()
return()
The method must return an object, as Generator syntax dictates- If the for… The return() method is called when the of loop exits prematurely (usually because of an error or a break statement)
- The return() method can be deployed if an object needs to clean up or release resources before completing traversal
throw()
The method is used primarily in conjunction with Generator functions and is not used for general traverser objects. See the chapter “Generator functions”.
let obj = {
data: ["a"."b"."c"."d"],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false}; }else {
return { value: undefined.done: true}; }},return() {
console.log("done");
return { done: true}; }}; }};for (let i of obj) {
console.log(i);
break;
}
Copy the code
3. Scenarios for calling the Iterator interface
There are situations where the Iterator interface (symbol. Iterator method) is called by default.
1. Deconstruct assignment
The symbol. iterator method is called by default when destructuring arrays and Set structures.
let set = new Set().add("a").add("b").add("c"); // Set(3) {"a", "b", "c"}
let [x, y] = set; // x='a'; y='b'
let [first, ...rest] = set; // first='a'; rest=['b','c'];
Copy the code
Note that destructuring assignment of objects does not work because Object does not deploy the Iterator interface
2. Extend operators
Any data structure with the Iterator interface deployed can be converted to a true array using the extension operator
// String to array
var str = "hello";
[...str]; // ['h','e','l','l','o']
// Class array object to array
let nodeList = document.querySelectorAll("div");
[...nodeList];
/ / function Generator
const go = function* () {
yield 1;
yield 2;
yield 3;
};
[...go()]; / / [1, 2, 3]
Copy the code
3. yield*
expression
Yield * is followed by a traversable structure, which invokes the traverser interface of that structure.
let generator = function* () {
yield 1;
yield* [2.3.4];
yield 5;
};
var iterator = generator();
iterator.next(); // { value: 1, done: false }
iterator.next(); // { value: 2, done: false }
iterator.next(); // { value: 3, done: false }
iterator.next(); // { value: 4, done: false }
iterator.next(); // { value: 5, done: false }
iterator.next(); // { value: undefined, done: true }
Copy the code
4. Cases where arrays are accepted as arguments
Any situation that takes an array as an argument actually calls the traversal interface (traversal of an array calls the traversal interface)
- Array.from()
- new Map(), new Set(), new WeakMap(), new WeakSet()
- Promise. All (), Promise. Race ()
- for… of
1. Array.from()
Array.from can be converted to an Array for any data structure that has an Iterator interface deployed
Array.from("hello");
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(["a"."b"]);
Array.from(namesSet); // ['a', 'b']
Copy the code
2. for… of
for… Inside the of loop is the symbol. iterator method of the data structure
- The characteristics of
- You can use
break
.continue
或return
Terminate the loop - Generators should not be reused. After exiting the loop, the generator closes and tries to iterate again without producing any further results.
let array1 = ["a"."b"."c"];
let iterator = array1.entries();
for (let [key, value] of iterator) {
console.log([key, value]);
}
// If the call continues
iterator.next(); // {value: undefined, done: true}
Copy the code
for... of
与for... in
The difference between
for... in
- Loops are designed primarily for traversing objects, not for traversing groups of numbers
for... in
Statements in any orderIterate over the enumerable properties of an object
.for... in
Loop read key names
for... of
Loop read key valuesfor... of
Statement traverses the iterablefor... of
Loop read key values- If you want to get through
for... of
Loop over the index of an array, using the array instanceEntries method
和Keys methods
The underlying principle can be referred to:
Array Iterator Objects