31 Januari 2021

Iterables / Bisa di iterasi

Objek yang bisa di iterasi adalah sebuah generalisasi dari sebuah array. Konsep itu membolehkan kita untuk membuat objek apapun yang bisa digunakan didalam perulangan for..of.

Tentu saja, Array bisa di iterasi. Tapi disana terdapat objek bawaan (built-in objek) lainnya, yang tentu saja bisa di iterasi. Contoh string juga bisa di iterasi.

Jika sebuah objek secara teknis bukan sebuah array, tapi representasi dari sebuah koleksi (list, set) dari sesuatu, lalu for..of adalah sintaks yang bagus untuk melakukan perulangan didalamnya, Jadi ayo kita lihat bagaimana cara membuat itu bekerja.

Symbol.iterator

Kita bisa dengan mudah mendapatkan konsep dari iterasi dengan membuatnya sendiri.

Untuk contoh, kita mempunyai sebuah objek yang bukanlah array, tapi cocok untuk for..of.

Seperti objek range yang merepresentasikan sebuah interval dari angka:

let range = {
  from: 1,
  to: 5
};

// Kita ingin membuat for..of untuk bisa digunakan:
// for(let num of range) ... num=1,2,3,4,5

Untuk bisa membuat range bisa diiterasi (dan membuat for..of bekerja) kita harus menambahkan sebuah metode kedalam objeknya bernama Symbol.iterator (Simbol built-in spesian yang hanya digunakan untuk hal itu).

  1. Ketika for.of dimulai, itu akan memanggil metodenya sekali (atau error jika tidak ditemukan). Metodenya haruslah mengembalikan sebuah iterator – sebuah objek dengan metode next.
  2. Selanjutnya, for..of bekerja hanya bila itu mengembalikan objek.
  3. Ketika for..of menginginkan nilai selanjutnya, itu akan memanggil next() didalam objeknya.
  4. Hasil dari next() harus mempunyai form {done: Boolean, value: any}, dimana done=true berarti iterasinya telah selesai, sebaliknya value adalah nilai selanjutnya.

Ini adalah implementasi penuh untuk range dengan catatan:

let range = {
  from: 1,
  to: 5
};

// 1. panggil for..of pertama kali untuk memanggil ini
range[Symbol.iterator] = function() {

  // ini akan mengembalikan objek iterator:
  // 2. Selanjutnya, for..of hanya bekerja dengan iterator ini, menanyakan nilai selanjutnya
  return {
    current: this.from,
    last: this.to,

    // 3. next() dipanggil untuk setiap iterasi oleh perulangan for..of
    next() {
      // 4. itu harus mengembalikan nilai sebagai sebuah objek {done:.., nilai:...}
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

// sekarang ini bekerja!
for (let num of range) {
  alert(num); // 1, lalu 2, 3, 4, 5
}

Perhatikan fitur utama dari iterables: pemisahan perhatian.

  • range sendiri tidak memiliki metode next().
  • Malah, objek lainnya, yang dipanggil “iterator” dibuat dengan memanggil ke range[Symbol.iterator](), dan next() miliknya menghasilkan nilai untuk diiterasi.

Jadi, objek iterator berbeda dari objek yang diiterasi.

Secara teknis, kita mungkin menyatukannya dan menggunakan range nya sendiri sebagai iterator untuk membuat kode lebih simpel.

Seperti ini: