Kiểm tra xem thứ gì đó có thể lặp lại được không


104

Trong tài liệu MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

Các for...of trúc được mô tả để có thể lặp lại các đối tượng "có thể lặp lại". Nhưng có một cách tốt để quyết định xem một đối tượng có thể lặp lại không?

Tôi đã cố gắng tìm các thuộc tính chung cho mảng, trình vòng lặp và trình tạo, nhưng không thể thực hiện được.

Ngoài việc thực hiện một for ... ofkhối thử và kiểm tra lỗi loại, có cách nào tốt để thực hiện việc này không?


Chắc chắn, với tư cách là tác giả, bạn biết đối tượng của mình có thể lặp lại không?
andrewb

5
Đối tượng được chuyển như một đối số, tôi không chắc chắn.
simonzack

1
Tại sao không kiểm tra kiểu chữ của đối số?
James Bruckner

2
@ andrew-buchan, James Bruckner: Kiểm tra các loại có thể hoạt động, nhưng nếu bạn đọc tài liệu MDN, bạn sẽ nhận thấy rằng nó có nội dung "giống mảng". Tôi không biết chính xác điều này có nghĩa là gì, do đó có câu hỏi.
simonzack 19/09/13

1
wiki.ecmascript.org/doku.php?id=harmony:iterators tuyên bố " Một đối tượng có thể lặp lại nếu nó có một iterator()phương thức. ". Tuy nhiên, vì đây là bản nháp nên chỉ có một tấm séc có thể phụ thuộc vào sự ép buộc. Bạn sử dụng môi trường nào?
Bergi

Câu trả lời:


142

Cách thích hợp để kiểm tra khả năng lặp lại như sau:

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

Tại sao điều này hoạt động (chuyên sâu về giao thức có thể lặp lại): https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

Vì chúng ta đang nói về for..of, tôi cho rằng, chúng ta đang có tư duy ES6.

Ngoài ra, đừng ngạc nhiên khi hàm này trả về trueif objlà một chuỗi, vì các chuỗi lặp qua các ký tự của chúng.


14
hoặc Symbol.iterator in Object(obj),.

14
Có (ít nhất) một ngoại lệ khi sử dụng toán tử 'in': string. Chuỗi có thể lặp lại (theo nghĩa của for..of) nhưng bạn không thể sử dụng 'in' trên nó. Nếu không phải vì điều này, tôi muốn sử dụng 'in' nó trông chắc chắn đẹp hơn.
Tomas Kulich

không nên nó được return typeof obj[Symbol.iterator] === 'function'? "Để có iterable, một đối tượng phải thực hiện các phương pháp @@ iterator" - đó quy định cụ thể phương pháp
Callum

Không có ngữ nghĩa thích hợp cho obj [Symbol.iterator] là bất kỳ thứ gì khác ngoài hàm (không xác định hoặc). Nếu ai đó đặt ví dụ Chuỗi ở đó, đó là một điều tồi tệ và IMO sẽ tốt nếu mã bị lỗi càng sớm càng tốt.
Tomas Kulich

Sẽ typeof Object(obj)[Symbol.iterator] === 'function'hoạt động trong mọi trường hợp?
Craig Gidney

26

Tại sao lại dài dòng như vậy?

const isIterable = object =>
  object != null && typeof object[Symbol.iterator] === 'function'

57
Readability > Clevernessluôn luôn trở lại true.
jfmercer

27
Haha, bình thường tôi là người phàn nàn về mã không thể đọc được, nhưng thực sự tôi nghĩ nó khá dễ đọc. Giống như một câu tiếng Anh: Nếu đối tượng không phải là null và thuộc tính vòng lặp biểu tượng là một hàm, thì nó có thể lặp được. Nếu điều đó không đơn giản là chết, tôi không biết là gì ...
adius

4
imo, quan điểm "dễ đọc" là để hiểu những gì đang xảy ra mà không thực sự đọc
Dmitry Parzhitsky

2
@Alexander Mills Cải tiến của bạn khiến mã trở nên tồi tệ hơn. 1. Giống như @jfmercer đã nói Readability > Cleverness, vì vậy rút ngắn biến objectđể okhông giúp ích gì cho ai cả. 2. Một chuỗi rỗng ''có thể lặp lại và vì vậy nó phải trả về true.
adius

1
Đã nhận nó, tôi đoán là có một lý do tại sao nó được sử dụng!= null
Alexander Mills

22

Giải pháp đơn giản nhất thực sự là:

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

Objectsẽ bọc bất kỳ thứ gì không phải là đối tượng trong một, cho phép intoán tử hoạt động ngay cả khi giá trị ban đầu không phải là Đối tượng. nullundefinedđược biến thành các đối tượng rỗng nên không cần phát hiện trường hợp cạnh và các chuỗi được bọc thành các đối tượng Chuỗi có thể lặp lại.


