This article mainly introduces you to the learning summary of ES6 iterator (Iterator) and for.of loop usage. The editor thinks it is quite good, so I will share it with you now and give it as a reference. Let’s follow the editor to take a look, I hope it can help everyone.
1. What is an iterator?
The concept of generators is available in Java, Python and other languages, and ES6 has also been added to JavaScript. Iterator allows us to avoid the need to initialize the collection and index variables. Instead, we use the next method of the iterator object to return the value of the next item in the collection, which is biased toward programming.
Iterators are objects with special interfaces. Contains a next() method. The call returns an object containing two attributes, namely value and done. Value represents the value of the current position, and done represents whether the iteration is complete. When it is true, calling next is invalid.
Traversing collections in ES5 usually uses a for loop. Arrays also have forEach methods, and objects are for-in. Map and Set are added in ES6, and iterators can handle all collection data in a unified way. Iterator is an interface. As long as your data structure exposes an iterator interface, iteration can be completed. ES6 created a new traversal command for...of loop, and the Iterator interface is mainly used for consumption by for...of.
2. How to use iterator?
1. Default Iterator interface
As long as the data structure deploys the Iterator interface, we will make this data structure "traversable" (Iterable). ES6 stipulates that the default Iterator interface is deployed in the Symbol.iterator property of the data structure. In other words, as long as a data structure has Symbol.iterator data, it can be considered "traversable" (iterable).
Native data structure that can be consumed by for...of
Array
Map
Set
String
TypedArray (a generic fixed-length buffer type that allows reading from the buffer Binary data)
The arguments object in the function
NodeList object
can be seen above There is no object (Object) in the native data structure. Why?
That’s because the order of traversal of object properties is uncertain and needs to be specified manually by the developer. In essence, the traverser is a linear process. For any non-linear data structure, deploying the traverser interface is equivalent to deploying a linear transformation.
Do the following processing to make the object available for for...of consumption:
// code1 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
2. When calling the Iterator interface
(1) Destructuring assignment
// code2 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) Extension operator
// code3 // 例一 var str = 'hello'; [...str] // ['h','e','l','l','o'] // 例二 let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
(3) Yield* expression in the Generator function (introduced in the next chapter)
// code4 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
for..of
Array.from
Map(), Set(), WeakMap (), WeakSet()
Promise.all()
Promise.race()
3. Advantages of for...of loop
Let’s first look at the disadvantages of the array forEach method:
// code5 myArray.forEach(function (value) { console.log(value); });
The problem with this writing method is that it cannot jump out of the forEach loop midway, the break command or None of the return commands take effect.
Look again, the shortcomings of the object for...in loop:
for (var index in myArray) { console.log(myArray[index]); };
The key name of the array is a number, but the for...in loop is based on Strings as key names, "0", "1", "2", etc.
The for...in loop can not only traverse numeric key names, but also traverse manually added period recommendations, even keys on the prototype chain.
In some cases, the for...in loop session traverses the key names in any order
for...in traversal is mainly for Designed for traversing objects, not suitable for traversing arrays
So, what are the significant advantages of for...of?
Has the same concise syntax as for...in, but does not have the shortcomings of for...in
Different from the forEach method, It can be used with break, continue and return
Provides a unified operation interface for traversing all data structures
for (var n of fibonacci) { if (n > 1000) { break; console.log(n); } }
4. Each data type How to use for...of loop?
(1) Array
for...of loop allows traversing the array to obtain key values
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 }
for...of loop calls the traverser interface, array The traverser interface only returns values with numeric indexes
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" }
(2) Map and Set structures
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
As can be seen from the above code, for...of loop traversal When traversing the Map and Set structures, the order of traversal is based on the order in which each member is added to the data structure. When traversing the Set structure, it returns a value, while when traversing the Map structure, it returns an array. The two members of the array are respectively The key name and key value of the current Map member.
(3) Array-like object
String
// 普通的字符串遍历 let str = "yuan"; for (let s of str) { console.log(s); // y u a n } // 遍历含有 32位 utf-16字符的字符串 for (let x of 'a\uD83D\uDC0A') { console.log(x); } // 'a' // '\uD83D\uDC0A'
DOM NodeList object
let paras = document.querySelectorAll("p"); for (let p of paras) { p.classList.add("test"); }
arguments object
function printArgs() { for (let x of arguments) { console.log(x); } } printArgs("a", "n"); // "a" // "n"
Traversal processing of array objects without Iterator interface class
Borrow Array.from method processing
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); }
(4) Object
For ordinary objects, you cannot directly use for...of to traverse, otherwise an error will be reported, and the Iterator interface must be deployed to use it. The following two methods are deployed:
// 方法一:使用 Object.keys 方法讲对象的键名生成一个数组 for (var key of Object.keys(someObject)) { console.log(key + ": " + someObject[key]); } // 方法二:使用Generator 函数将对象重新包装一下 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
3. Iterator application example
1、斐波那契数列
下面我们就使用迭代器来自定义自己的一个斐波那契数列组,我们直到斐波那契数列有两个运行前提,第一个前提是初始化的前两个数字为0,1,第二个前提是将来的每一个值都是前两个值的和。这样我们的目标就是每次都迭代输出一个新的值。
var it = { [Symbol.iterator]() { return this }, n1: 0, n2: 1, next() { let temp1 = this.n1, temp2 = this.n2; [this.n1, this.n2] = [temp2, temp1 + temp2] return { value: temp1, done: false } } } for (var i = 0; i < 20; i++) { console.log(it.next()) } // "value": 0, "done": false } { "value": 1, "done": false } { "value": 1, "done": false } { "value": 2, "done": false } { "value": 3, "done": false } { "value": 5, "done": false }... { "value": 2584, "done": false } { "value": 4181, "done": false }
2、任务队列迭代器
我们可以定义一个任务队列,该队列初始化时为空,我们将待处理的任务传递后,传入数据进行处理。这样第一次传递的数据只会被任务1处理,第二次传递的只会被任务2处理… 代码如下:
var Task = { actions: [], [Symbol.iterator]() { var steps = this.actions.slice(); return { [Symbol.iterator]() { return this; }, next(...args) { if (steps.length > 0) { let res = steps.shift()(...args); return { value: res, done: false } } else { return { done: true } } } } } } Task.actions.push(function task1(...args) { console.log("任务一:相乘") return args.reduce(function(x, y) { return x * y }) }, function task2(...args) { console.log("任务二:相加") return args.reduce(function(x, y) { return x + y }) * 2 }, function task3(...args) { console.log("任务三:相减") return args.reduce(function(x, y) { return x - y }) }); var it = Task[Symbol.iterator](); console.log(it.next(10, 100, 2)); console.log(it.next(20, 50, 100)) console.log(it.next(10, 2, 1)) // 任务一:相乘 { "value": 2000, "done": false }任务二:相加 { "value": 340, "done": false }任务三:相减 { "value": 7, "done": false }
3、延迟执行
假设我们有一个数据表,我们想按大小顺序依次的获取数据,但是我们又不想提前给他排序,有可能我们根本就不去使用它,所以我们可以在第一次使用的时候再排序,做到延迟执行代码:
var table = { "d": 1, "b": 4, "c": 12, "a": 12 } table[Symbol.iterator] = function() { var _this = this; var keys = null; var index = 0; return { next: function() { if (keys === null) { keys = Object.keys(_this).sort(); } return { value: keys[index], done: index++>keys.length }; } } } for (var a of table) { console.log(a) } // a b c d
四、结语
本章内容,重点是明白 Iterator 接口的机制,以及 for...of 循环的使用方法。
相关推荐:
关于PHP聚合式迭代器接口IteratorAggregate用法分享
The above is the detailed content of How to use ES6 iterator and for.of loop. For more information, please follow other related articles on the PHP Chinese website!