Memeriksa apakah ada sesuatu yang dapat diulang

104

Dalam dokumen MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

The for...ofmembangun digambarkan untuk dapat iterate atas "iterable" objek. Tetapi apakah ada cara yang baik untuk memutuskan apakah suatu objek dapat diulang?

Saya telah mencoba menemukan properti umum untuk array, iterator, dan generator, tetapi tidak dapat melakukannya.

Selain melakukan for ... ofblok percobaan dan memeriksa kesalahan jenis, apakah ada cara yang bersih untuk melakukan ini?

simonzack
sumber
Tentunya, sebagai penulis, Anda tahu jika objek Anda dapat diulang?
andrewb
5
Objek tersebut dilewatkan sebagai argumen, saya tidak yakin.
simonzack
1
Mengapa tidak menguji tipe argumen tersebut?
James Bruckner
2
@ andrew-buchan, James Bruckner: Memeriksa jenis mungkin berhasil, tetapi jika Anda membaca dokumen MDN, Anda akan melihat bahwa ia mengatakan "seperti array". Saya tidak tahu persis apa artinya ini, oleh karena itu pertanyaannya.
simonzack
1
wiki.ecmascript.org/doku.php?id=harmony:iterators menyatakan " Sebuah objek dapat diulang jika memiliki iterator()metode. ". Namun, karena ini adalah draf, hanya pemeriksaan yang mungkin bergantung pada penerapan. Lingkungan apa yang Anda gunakan?
Bergi

Jawaban:

142

Cara yang tepat untuk memeriksa iterabilitas adalah sebagai berikut:

function isIterable(obj) {
  // checks for null and undefined
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}

Mengapa ini berfungsi (protokol yang dapat diulang secara mendalam): https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

Karena kita berbicara tentang untuk..dari, saya berasumsi, kita berada dalam pola pikir ES6.

Juga, jangan kaget bahwa fungsi ini mengembalikan truejika objadalah sebuah string, sebagai string yang mengulangi karakternya.

Tomas Kulich
sumber
14
atau Symbol.iterator in Object(obj),.
14
Ada (setidaknya) satu pengecualian untuk menggunakan operator 'in': string. String bersifat iterable (dalam istilah for..of) tetapi Anda tidak dapat menggunakan 'in' di atasnya. Jika bukan karena ini, saya lebih suka menggunakan 'dalam' yang pasti terlihat lebih bagus.
Tomas Kulich
bukankah seharusnya begitu return typeof obj[Symbol.iterator] === 'function'? "Agar dapat diulang, objek harus mengimplementasikan metode @@ iterator" - ini menentukan metode
callum
Tidak ada semantik yang tepat untuk obj [Symbol.iterator] menjadi apa pun selain fungsi (undefined or). Jika ada yang meletakkan misalnya String di sana, itu hal yang buruk dan IMO bagus jika kode tersebut gagal secepat mungkin.
Tomas Kulich
Apakah akan typeof Object(obj)[Symbol.iterator] === 'function'berhasil dalam semua kasus?
Craig Gidney
26

Mengapa begitu bertele-tele?

const isIterable = object =>
  object != null && typeof object[Symbol.iterator] === 'function'
adius
sumber
57
Readability > Clevernessselalu kembali true.
jfmercer
27
Haha, biasanya saya yang mengeluh tentang kode yang tidak bisa dibaca, tapi sebenarnya menurut saya ini cukup mudah dibaca. Seperti kalimat bahasa Inggris: Jika objeknya bukan null dan properti iterator simbolik adalah sebuah fungsi, maka itu dapat diulang. Jika itu tidak sesederhana itu, saya tidak tahu apa yang ...
adius
4
imo, inti dari "keterbacaan" adalah untuk memahami apa yang terjadi tanpa pembacaan yang sebenarnya
Dmitry Parzhitsky
2
@Alexander Mills Peningkatan Anda membuat kode menjadi lebih buruk. 1. Seperti yang dikatakan @jfmercer Readability > Cleverness, jadi mempersingkat variabel objectuntuk otidak membantu siapa pun. 2. String kosong ''dapat diulang dan karena itu harus dikembalikan true.
adius
1
Mengerti, saya pikir ada alasan mengapa itu digunakan!= null
Alexander Mills
22

Solusi paling sederhana sebenarnya adalah ini:

function isIterable (value) {
  return Symbol.iterator in Object(value);
}

Objectakan membungkus apa pun yang bukan objek menjadi satu, memungkinkan inoperator untuk bekerja bahkan jika nilai aslinya bukan Objek. nulldan undefineddiubah menjadi objek kosong sehingga tidak perlu deteksi kasus tepi, dan string dibungkus menjadi objek String yang dapat diulang.

