ES6 - Iteratore
Introduzione a Iterator
Iterator è un oggetto che ci permette di accedere a una raccolta di oggetti uno alla volta.
I seguenti tipi incorporati sono iterabili per impostazione predefinita:
- String
- Array
- Map
- Set
Viene considerato un oggetto iterable, se l'oggetto implementa una funzione la cui chiave è [Symbol.iterator]e restituisce un iteratore. Un ciclo for ... of può essere utilizzato per iterare una raccolta.
Esempio
L'esempio seguente dichiara una matrice, contrassegna e scorre attraverso di essa utilizzando un file for..of ciclo continuo.
<script>
let marks = [10,20,30]
//check iterable using for..of
for(let m of marks){
console.log(m);
}
</script>
L'output del codice sopra sarà come indicato di seguito:
10
20
30
Esempio
L'esempio seguente dichiara un array, contrassegna e recupera un oggetto iteratore. Il[Symbol.iterator]()può essere utilizzato per recuperare un oggetto iteratore. Il metodo next () dell'iteratore restituisce un oggetto con'value' e 'done'proprietà . "done" è booleano e restituisce true dopo aver letto tutti gli elementi nella raccolta.
<script>
let marks = [10,20,30]
let iter = marks[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
</script>
L'output del codice sopra sarà come mostrato di seguito -
{value: 10, done: false}
{value: 20, done: false}
{value: 30, done: false}
{value: undefined, done: true}
Iterabile personalizzato
Alcuni tipi in JavaScript sono iterabili (ad es. Array, Map ecc.) Mentre altri non lo sono (ad es. Class). I tipi JavaScript che non sono iterabili per impostazione predefinita possono essere iterati utilizzando il protocollo iterabile.
L'esempio seguente definisce una classe denominata CustomerListche memorizza più oggetti cliente come un array. Ogni oggetto cliente ha proprietà firstName e lastName.
Per rendere iterabile questa classe, la classe deve implementare [Symbol.iterator]()funzione. Questa funzione restituisce un oggetto iteratore. L'oggetto iteratore ha una funzionenext che restituisce un oggetto {value:'customer',done:true/false}.
<script>
//user defined iterable
class CustomerList {
constructor(customers){
//adding customer objects to an array
this.customers = [].concat(customers)
}
//implement iterator function
[Symbol.iterator](){
let count=0;
let customers = this.customers
return {
next:function(){
//retrieving a customer object from the array
let customerVal = customers[count];
count+=1;
if(count<=customers.length){
return {
value:customerVal,
done:false
}
}
//return true if all customer objects are iterated
return {done:true}
}
}
}
}
//create customer objects
let c1={
firstName:'Sachin',
lastName:'Tendulkar'
}
let c2={
firstName:'Rahul',
lastName:'Dravid'
}
//define a customer array and initialize it let customers=[c1,c2]
//pass customers to the class' constructor
let customersObj = new CustomerList(customers);
//iterating using for..of
for(let c of customersObj){
console.log(c)
}
//iterating using the next() method
let iter = customersObj[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
</script>
L'output del codice precedente sarà il seguente:
{firstName: "Sachin", lastName: "Tendulkar"}
{firstName: "Rahul", lastName: "Dravid"}
{
done: false
value: {
firstName: "Sachin",
lastName: "Tendulkar"
}
}
{
done: false
value: {
firstName: "Rahul",
lastName: "Dravid"
}
}
{done: true}
Generatore
Prima di ES6, le funzioni in JavaScript seguivano un modello run-to-complete. ES6 introduce funzioni note come Generatore che possono interrompersi a metà e poi continuare da dove si erano fermate.
Un generatore antepone al nome della funzione un carattere asterisco * e ne contiene uno o più yielddichiarazioni. Ilyield la parola chiave restituisce un oggetto iteratore.
Sintassi
function * generator_name() {
yield value1
...
yield valueN
}
Esempio
L'esempio definisce una funzione del generatore getMarkscon tre dichiarazioni di rendimento. A differenza delle normali funzioni, ilgenerator function getMarks(), quando invocato, non esegue la funzione ma restituisce un oggetto iteratore che aiuta a eseguire il codice all'interno della funzione generatore.
Alla prima chiamata a markIter.next()le operazioni all'inizio verrebbero eseguite e l'istruzione yield interrompe l'esecuzione del generatore. Successive chiamate almarkIter.next() riprenderà la funzione generatore fino al successivo yield espressione.
<script>
//define generator function
function * getMarks(){
console.log("Step 1")
yield 10
console.log("Step 2")
yield 20
console.log("Step 3")
yield 30
console.log("End of function")
}
//return an iterator object
let markIter = getMarks()
//invoke statements until first yield
console.log(markIter.next())
//resume execution after the last yield until second yield expression
console.log(markIter.next())
//resume execution after last yield until third yield expression
console.log(markIter.next())
console.log(markIter.next()) // iteration is completed;no value is returned
</script>
L'output del codice sopra sarà come indicato di seguito -
Step 1
{value: 10, done: false}
Step 2
{value: 20, done: false}
Step 3
{value: 30, done: false}
End of function
{value: undefined, done: true}
Esempio
L'esempio seguente crea una sequenza infinita di numeri pari
* evenNumberGenerator funzione del generatore.
Possiamo scorrere tutti i numeri pari utilizzando next() o utilizzando for of loop come mostrato di seguito
<script>
function * evenNumberGenerator(){
let num = 0;
while(true){
num+=2
yield num
}
}
// display first two elements
let iter = evenNumberGenerator();
console.log(iter.next())
console.log(iter.next())
//using for of to iterate till 12
for(let n of evenNumberGenerator()){
if(n==12)break;
console.log(n);
}
</script>
L'output del codice precedente sarà il seguente:
{value: 2, done: false}
{value: 4, done: false}
2
4
6
8
10