Bài 18: 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:

// 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à valuedone.

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 ProtocolIterable.

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 qua Symbol.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.

Hãy để lại link bài viết gốc khi chia sẻ bài viết này, mình sẽ report DMCA với những website lấy nội dung mà không để nguồn hoặc copy bài với số lượng lớn.

Nguồn: freetuts.net

Profile photo of adminTheHalfHeart

TheHalfHeart

Có sở thích viết tuts nên đã từng tham gia viết ở một số diễn đàn, đến năm 2014 mới có điều kiện sáng lập ra freetuts.net. Sinh năm 90 và có 1 vợ 2 con, thích ca hát và lập trình.

ĐĂNG BÌNH LUẬN: Đăng câu hỏi trên Group Facebook để được hỗ trợ nhanh nhất.