Kartu domino
sumber
1
Anda menggunakan simbol yang salah. Ini: Symbol.iterator.
Gil
@Gil Anda benar sekali, ups! Saya harus memiliki kode yang diuji salin-tempel alih-alih mengetik langsung di posting.
Domino
Pembuatan objek baru dengan Objectsia-sia untuk jenis pemeriksaan ini.
Ruben Verborgh
Saya tidak bisa mengatakan saya tahu persis bagaimana Objectfungsi diimplementasikan, tetapi itu hanya membuat objek baru jika valuebelum menjadi objek. Saya berharap kombinasi dari indan Object(...)menjadi sesuatu yang dapat dioptimalkan dengan mudah oleh mesin browser, tidak seperti yang dikatakan value !== undefined && value !== null && value[Symbol.iterator] && true. Plus, ini sangat mudah dibaca, yang saya pedulikan.
Domino
9

Sebagai catatan samping, HATI - HATI tentang definisi iterable . Jika Anda datang dari bahasa lain Anda akan berharap bahwa sesuatu yang Anda dapat beralih di atas dengan, mengatakan, sebuah forloop iterable . Saya khawatir itu tidak terjadi di sini di mana iterable berarti sesuatu yang mengimplementasikan protokol iterasi .

Untuk memperjelas semua contoh di atas mengembalikan falseobjek ini {a: 1, b: 2}karena objek itu tidak mengimplementasikan protokol iterasi. Jadi, Anda tidak akan dapat mengulanginya dengan for...of TETAPI Anda masih bisa melakukannya dengan file for...in.

Jadi, jika Anda ingin menghindari kesalahan yang menyakitkan buat kode Anda lebih spesifik dengan mengganti nama metode Anda seperti yang ditunjukkan di bawah ini:

/**
 * @param variable
 * @returns {boolean}
 */
const hasIterationProtocol = variable =>
    variable !== null && Symbol.iterator in Object(variable);
Francesco Casula
sumber
Anda tidak masuk akal. Menurut Anda mengapa itu akan putus undefined?
adius
@adius Saya pikir saya salah berasumsi bahwa Anda melakukan object !== nulldalam jawaban Anda tetapi Anda melakukannya object != nullsehingga tidak melanggar undefineddalam kasus khusus itu. Saya telah memperbarui jawaban saya.
Francesco Casula
Ok aku paham. Btw: Kode Anda salah. hasIterationProtocol('')harus kembali true! Bagaimana kalau Anda menghapus kode Anda dan biarkan saja bagian penjelasan yang berulang, yang merupakan satu-satunya hal yang menambah nilai nyata / sesuatu yang baru dalam jawaban Anda.
adius
1
Itu kembali true, dengan menghapus sebagian dari jawaban lama saya menggabungkan kedua fungsi dan lupa tentang perbandingan yang ketat. Sekarang saya mengembalikan jawaban asli yang berfungsi dengan baik.
Francesco Casula
1
Saya tidak pernah berpikir untuk menggunakan Object(...), tangkapan yang bagus. Tetapi dalam kasus ini pemeriksaan null tidak diperlukan.
Domino
2

Saat ini, seperti yang telah disebutkan, untuk menguji apakah objiterable lakukan saja

obj != null && typeof obj[Symbol.iterator] === 'function' 

Jawaban historis (tidak lagi valid)

The for..ofkonstruk merupakan bagian dari ECMAScript edisi 6 Bahasa Keterangan Draft. Jadi bisa berubah sebelum versi final.

Dalam draf ini, objek yang dapat diulang harus memiliki fungsi iteratorsebagai properti.

Anda dapat memeriksa apakah suatu objek dapat diulang seperti ini:

function isIterable(obj){
   if(obj === undefined || obj === null){
      return false;
   }
   return obj.iterator !== undefined;
}
Ortomala Lokni
sumber
7
Properti iterator adalah hal firefox sementara. Ini tidak sesuai dengan ES6. lihat: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Amir Arad
2

Untuk iterator asinkron, Anda harus memeriksa 'Symbol.asyncIterator', bukan 'Symbol.iterator':

async function* doSomething(i) {
    yield 1;
    yield 2;
}

let obj = doSomething();

console.log(typeof obj[Symbol.iterator] === 'function');      // false
console.log(typeof obj[Symbol.asyncIterator] === 'function'); // true
Kamarey
sumber
1

Jika Anda ingin memeriksa apakah suatu variabel adalah object ( {key: value}) atau array ( [value, value]), Anda dapat melakukan itu:

const isArray = function (a) {
    return Array.isArray(a);
};

const isObject = function (o) {
    return o === Object(o) && !isArray(o) && typeof o !== 'function';
};

function isIterable(variable) {
    return isArray(variable) || isObject(variable);
}
Seglinglin
sumber