Tình hình mình mần về ES6 đang phân vân chưa hiểu sự khác nhau và cách dùng của 2 chú này. Ai chỉ mình với
Iterator & Iterable trong Javascript?
Iterator là danh từ
Iterable là tính từ
Iterator là 1 đối tượng cụ thể
Iterable là khả năng của đối tượng
In JavaScript an iterator is an object which defines a sequence and potentially a return value upon its termination
An object is iterable if it defines its iteration behavior
Iterable là tính từ chỉ khả năng nên ta không dùng
Cách dùng Iterator : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
JavaScript hay sử dụng Duck typing để nhận biết object thuộc kiểu dữ liệu nào.
Iterable
là object có 1 property đặc biệt:
- key có kiểu
Symbol
và có tên@@iterator
, tên của key không thể truy xuất thông qua literal như string hay number, mà qua propertyiterator
củaSymbol
object, haySymbol.iterator
- value có kiểu
function
, function không có tham số truyền vào
Đoạn code sau tạo biến iter
dạng Iterable
. Mình sử dụng cả 2 cách ES6 và ES5. ES6 sẽ nhanh hơn so với ES5 một chút.
var iterator = { ... };
// ES6
var iter = {
[Symbol.iterator]: function () {
return iterator;
}
};
// ES5
var iter = {};
iter[Symbol.iterator] = function () {
return iterator;
};
Thông thường Iterable
sau khi gọi function object của property @@iterator
thì sẽ trả về Iterator
object. Tuy nhiên, JavaScript nhận diện kiểu duck typing, nên có thể trả về object bất kì. JavaScript chỉ biết Iterable
là object có property là @@iterator
và value của @@iterator
có kiểu function.
Vì vậy cách nhận biết 1 object là Iterable
được hiện thực gần giống như sau:
function isIterable(obj) {
if (typeof obj !== 'object') {
return false;
}
if (!(Symbol.iterator in obj)) {
return false;
}
return typeof obj[Symbol.iterator] === 'function';
}
Iterator
là object có 3 property có value thuộc kiểu function là: next
, return
, throw
, nó sẽ có dạng tương tự như sau:
var iterator = {
next: function (val) { /*...*/ },
return: function (val) { /*...*/ },
throw: function (val) { /*...*/ }
};
Function nhận biết object là Iterator
như sau:
function isIterator(obj) {
if (typeof(obj) !== 'object') {
return false;
}
return !!(
('next' in obj && typeof obj.next === 'function') &&
('return' in obj && typeof obj.return === 'function') &&
('throw' in obj && typeof obj.throw === 'function')
);
}
next
của Iterator
trả về object có 2 property là value
và done
:
-
value
có giá trị bất kì, nhưng thường dùng để liệt kê phần tử trong array-like object. Array-like object là object có các property dạng “0”, “1”, “2”,… Các property đều có property descriptorenumerable
làtrue
. -
done
có giá trịtrue
hoặcfalse
, kiểu boolean.done
thường được dùng thông báo bạn đã duyệt hết phần tử trong array-like object chưa. Nếu chưa trả vềfalse
, nếu đã duyệt hết thì trả vềtrue
.
return
gọi tới next
. Sau đó return
sẽ thay đổi property done
từ kết quả trả về của next
sang true và trả về object đã thay đổi.
throw
gọi tới next
. throw
không có trả về bất kì giá trị gì, nên throw
trả về giá trị undefined
. Ngoài ra, throw
gọi lệnh throw
với giá trị value
của object trả về từ next
.
Vì vậy object có dạng Iterator
được hiện thực như sau:
var iterator = {
next: function (val) {
// stuff
var nextValue = ...
// stuff
var nextDone = ...
// stuff
return { value: nextValue, done: nextDone };
},
return: function (val) {
var nextObj = this.next(val);
nextObj.done = true;
return nextObj;
},
throw: function (val) {
var nextObj = this.next(val);
throw nextObj.value;
}
};
Ứng dụng của Iterable
và Iterator
được dùng để hiện thực Iterator pattern.
Ví dụ mình tạo 1 array-like object có dạng Iterable
để duyệt các phần tử từ 1 đến 6. Đoạn code bên dưới:
var numbersGen = {
[Symbol.iterator]: function () {
return {
values: [1, 2, 3, 4, 5, 6],
index: 0,
next: function () {
if (this.index < this.values.length) {
return {
value: this.values[this.index++],
done: false
};
} else {
return { done: true };
}
}
};
}
};
Đầu tiên, mình gọi @@iterator()
đến Iterable
để trả về Iterator
var numbers = numbersGen[Symbol.iterator]();
Sau đó, mình gọi tiếp các next()
trong Iterator
và in nó ra
var nextVal;
do {
nextVal = numbers.next();
console.log(nextVal.value);
} while (!nextVal.done);
Output
Bước gọi trên có thể viết gọn lại trong bằng lệnh for of
. Cho kết quả tương tự
for (let value of numbersGen) {
console.log(value);
}
for let
gọi @@iterator
của Iterable
đứng đằng sau of
. Sau đó gọi next()
cho mỗi lần lặp. Nếu object trả về từ next()
có property done
trả về false
thì thực hiện các câu lệnh trong block.
Điều kiện kết thúc của for of
:
- kiểm tra property
done
bằngtrue
từ object trả vềnext()
hoặcreturn()
- catch exception từ
throw()