The concept 1.Iterator (walker) of
JavaScript original means "a collection of" data structures, primarily array ( Array
) and objects ( Object
), has added a ES6 Map
and Set
. So there is a set of four data, users can also use them in combination, define your own data structures, such as members of the array it is Map
, Map
members object. This requires a unified interface mechanism to deal with all the different data structures.
Iterator (Iterator) is one such mechanism. It is an interface that provides a unified mechanism to access a variety of different data structures. Any data structure as long as the deployment Iterator interface, to complete traversal operation (i.e., sequentially processed all members of the data structure).
Iterator role has three: one for the various data structures, to provide a unified, easy-access interface; second is to make members of the data structure can be arranged in a certain order; the third is ES6 create a new traverse command for...of
cycle , Iterator interface is mainly for for...of
consumption.
Iterator traversal is this.
(1) Create a pointer to the object , points to the start position of the current data structure. That is, on the object nature walker, is a pointer to the object.
(2) the first call object pointer next
method, the first pointer to a data structure member.
(3) a second pointer to the object call next
method, a pointer points to a data structure of the second member.
(4) continues to call the object pointer next
method, the end position until it points to the data structure.
Each call next
method, will return information about the current members of the data structure. Specifically, it is a return contains value
and done
the object two properties. Where value
property is the value of the current member, done
is a Boolean value that indicates whether the end of the traverse.
The following is a simulation next
example of a method return value.
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
The above code defines a makeIterator
function, it is generated a traversal function, simply sends a visitor object. Array ['a', 'b']
implementation of this function will return the object to traverse the array (i.e., the object pointer) it
.
Pointer to the object next
a method for moving the pointer. Initially, the pointer to the beginning of the array. Then, each call to next
the method, the pointer will point to the next member of the array. The first call, point a
; the second call, pointing b
.
next
Method returns an object that represents information about the current data members. This object has value
and done
two properties, value
property returns a member of the current position, done
is a Boolean value that indicates whether the end of the traverse, whether that is still necessary to call again next
method.
In summary, the pointer to the object call next
method, can be given in advance to traverse the data structure.
For walker objects, done: false
and value: undefined
attributes are omitted, so that the above makeIterator
function can be simplified into the following form.
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++]} :
{done: true};
}
};
}
Since only the Iterator interface standard is added to the data structure above, therefore, that traverse the data structure with which it is traversed, physically separate, you can write the corresponding data does not traverse the object structure, with a walker or simulation of a data structure of the object. The following is an infinite object traversing running example.
var it = idMaker();
it.next().value // 0
it.next().value // 1
it.next().value // 2
// ...
function idMaker() {
var index = 0;
return {
next: function() {
return {value: index++, done: false};
}
};
}
In the above example, it generates traversal function idMaker
, returns a visitor object (i.e. the object pointer). However, no corresponding data structures, or that, traversing the object itself describes a data structure out.
If TypeScript wording, traversal interface (the Iterable), the pointer object (the Iterator) and the next
method returns the values of the specifications may be described as follows.
interface Iterable {
[Symbol.iterator]() : Iterator,
}
interface Iterator {
next(value?: any) : IterationResult,
}
interface IterationResult {
value: any,
done: boolean,
}
2. Default Iterator interface
Iterator purpose interface, all data structures is to provide a unified access mechanism, i.e., for...of
the cycle (see below). When for...of
the loop a certain data structure traversal, the cycle will automatically look for Iterator interface.
A data structure as long as the deployment of the Iterator interface, we call this data structure is a "traversable" (iterable).
ES6 specified, the default Iterator interface deployed in the data structure of the Symbol.iterator
property, or that has a data structure as long as the Symbol.iterator
property can be considered to be "traversed" (iterable). Symbol.iterator
Attribute is itself a function of the current data structure is traversed to generate the default function. Implementation of this function will return an iterator. As for the attribute name Symbol.iterator
, it is an expression that returns Symbol
an object of iterator
property, which is a pre-defined type of special value Symbol, it should be placed in square brackets (see "Symbol" chapter).
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
The above code, the object obj
is traversable (Iterable), because of having Symbol.iterator
properties. Implementation of this property, it will return a visitor object. The fundamental characteristic of the object is to have a next
method. Each call next
method returns an information object represents the current members, with value
and done
two properties.
Some of the data structure includes a native ES6 Iterator interface (such as arrays), i.e. without any treatment, it can be for...of
loop through. The reason is that these data structures deployed native Symbol.iterator
properties (see below), some other data structures are not (such as object). Those who deployed Symbol.iterator
attribute data structure, called the deployment a traversal interface. Call this interface, it will return a visitor object.
Native data structure includes the following Iterator interface.
- Array
- Map
- Set
- String
- TypedArray
- arguments object function
- NodeList object
The following example is an array of Symbol.iterator
properties.
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }
The above code, the variable arr
is an array, having traversed the original interface gifted deployed in arr
the Symbol.iterator
properties above. So, call this property, you get a visitor object.
For native deployment data structure Iterator interface, do not write your own walker generating function, for...of
the cycle will automatically traverse them. In addition, other data structures (mainly object) Iterator interface, you need to own the Symbol.iterator
property above the deployment, this will be for...of
looping through.
Object (Object) is no reason why the default deployment Iterator interface, which is because the properties of the object first traversal, after which traverse the property is uncertain, developers need to manually specify. Essentially, the walker is a linear process, for any non-linear data structure, traversing the deployment interface, it is equivalent to conversion of a linear deployment. However, strictly speaking, the object traversing the deployment interface is not very necessary, because when an object is actually using the structure as a Map, ES5 Map no structure, and ES6 native offers.
If an object can be provided to for...of
the Iterator interface called loop must be in Symbol.iterator
the deployment method of traversing generated property (prototype object The method may also have chain).
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
The above codes are a class written deployment Iterator interface. Symbol.iterator
Attribute corresponds to a function that returns the current object traversing objects after execution.
The following are examples realized by a pointer to a structure walker.
function Obj(value) {
this.value = value;
this.next = null;
}
Obj.prototype[Symbol.iterator] = function() {
var iterator = { next: next };
var current = this;
function next() {
if (current) {
var value = current.value;
current = current.next;
return { done: false, value: value };
} else {
return { done: true };
}
}
return iterator;
}
var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);
one.next = two;
two.next = three;
for (var i of one){
console.log(i); // 1, 2, 3
}
The above code is first deployed in the prototype chain constructor Symbol.iterator
method call which returns a visitor object iterator
, call the object's next
methods, while a return value, the automatic internal pointer to the next instance.
Here is another object to add to the Iterator interface examples.
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 array-like objects (keys and values exist length
attributes), deployed Iterator interface, there is a simple method, it is Symbol.iterator
the method of direct reference to an array of interfaces Iterator.
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];
[...document.querySelectorAll('div')] // 可以执行了
NodeList object is an array of similar objects, having already traversed interface directly traverse. The above code, we will traverse the interface into its array of Symbol.iterator
attributes, we can see no effect.
Here is another call array-like object array Symbol.iterator
example of the method.
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
Note that ordinary objects to deploy an array of Symbol.iterator
methods, there is no effect.
let iterable = {
a: 'a',
b: 'b',
c: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // undefined, undefined, undefined
}
If the Symbol.iterator
corresponding method is not generated traversal function (i.e., returns an iterator object), explanation will be given engine.
var obj = {};
obj[Symbol.iterator] = () => 1;
[...obj] // TypeError: [] is not a function
The above code, the variables obj
of Symbol.iterator
the corresponding method is not generated traversal function, an error is reported.
With walker interfaces, data structures can be used for...of
to loop through (see below), it may also be used while
to loop through.
var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
var x = $result.value;
// ...
$result = $iterator.next();
}
In the above code, ITERABLE
it represents a certain data structure traversal, $iterator
it traversal object. Each traversing movement of the pointer object ( next
methods), are returned check value done
attribute, if not traverse end, the pointer moves to the next visitor object ( next
method), continuous cycle.
3. Call Iterator interface occasion
Some occasions call Iterator interface by default (ie Symbol.iterator
method), the following will be introduced in addition to the for...of
cycle, there are several other occasions.
(1) deconstruction assignment
When arrays and structures deconstruction Set assignment, call the default Symbol.iterator
method.
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a'; y='b'
let [first, ...rest] = set;
// first='a'; rest=['b','c'];
(2) Extended operator
Extended operator (...) will call the default Iterator interface.
// 例一
var str = 'hello';
[...str] // ['h','e','l','l','o']
// 例二
let arr = ['b', 'c'];
['a', ...arr, 'd']
// ['a', 'b', 'c', 'd']
Internal expansion operator code above is called Iterator interface.
In fact, this provides a simple mechanism, it can be deployed in any data structure Iterator interface, into an array. That is, whenever a data structure deployed Iterator interface, it can be extended using the operator, which is converted to an array.
let arr = [...iterable];
(3)yield*
yield*
Is back with a traversable structure, it calls the walker of the structure of the interface.
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 }
(4) Other occasions
Due to iterate calls traverse the interface, so any occasions to accept arrays as parameters, in fact, are called to traverse interface. Here are some examples.
- for...of
- Array.from()
- Map (), Set (), WeakMap (), WeakSet () ( for example
new Map([['a',1],['b',2]])
) - Promise.all()
- Promise.race()
4. string Iterator interface
String is an array-like objects, also having a native Iterator interface.
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
The above code, the call Symbol.iterator
returns an object traversing, the traverse this next method can be invoked, for the traversal of the string.
It may cover native Symbol.iterator
method, the purpose of modifying the behavior of the traverse.
var str = new String("hi");
[...str] // ["h", "i"]
str[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return { value: "bye", done: false };
} else {
return { done: true };
}
},
_first: true
};
};
[...str] // ["bye"]
str // "hi"
In the above code, the string str Symbol.iterator
method is modified, so extended operator ( ...
) Returns the value became bye
, and the string itself or hi
.
Generator function interface 5.Iterator
Symbol.iterator
The simplest implementation of the method, or the use of Generator function to introduce the next chapter.
let myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
}
[...myIterable] // [1, 2, 3]
// 或者采用下面的简洁写法
let obj = {
* [Symbol.iterator]() {
yield 'hello';
yield 'world';
}
};
for (let x of obj) {
console.log(x);
}
// "hello"
// "world"
In the above code, Symbol.iterator
the method almost without deploying any code, as long as the return value of the command given by the yield to each step.
6. return visitor object (), throw ()
In addition to traversing the object next
method, it may also have return
methods and throw
methods. If you write your own visitor object generating function, it next
is to be deployed, return
methods, and throw
whether the method of deployment is optional.
return
The method of using the occasion is, if the for...of
loop exits in advance (usually because of a mistake, or have break
statements), it will call return
the method. If an object before completing the traverse, need to be cleaned or release resources, you can deploy return
method.
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close();
return { done: true };
}
};
},
};
}
In the above code, the function readLinesSync
accepts a file object as an argument and returns a visitor object, which in addition to next
the method, also deployed return
method. The following two cases will trigger the execution return
method.
// 情况一
for (let line of readLinesSync(fileName)) {
console.log(line);
break;
}
// 情况二
for (let line of readLinesSync(fileName)) {
console.log(line);
throw new Error();
}
In the above code, after the first line of a case where the output file, will execute return
the method, close the file; Case 2 will be performed return
after the method to close the file, then an error is thrown.
Note that the return
method must return an object, which is determined by the specifications Generator.
throw
Generator function method is mainly used with the general object traversing less than this method. See "Generator Function" chapter.
7.for ... of circulation
A data structure as long as the deployment of the Symbol.iterator
property, it is considered to have iterator interface, you can for...of
loop through its members. In other words, for...of
the inner loop is a data structure called the Symbol.iterator
method.
for...of
Range of circulating arrays may be used include, Set, and Map structure, array-like some objects (such as arguments
objects, DOM NodeList objects), hereinafter the Generator objects, and character strings.
Array
Native array includes iterator
an interface (i.e., the default deployment Symbol.iterator
property), for...of
is generated by that interface on to traverse the recursive nature, may be proved by the following code.
const arr = ['red', 'green', 'blue'];
for(let v of arr) {
console.log(v); // red green blue
}
const obj = {};
obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);
for(let v of obj) {
console.log(v); // red green blue
}
In the above code, empty object obj
to deploy an array arr
of Symbol.iterator
properties, the results of obj
the for...of
cycle, and produces arr
exactly the same results.
for...of
Examples of loop array can replace forEach
method.
const arr = ['red', 'green', 'blue'];
arr.forEach(function (element, index) {
console.log(element); // red green blue
console.log(index); // 0 1 2
});
JavaScript original for...in
cycle, can only obtain key subjects you can not get the key directly. ES6 provided for...of
loop, allowing traversal key is obtained.
var arr = ['a', 'b', 'c', 'd'];
for (let a in arr) {
console.log(a); // 0 1 2 3
}
for (let a of arr) {
console.log(a); // a b c d
}
The above codes indicate, for...in
loop reads the key name, for...of
loop reading key. If you want to through for...of
circulation, access to the index of the array, can make use of the array instance entries
method and the keys
method (see "Extended arrays" chapter).
for...of
Interface call loop traversal, the traversal interface returns an array property with only numerical index. With this for...in
cycle is not the same.
let arr = [3, 5, 7];
arr.foo = 'hello';
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo"
}
for (let i of arr) {
console.log(i); // "3", "5", "7"
}
In the above code, for...of
the loop does not return an array arr
of foo
properties.
Set structure and Map
Set Map native structure having Iterator interface, may be used as for...of
loop.
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
console.log(e);
}
// Gecko
// Trident
// Webkit
var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262
The above code demonstrates how to iterate Set structure and Map structure. There are two noteworthy, first traversal order is in accordance with the respective members are added sequentially into the data structure. Secondly, Set structure traversal, returns a value, while Map structure traversal, returns an array of two members of the array are key names and values of the current members of the Map.
let map = new Map().set('a', 1).set('b', 2);
for (let pair of map) {
console.log(pair);
}
// ['a', 1]
// ['b', 2]
for (let [key, value] of map) {
console.log(key + ' : ' + value);
}
// a : 1
// b : 2
Calculating the generated data structure
Some of the data structure is based on existing data structure, generated computationally. For example, ES6 array, Set, Map deploy the following three methods return visitor object after the call.
entries()
It returns an object traversing for traversing[键名, 键值]
an array of. An array of index values for the key name is; for Set, key name with the same key. Map Iterator interface structure, the default is to call theentries
method.keys()
Returns an iterator object that is used to traverse all the keys.values()
It returns an object traversing for traversing all keys.
After three generation traverse object method call, traversed by the generated data structure are calculated.
let arr = ['a', 'b', 'c'];
for (let pair of arr.entries()) {
console.log(pair);
}
// [0, 'a']
// [1, 'b']
// [2, 'c']
Similar arrays of objects
Array-like object comprises a number of categories. The following is for...of
recycled for use in a string, DOM NodeList objects, arguments
an example of the object.
// 字符串
let str = "hello";
for (let s of str) {
console.log(s); // h e l l o
}
// DOM NodeList对象
let paras = document.querySelectorAll("p");
for (let p of paras) {
p.classList.add("test");
}
// arguments对象
function printArgs() {
for (let x of arguments) {
console.log(x);
}
}
printArgs('a', 'b');
// 'a'
// 'b'
For strings, for...of
the cycle there is a feature that will correctly identify the 32-bit UTF-16 character.
for (let x of 'a\uD83D\uDC0A') {
console.log(x);
}
// 'a'
// '\uD83D\uDC0A'
Not all objects have a similar array Iterator interface, a simple solution is to use the Array.from
method to be converted to an array.
let arrayLike = { length: 2, 0: 'a', 1: 'b' };
// 报错
for (let x of arrayLike) {
console.log(x);
}
// 正确
for (let x of Array.from(arrayLike)) {
console.log(x);
}
Objects
For ordinary objects, for...of
the structure can not be used directly, will complain, must be deployed in order to use the Iterator interface. However, in this case, the for...in
cycle can still be used to traverse the keys.
let es6 = {
edition: 6,
committee: "TC39",
standard: "ECMA-262"
};
for (let e in es6) {
console.log(e);
}
// edition
// committee
// standard
for (let e of es6) {
console.log(e);
}
// TypeError: es6[Symbol.iterator] is not a function
The above codes indicate ordinary objects for...in
cycle may traverse keys, for...of
the cycle error.
One solution is to use Object.keys
the method to generate the object of an array keys, and then traverse the array.
for (var key of Object.keys(someObject)) {
console.log(key + ': ' + someObject[key]);
}
Another method is to use the Object Generator function will repackage.
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(obj)) {
console.log(key, '->', value);
}
// a -> 1
// b -> 2
// c -> 3
Comparison with other traversal syntax
An array, for example, JavaScript offers a variety of traversal syntax. The most original wording is for
circulating.
for (var index = 0; index < myArray.length; index++) {
console.log(myArray[index]);
}
The wording is too much trouble, and therefore provides an array of built-in forEach
method.
myArray.forEach(function (value) {
console.log(value);
});
The wording of the question that can not be half-way out of the forEach
loop, break
the command or return
commands are not effective.
for...in
Keys can traverse the loop array.
for (var index in myArray) {
console.log(myArray[index]);
}
for...in
Circulation has several drawbacks.
- The digital array keys, but the
for...in
cycle is a character string as keys "0", "1", "2" and so on. for...in
Not only traversal cycle numeric keys, other keys will be added manually traverse, and even key on the prototype chain.- In some cases,
for...in
the cycle will traverse the keys in any order.
In short, for...in
the cycle is mainly designed to traverse the object, it does not apply to traverse the array .
for...of
Cycle compared to the above several approaches, there are some significant advantages.
for (let value of myArray) {
console.log(value);
}
- It has with
for...in
the same simple syntax, but withoutfor...in
those drawbacks. - Unlike
forEach
the method, which may be associated withbreak
,continue
andreturn
used in conjunction. - Through all the data structure provides a unified user interface.
The following is a break statement, out of for...of
Examples of the cycle.
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
The above example will output the Fibonacci sequence of 1,000 or less entries. If the current item is greater than 1000, will use the break
statement to jump out of for...of
the loop.