1
Bạn đã sử dụng sai biểu tượng. Đó là: Symbol.iterator.
Gil

@Gil Bạn hoàn toàn đúng, rất tiếc! Tôi nên sao chép mã thử nghiệm được dán thay vì nhập trực tiếp vào một bài đăng.
Domino

Việc tạo ra một đối tượng mới với Objectloại kiểm tra này là lãng phí.
Ruben Verborgh

Tôi không thể nói rằng tôi biết chính xác Objecthàm được triển khai như thế nào , nhưng nó chỉ tạo một đối tượng mới nếu valuechưa phải là một đối tượng. Tôi mong đợi sự kết hợp inObject(...)trở thành thứ mà các công cụ trình duyệt có thể dễ dàng tối ưu hóa, không giống như nói value !== undefined && value !== null && value[Symbol.iterator] && true. Thêm vào đó, nó cực kỳ dễ đọc, điều mà tôi quan tâm.
Domino

9

Như một chú thích bên, hãy CẨN THẬN về định nghĩa của có thể lặp lại . Nếu bạn đang đến từ các ngôn ngữ khác bạn mong chờ điều gì đó bạn có thể duyệt qua với, nói, một forvòng lặp là iterable . Tôi e rằng đó không phải là trường hợp ở đây khi có thể lặp lại có nghĩa là thứ gì đó thực hiện giao thức lặp lại .

Để làm cho mọi thứ rõ ràng hơn, tất cả các ví dụ ở trên trả falsevề đối tượng này {a: 1, b: 2}vì đối tượng đó không triển khai giao thức lặp. Vì vậy, bạn sẽ không thể lặp lại nó với một for...of NHƯNG bạn vẫn có thể vớifor...in .

Vì vậy, nếu bạn muốn tránh những sai lầm đáng tiếc, hãy làm cho mã của bạn cụ thể hơn bằng cách đổi tên phương thức của bạn như được hiển thị bên dưới:

/**
 * @param variable
 * @returns {boolean}
 */
const hasIterationProtocol = variable =>
    variable !== null && Symbol.iterator in Object(variable);

Bạn không có ý nghĩa. Bạn nghĩ tại sao nó sẽ phá vỡ undefined?
adius

@adius Tôi nghĩ rằng tôi đã nhầm tưởng rằng bạn đang làm object !== nulltrong câu trả lời của mình nhưng bạn đang làm object != nullvì vậy nó không vi phạm undefinedtrong trường hợp cụ thể đó. Tôi đã cập nhật câu trả lời của mình cho phù hợp.
Francesco Casula,

Ok, tôi hiểu. Btw: Mã của bạn không chính xác. hasIterationProtocol('')phải trở về true! Còn bạn thì sao, hãy xóa mã của mình và chỉ để lại phần giải thích có thể lặp lại, đó là thứ duy nhất bổ sung giá trị thực / điều gì đó mới trong câu trả lời của bạn.
adius

1
Nó trả về true, bằng cách loại bỏ một phần của câu trả lời cũ, tôi đã hợp nhất cả hai hàm và quên đi sự so sánh chặt chẽ. Bây giờ tôi đặt lại câu trả lời ban đầu đang hoạt động tốt.
Francesco Casula,

1
Tôi chưa bao giờ nghĩ đến việc sử dụng Object(...), bắt đẹp. Nhưng trong trường hợp đó, kiểm tra null là không cần thiết.
Domino

2

Ngày nay, như đã nói, để kiểm tra xem objcó thể lặp lại hay không, chỉ cần làm

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

Câu trả lời lịch sử (không còn giá trị)

Cấu for..oftrúc này là một phần của Dự thảo Đặc tả Ngôn ngữ phiên bản thứ 6 của ECMASCript. Vì vậy, nó có thể thay đổi trước phiên bản cuối cùng.

Trong bản nháp này, các đối tượng có thể lặp lại phải có chức năng iteratornhư một thuộc tính.

Bạn có thể kiểm tra xem một đối tượng có thể lặp lại như thế này không:

function isIterable(obj){
   if(obj === undefined || obj === null){
      return false;
   }
   return obj.iterator !== undefined;
}

7
Thuộc tính trình lặp là một thứ tạm thời cho firefox. Nó không tuân thủ ES6. xem: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Amir Arad

2

Đối với async lặp bạn nên kiểm tra cho 'Symbol.asyncIterator' thay vì '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

1

Nếu bạn muốn kiểm tra thực tế một biến là một đối tượng ( {key: value}) hay một mảng ( [value, value]), bạn có thể làm điều đó:

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.