Iterables và iterators trong ES6
Trong bài này chúng ta tìm hiểu về iterables và iterators trong Javascript nói chung và trong ES6 nói riêng.
Từ trước đến nay để lặp qua các phần tử của một mảng hoặc một danh sách thì chúng ta sử dụng vòng lặp, tuy nhiên việc sử dụng vòng lặp đôi lúc gây khó khăn trong một số trường hợp. Ví dụ bạn muốn lặp qua từng phần tử và có thể dừng ở một phần tử bất kì, điều này hoàn toàn làm được bằng vòng lặp nhưng không được hay và tốn chi phí tính toán. Vậy trong ES6 cung cấp cho chúng ta khả năng duyệt nâng cao hơn nữa bằng cách sử dụng iterators.
Một ví dụ điển hình trong PHP về iterators như sau:
Bài viết này được đăng tại [free tuts .net]
// câu truy vấn $sql = 'Select * from Users'; // Thực hiện câu truy vấn $result = mysqli_query($link, $sql); // Lấy kết quả đầu tiên $first = mysqli_fetch_assoc($result); // Lấy phần tử kế tiếp $second = mysqli_fetch_assoc($result);
Đây là một ví dụ được viết bằng PHP, hàm mysqli_fetch_assoc()
sẽ lần lượt lấy từng phần tử trong danh sách kết quả trả về.
1. Iterators là gì?
Iterators là một bộ duyệt dùng để duyệt qua một mảng, một danh sách hoặc một collection mà qua mỗi lần duyệt sẽ ghi lại vị trí đã duyệt để từ đó có thể biết và lấy vị trí tiếp theo.
Trong Javascript thì iterators có chung cấp phương thức next()
và phương thức này sẽ return về phần tử kết tiếp, đồng thời ghi nhận luôn phần tử đã lặp là phần tử next()
. Phương thức next()
sẽ return về một Object gồm hai thuộc tính là value
và done
.
Ví dụ: Sử dụng Iterators với mảng
let arr = ['a', 'b', 'c']; var iterator = arr[Symbol.iterator](); console.log(iterator.next()); // a console.log(iterator.next()); // b console.log(iterator.next()); // c console.log(iterator.next()); // undefined
Kết quả:
Giá trị lần next()
cuối cùng là một giá trị undefined
và key done
của nó sẽ là true.
Có lẽ đoạn code trên bạn sẽ thắc mắc tại sao lại có Symbol.iterator
phải không nào :) Nếu vậy thì ta sẽ tìm hiểu qua một chút về Iterator Protocol và Iterable.
2. Iterable và iterator protocol
Trước tiên chúng ta tìm hiểu vè Iterable đã nhé.
Iterable
Iterable là khả năng cho phép các đối tượng trong Javascript sử dụng các kỹ thuật xử lý dữ liệu như for of loop
, toán tử ba chấm ...
.
Ví dụ:
var array = [1, 2, 3, 4]; for (let x of array) { console.log(x); } console.log(...array);
Với ES6 thì các đối tượng như Array
, Object
, Map
, WeakMap
, Set
, WeakSet
đều là đối tượng Iterable.
Iterator protocol
Iterator Protocol chẳng qua chỉ là các giao thức (method) xử lý một đối tượng có đánh dấu vị trí đã duyệt, vì vậy với các đối tượng thông thường sẽ không sử dụng được nên ta phải sử dụng Symbol.iterator
để chuyển đôi.
Quay lại ví dụ trên mình sẽ giải thích như sau:
// Mảng nguyên thủy let arr = ['a', 'b', 'c']; // Chuyển sang Iterable var iterable = arr[Symbol.iterator](); // Sử dụng các Iterator Protocol để chuyển qua các phần tử console.log(iterable.next()); // a console.log(iterable.next()); // b console.log(iterable.next()); // c console.log(iterable.next()); // undefined
Bây giờ chúng ta sẽ mổ xẻ tấm hình dưới đây.
Trong tấm hình mô phỏng ba tầng cấp như sau:
- Data consumers: Các phương thức - hành động của Iterator, đây cũng chính là Iterator Protocol.
- Interface: Interface là một lớp trung gian kế thừa tất cả các Data consummers, đây chính là Iterable. Và vì trong Javascript không tồn tại Interface nên ta sử dụng
Symbol.iterator
để chuyển đổi. - Data Resources: Các đối tượng dữ liệu trong Javascript muốn chuyenr sang Iterable.
Tóm lại:
- Các đối tượng muốn sử dụng được Iterator Protocol thì phải thông qua Interface. Thông thường thì các đối tượng như array, collection đề đã có sẵn iterable.
- Với phương thức
next()
thì bạn phải thực hiện thao tác chuyển đổi thông quaSymbol.iterator
.
3. Ví dụ Iterable và Iterator
Sau đây là một số đối tượng có sẵn Iterable và cách sử dụng Iterator trên đối tượng đó.
Array
var array = ['a', 'b', 'c']; console.log('***** Lặp qua các phần tử *****'); for (let x array) { console.log(x); } console.log('***** Sử dụng next *****'); var iterable = array[Symbol.iterator](); console.log(iterable.next()); console.log(iterable.next()); console.log(iterable.next()); console.log(iterable.next());
Kết quả:
Map
let map = new Map().set('a', 1).set('b', 2); console.log('***** Lặp qua các phần tử *****'); for (let pair of map) { console.log(pair); } console.log('***** Sử dụng next *****'); let iterable = map[Symbol.iterator](); console.log(iterable.next()); console.log(iterable.next()); console.log(iterable.next());
Kết quả:
Set
let set = new Set().add('a').add('b'); console.log('***** Lặp qua các phần tử *****'); for (let x of set) { console.log(x); } console.log('***** Sử dụng next *****'); var iterable = set[Symbol.iterator](); console.log(iterable.next()); console.log(iterable.next()); console.log(iterable.next());
Kết quả:
Arguments
Arguments là tổng các tham số truyền vào mảng.
function printArgs() { console.log('***** Lặp qua các phần tử *****'); for (let x of arguments) { console.log(x); } console.log('***** Sử dụng next *****'); var iterable = arguments[Symbol.iterator](); console.log(iterable.next()); console.log(iterable.next()); console.log(iterable.next()); } // Sử dụng printArgs('a', 'b');
Kết quả:
Còn khá nhiều trong thực tế nhưng mình không thể biểu diễn hết được. Nếu bạn muốn tự tìm hiểu thì hãy lên trang này nhé.
4. Toán tử ...
Toán tử ba chấm trong Iterator dùng để liệt kê các phần tử của các đối tượng.
Sử dụng với Array
var array = ['a', 'b', 'c']; console.log('Liệt kê'); console.log(...array); // a b c console.log('Bổ sung'); var new_arr = [...array, 'd', 'e']; console.log(...new_arr); // a b c d e
Kết quả:
Sử dụng với Map
console.log('Liệt kê'); let map = new Map().set('a', 1).set('b', 2); console.log(...map); console.log('Bổ sung'); let new_map = new Map([...map]).set('d', 3).set('e', 4); console.log(...new_map);
Sử dụng với Set
console.log('Liệt kê'); let set = new Set().add('a').add('b'); console.log(...set); console.log('Bổ sung'); let new_set = new Set([...set]).add('c').add('d'); console.log(...new_set);
Kết quả:
Và còn rất rất nhiều cách nữa nhưng mình đuối quá rồi, bạn tự tìm hiểu thêm nhé :)
5. Lời kết
Như vậy Iterator là những toán tử có khả năng liệt kê các phần tử trong các đối tượng tập hợp như Array
, Object
, Map
, Set
, WeakMap
, WeakSet
. Việc ứng dụng nó không đơn giản chỉ ở trong Javascript thuần mà còn ở trong các Javascript Framework như NodeJS, AngularJS. Bài này tương đối dài dòng và khó hiểu nên bạn phải tập trung công lực mới thấm được :)
Trong quá trình viết bài mình có tham khảo một số nguồn bằng tiếng Anh nên nếu có chỗ nào sai sót rất mong các bạn góp ý để mình chỉnh sửa lại bài viết. Chân thành cám ơn.