Luồng điều khiển

Luồng điều khiển là thứ tự mà trình thông dịch JavaScript thực thi tuyên bố. Nếu một tập lệnh không bao gồm các câu lệnh làm thay đổi luồng của tập lệnh, thì thực thi từ đầu đến cuối, mỗi lần một dòng. Cấu trúc điều khiển là dùng để xác định xem một tập hợp câu lệnh có được thực thi hay không dựa trên tập hợp tiêu chí xác định, thực thi một tập hợp câu lệnh nhiều lần hoặc làm gián đoạn một tập hợp chuỗi câu lệnh.

Câu lệnh có điều kiện

Câu lệnh có điều kiện xác định xem mã có được thực thi dựa trên một hoặc điều kiện khác. Câu lệnh có điều kiện sẽ thực thi mã chứa trong câu lệnh nếu điều kiện liên kết (hoặc nhóm điều kiện) được đánh giá thành true. Nếu không, giá trị mã bị bỏ qua.

if... else

Câu lệnh if đánh giá một điều kiện bên trong dấu ngoặc đơn khớp theo dõi. Nếu điều kiện bên trong dấu ngoặc đơn có giá trị là true, thì câu lệnh hoặc câu lệnh khối theo sau các dấu ngoặc đơn phù hợp sẽ được thực thi:

if ( true ) console.log( "True." );
> "True."

if ( true ) {
    const myString = "True.";
    console.log( myString );
}
> "True."

Nếu điều kiện bên trong dấu ngoặc đơn có giá trị là false, thì câu lệnh đó sau đó thì nó sẽ bị bỏ qua:

if ( false ) console.log( "True." );

Từ khoá else ngay sau câu lệnh if và Câu lệnh thực thi có điều kiện chỉ định câu lệnh sẽ được thực thi nếu Điều kiện if đánh giá là false:

if ( false ) console.log( "True." )''
else console.log( "False" );
> "False."

Để liên kết nhiều câu lệnh if với nhau, bạn có thể tạo câu lệnh thực thi có điều kiện sau else một câu lệnh if khác:

const myCondition = 2;
if ( myCondition === 5 ) console.log( "Five." );
else if ( myCondition === 2 ) console.log( "Two." );

Bạn nên sử dụng cú pháp câu lệnh khối sau các điều kiện để cải thiện khả năng đọc, nhưng mệnh đề else if thường là ngoại lệ đối với:

if ( myCondition === 5 ) {
    console.log( "Five." );
} else if ( myCondition === 3 ) {
    console.log( "Three" );
} else {
    console.log( "Neither five nor three." );
}
> "Neither five nor three."

Toán tử ba ngôi

if thực thi câu lệnh theo điều kiện. Toán tử 3 ngôi (chính xác hơn nhưng ít phổ biến hơn được gọi là toán tử có điều kiện 3 ngôi) là cách viết tắt để thực thi một biểu thức theo cách có điều kiện. Như chính tên gọi của nó, tam giác là toán tử JavaScript duy nhất sử dụng ba toán hạng:

  • Một điều kiện cần đánh giá, theo sau là dấu chấm hỏi (?).
  • Biểu thức để thực thi nếu điều kiện có giá trị là true, theo sau là một dấu hai chấm (:).
  • Biểu thức để thực thi nếu điều kiện có giá trị là false.

Hàm này thường dùng để đặt hoặc truyền một giá trị theo cách có điều kiện:

const myFirstResult  = true  ? "First value." : "Second value.";
const mySecondResult = false ? "First value." : "Second value.";

myFirstResult;
> "First value."

mySecondResult;
> "Second value."

switch... case

Sử dụng câu lệnh switch để so sánh giá trị của một biểu thức với danh sách giá trị tiềm năng được xác định bằng một hoặc nhiều từ khoá case. Cú pháp này là khác thường vì nó xuất phát từ một số quyết định thiết kế ban đầu của JavaScript. Cú pháp switch...case sử dụng từ khoá switch, theo sau là một biểu thức để sẽ được đánh giá trong dấu ngoặc đơn, theo sau là một cặp dấu ngoặc nhọn. Phần nội dung của switch có thể chứa case từ khoá, thường là một hoặc nhiều từ khoá, theo sau là một biểu thức hoặc giá trị, theo sau là dấu hai chấm (:).

Khi trình phiên dịch đạt đến case có giá trị khớp với biểu thức đang được được đánh giá trong dấu ngoặc đơn sau từ khoá switch, nó sẽ thực thi bất kỳ các câu lệnh tuân theo mệnh đề case đó:

switch ( 2 + 2 === 4 ) {
  case false:
    console.log( "False." );
  case true:
    console.log( "True." );
}
> "True."

Mọi câu lệnh sau case trùng khớp đều được thực thi, ngay cả khi chúng được đặt trong câu lệnh khối.

switch ( 2 + 2 === 4 ) {
    case false:
    console.log( "False." );
  case true:
    let myVariable = "True.";
    console.log( myVariable );

}
> "True."

Một sai lầm khi sử dụng switch…case là sau khi tìm thấy kết quả trùng khớp, Trình phiên dịch JavaScript thực thi bất kỳ câu lệnh nào tuân theo case phù hợp, ngay cả những câu lệnh trong mệnh đề case khác. Điều này được gọi là "kết thúc" vào case tiếp theo:

switch ( 2 + 2 === 7 ) {
    case false:
    console.log( "False." );
  case true:
    console.log( "True." );
}
> "False."
> "True."

Để tránh bỏ sót, hãy kết thúc mỗi trường hợp bằng từ khoá break. ngay lập tức dừng đánh giá phần thân switch:

switch ( 2 + 2 === 7 ) {
    case false:
    console.log( "False." );
    break;
  case true:
    console.log( "True." );
    break;
}
> "False."

Nếu không có case nào khớp với giá trị có điều kiện, switch sẽ chọn default mệnh đề nếu có:

switch ( 20 ) {
    case 5:
    console.log( "The value was five." );
    break;
  case 10:
    console.log( "The value was ten." );
    break;
  default:
    console.log( "The value was something unexpected." );
}
> "The value was something unexpected."

Tuy nhiên, khả năng giảm giá cũng áp dụng cho default, có khả năng dẫn đến kết quả không mong muốn. Để khắc phục vấn đề này, hãy kết thúc câu lệnh default bằng break, hoặc đặt đơn vị quảng cáo đó cuối cùng trong danh sách trường hợp.

switch ( 20 ) {
  default:
    console.log( "The value was something unexpected." );
  case 10:
    console.log( "The value was ten." );
    break;
  case 5:
    console.log( "The value was five." );
    break;
}
> The value was something unexpected.
> The value was ten.

Vì mệnh đề case không yêu cầu câu lệnh khối để nhóm nhiều câu lệnh, mệnh đề casedefault sẽ không được tạo Phạm vi từ vựng:

let myVariable;
switch ( true ) {
  case true:
    let myVariable = "True.";
    break;
  default:
    let myVariable = "False.";
    break;
}
> Uncaught SyntaxError: redeclaration of let myVariable

Để quản lý phạm vi, hãy sử dụng câu lệnh khối:

let myVariable;
switch ( true ) {
  case true: {
    let myVariable = "True.";
    break;
  }
  default: {
    let myVariable = "False.";
    break;
  }
}

Vòng lặp và lặp lại

Vòng lặp cho phép bạn lặp lại một tập hợp câu lệnh miễn là đáp ứng một điều kiện, hoặc cho đến khi thoả mãn điều kiện. Sử dụng vòng lặp để thực thi một tập hợp các lệnh số lần, cho đến khi đạt được một kết quả cụ thể hoặc cho đến khi thông dịch viên đến cuối của cấu trúc dữ liệu có thể lặp lại (ví dụ: phần tử cuối cùng trong một mảng, ánh xạ hoặc tập hợp, thuộc tính cuối cùng của một đối tượng hoặc ký tự cuối cùng trong một chuỗi).

Vòng lặp làm gián đoạn "từ trên xuống dưới" luồng thực thi của một tập lệnh bằng cách lặp lại trên một tập hợp câu lệnh cho đến khi một hoặc nhiều điều kiện được đáp ứng hoặc không còn tuỳ thuộc vào cú pháp dùng để tạo vòng lặp. Sau khi vòng lặp kết thúc, và tiếp tục thực thi các câu lệnh theo sau nó. Trong ví dụ sau đây: các câu lệnh trong phần nội dung của vòng lặp được thực thi 3 lần trước khi di chuyển trình phiên dịch:

let iterationCount = 0;
console.log( "Pre-loop." );
while( iterationCount < 3 ) {
  iterationCount++;
  console.log( "Loop iteration." );
}
console.log( "Continuing on." );
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Continuing on."

Nếu không thể đáp ứng các điều kiện trong quá trình thực thi của vòng lặp, thì vòng lặp sẽ tiếp tục vô thời hạn. Những vòng lặp vô hạn này là một lỗi lập trình phổ biến có thể gây ra luồng thực thi chính để tạm dừng vô thời hạn hoặc thậm chí là gặp sự cố với một thẻ trình duyệt.

Ví dụ sau đây thực thi cho đến khi giá trị boolean true vẫn còn true. Vì giá trị boolean là không thể thay đổi, điều này tạo ra một vòng lặp vô hạn.

console.log( "Pre-loop." );
while( true ) {
console.log( "Loop iteration." );
}
> "Pre-loop."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
> "Loop iteration."
…

Tránh để lại vòng lặp vô hạn trong mã sản xuất. Nếu bạn vô tình tạo trong quá trình phát triển, bạn có thể khắc phục bằng cách đóng thẻ trình duyệt đang chạy cập nhật mã để vòng lặp không còn vô hạn và mở lại .

while

Vòng lặp while được tạo bằng từ khoá while, theo sau là một cặp dấu ngoặc đơn phù hợp có chứa điều kiện cần đánh giá. Nếu giá trị được chỉ định điều kiện ban đầu có giá trị là true, câu lệnh (hoặc câu lệnh khối) tuân theo các dấu ngoặc đơn đó sẽ được thực thi. Nếu không, vòng lặp sẽ không bao giờ chạy. Sau mỗi thì điều kiện sẽ được đánh giá lại và nếu vẫn là true, vòng lặp lặp lại.

let iterationCount = 0;
while( iterationCount < 3 ) {
  iterationCount++;
  console.log( `Loop ${ iterationCount }.` );
}
> "Loop 1."
> "Loop 2."

Nếu tìm thấy một câu lệnh continue trong vòng lặp while, trình phiên dịch sẽ dừng câu lệnh đó lặp lại, đánh giá lại điều kiện và tiếp tục vòng lặp nếu có thể:

let iterationCount = 0;
while( iterationCount <= 5 ) {
  iterationCount++;
  if( iterationCount === 3 ) {
    continue;
  }
  console.log( `Loop ${ iterationCount }.` );
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Loop 4."
> "Loop 5."
> "Loop ended."

Nếu trình phiên dịch tìm thấy câu lệnh break trong vòng lặp while, thì lần lặp đó dừng và điều kiện không được đánh giá lại, cho phép trình thông dịch tiếp tục:

let iterationCount = 1;
while( iterationCount <= 5 ) {
  if( iterationCount === 3 ) {
    console.log(`Iteration skipped.``);`
    break;
  }
  console.log( `Loop ${ iterationCount }.` );
  iterationCount++;
}
console.log( "Loop ended." );
> "Loop 1."
> "Loop 2."
> "Iteration skipped.
> "Loop ended."

Bạn có thể sử dụng while để lặp lại một số lần được chỉ định, như trong ví dụ trước, nhưng trường hợp sử dụng phổ biến nhất cho while là một vòng lặp của độ dài không xác định:

let randomize = () => Math.floor( Math.random() * 10 );
let randomNum = randomize();
while( randomNum !== 3 ){
  console.log( `The number is not ${ randomNum }.` );
  randomNum = randomize();
}
console.log( `The correct number, ${ randomNum }, was found.` );
> "The number is not 0."
> "The number is not 6."
> "The number is not 1."
> "The number is not 8."
> "The correct number, 3, was found."

do... while

do...while là một biến thể của vòng lặp while, trong đó câu lệnh có điều kiện quá trình đánh giá diễn ra ở cuối mỗi lần lặp lại của vòng lặp. Điều này có nghĩa là phần thân của vòng lặp luôn được thực thi ít nhất một lần.

Để tạo vòng lặp do...while, hãy dùng từ khoá do, theo sau là câu lệnh (hoặc câu lệnh khối) trở thành được thực thi trên mỗi lần lặp của vòng lặp. Ngay sau câu lệnh đó, hãy thêm while và dấu ngoặc đơn khớp có chứa điều kiện cần đánh giá. Thời gian điều kiện này không còn đánh giá là true, vòng lặp sẽ kết thúc.

let iterationCount = 1;
do {
  console.log( `Loop ${ iterationCount }.` );
  iterationCount++;
} while ( iterationCount < 3 );
> "Loop 1."
> "Loop 2."
> "Loop 3."

Tương tự như với vòng lặp while, trường hợp sử dụng phổ biến nhất cho do...while là vòng lặp của độ dài không xác định:

let randomNum;
do {
  randomNum = ( () => Math.floor( Math.random() * 10 ) )();
  console.log( `Is the number ${ randomNum }?` );
} while ( randomNum !== 3 );
console.log( `Yes, ${ randomNum } was the correct number.` );
> "Is the number 9?"
> "Is the number 2?"
> "Is the number 8?"
> "Is the number 2?"
> "Is the number 3?"
> "Yes, 3 was the correct number."

for

Sử dụng vòng lặp for để lặp lại một số lượng đã biết. Trong các cơ sở mã cũ, đây là thường được dùng để lặp lại các phần tử trong một mảng.

Để tạo vòng lặp for, hãy dùng từ khoá for, tiếp đến là một cặp dấu ngoặc đơn chấp nhận ba biểu thức sau theo thứ tự và được phân tách bằng dấu chấm phẩy:

  1. Biểu thức sẽ được đánh giá khi vòng lặp bắt đầu
  2. Một điều kiện xác định liệu vòng lặp có nên tiếp tục hay không
  3. Biểu thức sẽ được thực thi ở cuối mỗi vòng lặp

Sau các dấu ngoặc đơn này, hãy thêm câu lệnh (thường là câu lệnh khối) là được thực thi trong vòng lặp.

for( let i = 0; i < 3; i++ ) {
  console.log( "This loop will run three times.")
}

Biểu thức đầu tiên khởi tạo một biến đóng vai trò là bộ đếm. Chiến dịch này biểu thức được đánh giá một lần, trước lần lặp lại đầu tiên của vòng lặp. Bạn có thể khởi tạo biến này bằng cách sử dụng let (hoặc var, trước đây) như bất kỳ biến nào khác và phạm vi của nó là phần nội dung của vòng lặp. Các biến này có thể có bất kỳ giá trị nhận dạng hợp lệ, nhưng chúng thường được gọi là i cho "vòng lặp" hoặc "chỉ mục". Điều này có vẻ trái ngược với tuyên bố của các phương pháp hay nhất về tên nhận dạng có thể dự đoán, nhưng quy ước được xây dựng đủ rõ ràng để các nhà phát triển khác hiểu rõ trong nháy mắt. Vì các bộ sưu tập được lập chỉ mục đều không được lập chỉ mục, các biến này hầu như luôn có giá trị ban đầu là 0.

Giống như các dạng vòng lặp khác, điều kiện là một biểu thức xác định liệu vòng lặp có được thực thi hay không. URL này thường được dùng nhất để đặt bị ràng buộc cho bộ đếm lặp lại. Trình thông dịch đánh giá điều kiện trước khi thực thi vòng lặp for lần đầu tiên.Nếu điều kiện không ban đầu đánh giá thành true, thì phần nội dung của vòng lặp sẽ không được thực thi.

Biểu thức cuối cùng được thực thi ở cuối mỗi lần lặp thông qua vòng lặp. Mã này thường được dùng để tăng giá trị nhận dạng thêm một.

Thường thì bạn sẽ thấy vòng lặp for lặp lại qua các mảng ở cơ sở mã. Trong những trường hợp này, điều kiện được chỉ định để tiếp tục vòng lặp là số lần lặp nhỏ hơn hoặc bằng độ dài của mảng được lặp qua. Biến dùng để theo dõi số lần lặp lại hiện tại được dùng để tìm giá trị được liên kết với chỉ mục đó trong mảng, cho phép mỗi phần tử trong mảng cần thực hiện theo thứ tự:

var myArray = [ true, false, true ];
for( let i = 0; i <= myArray.length; i++ ) {
  console.log( myArray[ i ] );
}
> true
> false
> true

Phương pháp này không còn được sử dụng nhiều, thay vào đó là các phương pháp hiện đại hơn để lặp lại theo cấu trúc dữ liệu có thể lặp lại.

for [...] of [...]

Sử dụng vòng lặp for...of... để lặp lại các giá trị được lưu trữ trong một cấu trúc dữ liệu có thể lặp lại, chẳng hạn như mảng, tập hợp hoặc bản đồ.

Vòng lặp for...of... sử dụng từ khoá for, theo sau là một cặp dấu ngoặc đơn chứa một biến, theo sau là of, sau đó là cấu trúc dữ liệu được lặp lại qua. Biến có thể là nội dung khai báo được thực hiện tại đây bằng let, const hoặc var, một biến được khai báo trước đó trong phạm vi hiện tại, một đối tượng thuộc tính hoặc một bản sao của chỉ định huỷ cấu trúc. Phương thức này chứa giá trị của phần tử tương ứng với vòng lặp hiện tại vòng lặp.

const myIterable = [ true, false, true ];
for( const myElement of myIterable ) {
  console.log( myElement );
}
> true
> false
> true

Trong ví dụ này, việc sử dụng const cho myElement hoạt động ngay cả khi myElement là được cung cấp một giá trị mới trong mỗi lần lặp lại của vòng lặp. Nguyên nhân là do các biến được khai báo bằng let hoặc const đều thuộc phạm vi của câu lệnh khối trong vòng lặp. Biến này được khởi tạo ở đầu mỗi lần lặp và bị xoá lúc điểm kết thúc của vòng lặp đó.

for...in...

Sử dụng vòng lặp for...in... để lặp lại các thuộc tính có thể đếm được của một đối tượng, bao gồm các thuộc tính kế thừa có thể liệt kê. Tương tự như với vòng lặp for...of..., một Vòng lặp for...in... sử dụng từ khoá for, theo sau là một cặp dấu ngoặc đơn có chứa một biến chứa giá trị của khoá thuộc tính tương ứng với vòng lặp hiện tại. Theo sau biến này là in từ khoá, sau đó đối tượng sẽ được lặp lại:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
  console.log( myKey );
}
> "myProperty"
> "mySecondProperty"

Xin nhắc lại, mặc dù giá trị của myKey thay đổi sau mỗi lần lặp lại, bạn có thể sử dụng const mà không có lỗi vì biến này đã bị loại bỏ một cách hiệu quả ở cuối mỗi lần lặp, sau đó được tạo lại ở đầu.

Giá trị được liên kết với mỗi khoá thuộc tính không được cung cấp trực tiếp cho Cú pháp for...in.... Tuy nhiên, do vòng lặp này có quyền truy cập vào khoá thuộc tính trong mỗi lần lặp lại, bạn có thể sử dụng khoá đó để "tra cứu" giá trị của nó:

const myObject = { "myProperty" : true, "mySecondProperty" : false };
for( const myKey in myObject ) {
  const myValue = myObject[ myKey ];
  console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "mySecondProperty : false"

Các thuộc tính kế thừa từ hàm khởi tạo tích hợp là không thể liệt kê, nghĩa là for...in... không lặp lại thông qua các thuộc tính kế thừa từ Object hàm khởi tạo. Tuy nhiên, mọi thuộc tính có thể liệt kê trong phần tử của đối tượng chuỗi nguyên mẫu được bao gồm:

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty: {
    value: true,
    enumerable: true
    }
});
for ( const myKey in myObject ) {
  const myValue = myObject[ myKey ];
  console.log( `${ myKey } : ${ myValue }` );
}
> "myProperty : true"
> "protoProperty : true"

JavaScript cung cấp các phương thức tích hợp để xác định xem một thuộc tính có phải là thuộc tính trực tiếp của đối tượng thay vì thuộc tính trên nguyên mẫu của đối tượng chuỗi: chuỗi video hiện đại Object.hasOwn() và các phương thức Object.prototype.hasOwnProperty() cũ. Các các phương thức đánh giá xem một thuộc tính cụ thể có được kế thừa (hay không được khai báo), chỉ trả về true cho các thuộc tính tức thì của một đối tượng được chỉ định:

const myPrototype = { "protoProperty" : true };
const myObject = Object.create( myPrototype, {
    myProperty: {
    value: true,
    enumerable: true
    }
});
for ( const myKey in myObject ) {
  const myValue = myObject[ myKey ];
  if ( Object.hasOwn( myObject, myKey ) ) {
    console.log( `${ myKey } : ${ myValue }` );
  }
}
> "myProperty : true"

Ngoài ra còn có 3 phương thức tĩnh mà mỗi phương thức trả về một Mảng được tạo thành từ một Các khoá có thể liệt kê của đối tượng (Object.keys()), giá trị (Object.values()) hoặc cặp khoá-giá trị (Object.entries()):

const myObject = { "myProperty" : true, "mySecondProperty" : false };
Object.keys( myObject );
> Array [ "myProperty", "mySecondProperty" ]

Điều này cho phép bạn lặp lại các khoá, giá trị hoặc cặp khoá-giá trị của Đối tượng (sử dụng chỉ định huỷ cấu trúc) mà không bao gồm các thuộc tính mà nguyên mẫu của Đối tượng đó sở hữu:

const myPrototype = { "protoProperty" : "Non-enumerable property value." };
const myObject = Object.create( myPrototype, {
    myProperty: {
    value: "Enumerable property value.",
    enumerable: true
    }
});

for ( const propKey of Object.keys( myObject ) ) {
  console.log( propKey );
}
> "myProperty"

for ( const propValue of Object.values( myObject ) ) {
  console.log( propValue );
}
> "Enumerable property value."

for ( const [ propKey, propValue ] of Object.entries( myObject ) ) {
  console.log( `${ propKey } : ${ propValue }` );
}
> "myProperty : Enumerable property value."

forEach()

Các phương thức forEach() do Array cung cấp, Bản đồ, Đặt, và các hàm khởi tạo NodeList là cách viết tắt hữu ích để lặp lại qua một dữ liệu cấu trúc trong ngữ cảnh của hàm callback. Không giống như các dạng vòng lặp khác, vòng lặp được tạo bằng bất kỳ phương thức forEach() nào đều không thể bị gián đoạn bằng break hoặc continue.

forEach là một phương thức thuộc sở hữu của từng nguyên mẫu cấu trúc dữ liệu. Mỗi forEach phương thức mong đợi một hàm callback làm đối số, mặc dù chúng hơi khác nhau về các phần tử của đối số được đưa vào khi hàm đó được gọi. Thứ hai, không bắt buộc đối số chỉ định một giá trị this để dùng làm ngữ cảnh gọi cho giá trị hàm callback.

Hàm callback dùng với Array.forEach cung cấp các tham số chứa giá trị của phần tử hiện tại, chỉ mục của phần tử hiện tại và mảng mà phương thức forEach đã được gọi:

const myArray = [ true, false ];
myArray.forEach( ( myElement, i, originalArray ) => {
  console.log( i, myElement, originalArray  );
});
> 0 true Array(3) [ true, false ]
> 1 false Array(3) [ true, false ]

Hàm callback dùng với Map.forEach cung cấp các tham số chứa giá trị được liên kết với phần tử hiện tại, khoá liên kết với phần tử hiện tại và Ánh xạ, phương thức forEach đã được gọi vào:

const myMap = new Map([
  ['myKey', true],
  ['mySecondKey', false ],
]);
myMap.forEach( ( myValue, myKey, originalMap ) => {
    console.log( myValue, myKey, originalMap  );
});
> true "myKey" Map { myKey → true, mySecondKey → false }
> false "mySecondKey" Map { myKey → true, mySecondKey → false }

Lệnh gọi lại Set.forEach bao gồm các tham số tương tự. Vì Set không có chỉ mục hoặc khoá khác với giá trị, thì đối số thứ hai sẽ cung cấp giá trị thừa, không thể bỏ qua, mà chỉ cần dùng cú pháp nhất quán với các phương thức forEach khác.

const mySet = new Set([ true, false ]);
mySet.forEach( ( myValue, myKey, originalSet ) => {
  console.log( myValue, myKey, originalSet  );
});
> true true Set [ true, false ]
> false false Set [ true, false ]

Bộ lặp

Có thể lặp lại là bất kỳ cấu trúc dữ liệu nào được tạo thành từ các phần tử riêng lẻ có thể lặp lại bằng các phương pháp tiếp cận đã nêu chi tiết trước đó. Biến lặp là một đối tượng có thể lặp lại tuân theo giao thức biến lặp, tức là đối tượng này phải triển khai phương thức next() tiến lần lượt qua các phần tử mà nó chứa mỗi khi phương thức đó được gọi, trả về một đối tượng cho mỗi tuần tự ở một định dạng cụ thể.

Cấu trúc dữ liệu lặp tích hợp sẵn của JavaScript (chẳng hạn như Array (Mảng), Bản đồSet) không phải là biến lặp trong và nhưng tất cả chúng đều kế thừa phương thức iterator, có thể truy cập được bằng cách sử dụng @@iterator Biểu tượng phổ biến, phương thức này trả về một đối tượng lặp được tạo từ cấu trúc dữ liệu có thể lặp lại:

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterable;
> (3) [1, 2, 3]

myIterator;
> Array Iterator {}

Khi gọi phương thức next() trên một đối tượng lặp, nó sẽ thực hiện các bước qua các phần tử mà nó thực hiện chứa một đối tượng lần lượt, mỗi lệnh gọi trả về một đối tượng chứa hai thuộc tính: value, chứa giá trị của phần tử hiện tại và done, một boolean cho chúng ta biết liệu biến lặp đã truyền phần tử cuối cùng trong cấu trúc dữ liệu. Giá trị của done chỉ là true khi gọi next() dẫn đến việc truy cập một phần tử nằm ngoài phần tử cuối cùng trong biến lặp.

const myIterable = [ 1, 2, 3 ];
const myIterator = myIterable[ Symbol.iterator ]();

myIterator.next();
> Object { value: 1, done: false }

myIterator.next();
> Object { value: 2, done: false }

myIterator.next();
> Object { value: 3, done: false }

myIterator.next();
> Object { value: undefined, done: true }

Chức năng của trình tạo

Dùng từ khoá function* (lưu ý dấu hoa thị) để khai báo trình tạo hàm hoặc xác định biểu thức hàm tạo:

function* myGeneratorFunction() { };

Giống như biến lặp, hàm tạo duy trì trạng thái. Gọi một hàm trình tạo trả về một đối tượng Trình tạo mới nhưng không trả về ngay lập tức thực thi mã trong phần nội dung của hàm:

function* myGeneratorFunction() {
  console.log( "Generator function body ")
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject;
> Generator {  }

typeof myGeneratorObject;
> "object"

Đối tượng trình tạo tuân theo giao thức biến lặp. Giá trị mà mỗi lệnh gọi đến next() trên hàm biểu thức trả về được xác định bằng biểu thức yield, Thao tác này sẽ tạm dừng việc thực thi hàm tạo và trả về giá trị của biểu thức chứa từ khoá yield. Lệnh gọi sau đến next() tiếp tục thực thi hàm, tạm dừng ở biểu thức yield tiếp theo và trả về giá trị được liên kết.

function* myGeneratorFunction() {
  yield "My first yielded value.";
  yield "My second yielded value.";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject.next();
> Object { value: "My first yielded value.", done: false }

myGeneratorObject.next();
> Object { value: "My second yielded value.", done: false }

Khi next() được gọi sau khi không có giá trị nào khác được chỉ định bằng yield, return hoặc throw (trong trường hợp xảy ra lỗi), phần còn lại của hàm nội dung thực thi và Đối tượng được trả về có valueundefineddone thuộc tính của true:


function* myGeneratorFunction() {
    console.log( "Start of the generator function." );
    yield "First";
    console.log( "Second part of the generator function."  );
    yield "Second";
    console.log( "Third part of the generator function." );
    yield "Third";
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject.next();
> "Start of the generator function."
> Object { value: "First", done: false }

myGeneratorObject.next();
> "Second part of the generator function."
> Object { value: "Second", done: false }

myGeneratorObject.next();
> "Third part of the generator function."
> Object { value: "Third", done: false }

myGeneratorObject.next();
> Object { value: undefined, done: true }

Chỉ sử dụng next() trên Đối tượng mà hàm tạo trả về, chứ không phải trên chính hàm biểu thị hàm tạo. Nếu không, mỗi lệnh gọi đến hàm trình tạo tạo một Đối tượng trình tạo mới:

function* myGeneratorFunction() {
  yield "First";
  yield "Second";
};

myGeneratorFunction().next();
> Object { value: "First", done: false }

myGeneratorFunction().next();
> Object { value: "First", done: false }

Tương tự như với bất kỳ hàm nào khác, hàm tạo sẽ tạm dừng khi gặp return từ khoá. Sau đó, phương thức này sẽ trả về một Đối tượng cho ngữ cảnh gọi có chứa giá trị trả về và thuộc tính done có giá trị true.

function* myGeneratorFunction() {
  yield 1;
  yield 2;
  return 3;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject.next().done;
> Object { value: 1, done: false }

myGeneratorObject.next().done;
> Object { value: 2, done: false }

myGeneratorObject.next();
> Object { value: 3, done: true }

Biểu thức yield có thể chấp nhận một số ngữ nghĩa của giá trị nhận dạng, cho phép "giao tiếp" hai chiều từ và quay lại phần bị tạm ngưng của hàm tạo. Khi một giá trị được truyền vào phương thức next() của trình tạo dưới dạng một đối số, nó sẽ thay thế giá trị được liên kết với đối số trước đó, bị tạm ngưng Biểu thức yield:

function* myGeneratorFunction() {
    const firstYield = yield;
    yield firstYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject.next();
> Object { value: undefined, done: false }

myGeneratorObject.next( 5 );
> Object { value: 15, done: false }

Lưu ý rằng điều này thay thế toàn bộ biểu thức được liên kết với yield trước đó và không chỉ định lại giá trị của yield trước đó cho giá trị được chỉ định trong next():

function* myGeneratorFunction() {
    const firstYield = yield;
    const secondYield = yield firstYield + 100;
    yield secondYield + 10;
};
const myGeneratorObject = myGeneratorFunction();

myGeneratorObject.next();
> Object { value: undefined, done: false }

myGeneratorObject.next( 10 ); // Can be thought of as changing the value of the `firstYield` variable to `10
> Object { value: 110, done: false }

myGeneratorObject.next( 20 ); // Can be thought of as changing the value of the `secondYield` variable to `20`, _not_ `20 + 100;`
> Object { value: 30, done: false }

Mọi đối số được truyền đến lệnh gọi đầu tiên đến next() đều bị bỏ qua do không có biểu thức yield trước đó để chấp nhận giá trị đó. Giống như bất kỳ hàm nào khác, các đối số được truyền đến lệnh gọi hàm trình tạo ban đầu có sẵn trong suốt phạm vi phần nội dung của hàm trình tạo:

function* myGeneratorFunction( startingValue ) {
    let newValue = yield startingValue + 1;
    newValue = yield newValue + 10;
    yield startingValue + 20;
};
const myGeneratorObject = myGeneratorFunction( 2 );

myGeneratorObject.next( 1 );
> Object { value: 3, done: false }

myGeneratorObject.next( 5 );
> Object { value: 15, done: false }

myGeneratorObject.next( 10 );
Object { value: 22, done: false }

Toán tử yield* (lưu ý rằng dấu hoa thị) được dùng cùng với toán tử lặp, chẳng hạn như một hàm tạo khác, để lặp lại và tạo ra từng giá trị toán hạng của nó trả về:

function* mySecondaryGenerator() {
  yield 2;
  yield 3;
}

function* myGenerator() {
  yield 1;
  yield* mySecondaryGenerator();
  yield 4;
  return 5;
}

const myIterator = myGenerator();

myIterator.next();
> Object { value: 1, done: false }

myIterator.next();
> Object { value: 2, done: false }

myIterator.next();
> Object { value: 3, done: false }

myIterator.next();
> Object { value: 4, done: false }

myIterator.next();
> Object { value: 5, done: true }

JavaScript không đồng bộ

Mặc dù về cơ bản, JavaScript có tính đồng bộ trong thực thi, có những cơ chế cho phép nhà phát triển tận dụng vòng lặp sự kiện để thực hiện các tác vụ không đồng bộ.

Lời hứa

Lời hứa là phần giữ chỗ cho một giá trị không xác định khi nào lời hứa được đặt đã tạo. Đó là một vùng chứa quy định một hoạt động không đồng bộ, các điều khoản theo mà thao tác được coi là thành công hoặc không thành công, các hành động cần được thực hiện trong cả hai trường hợp và giá trị thu được.

Tạo một thực thể Promise bằng toán tử new với Promise tích hợp sẵn hàm khởi tạo. Hàm khởi tạo này chấp nhận một hàm được gọi là thực thi làm đối số. Hàm thực thi đó thường được dùng để thực hiện một hoặc nhiều hành động không đồng bộ, sau đó chỉ ra các điều khoản mà Promise sẽ được dùng được coi là đã thực hiện thành công hoặc bị từ chối. Lời hứa được định nghĩa là đang chờ xử lý trong khi hàm thực thi đang chạy. Sau khi người thực thi kết thúc, một Promise (Lời hứa) được coi là đã thực hiện (hoặc đã được giải quyết, trong một số nguồn tài liệu) nếu đã hoàn tất hàm thực thi và hành động không đồng bộ mà nó thực hiện thành công và bị từ chối nếu hàm thực thi gặp lỗi, hoặc thì hành động không đồng bộ đang được thực hiện sẽ không thành công. Sau khi thực hiện Lời hứa hoặc bị từ chối thì việc đó được coi là đã giải quyết.

const myPromise = new Promise( () => { });

Hàm khởi tạo gọi hàm thực thi với 2 đối số. Những đối số đó là các hàm cho phép bạn thực hiện hoặc từ chối Lời hứa theo cách thủ công:

const  myPromise = new Promise( ( fulfill, reject ) => { });

Các hàm dùng để thực hiện hoặc từ chối Lời hứa sẽ được gọi với kết quả giá trị của Promise làm đối số (thường là lỗi khi từ chối):

const myPromise = new Promise( ( fulfill, reject ) => {
  const myResult = true;
  setTimeout(() => {
    if( myResult === true ) {
        fulfill( "This Promise was successful." );    
    } else {
        reject( new Error( "This Promise has been rejected." ) );
    }
  }, 10000);
});

myPromise;
> Promise { <state>: "pending" }

myPromise;
> Promise { <state>: "fulfilled", <value>: "This Promise was successful." }

Chuỗi lời hứa

Bạn có thể thực hiện thao tác với đối tượng Promise thu được bằng cách sử dụng then(), catch() và Các phương thức finally() kế thừa từ hàm khởi tạo Promise. Mỗi phương án trong số này các phương thức sẽ trả về một Promise (Lời hứa) có thể được thực hiện ngay lập tức bằng then(), catch() hoặc finally() lại, cho phép bạn tạo chuỗi các Lời hứa thu được.

then() cung cấp 2 hàm callback làm đối số. Sử dụng phương thức đầu tiên để thực hiện đơn hàng Lời hứa kết quả và người thứ hai từ chối lời hứa đó. Cả hai phương thức đều chấp nhận một mã đối số cung cấp kết quả Promise giá trị của nó.

const myPromise = new Promise( ( fulfill, reject ) => {
  const myResult = true;
  setTimeout(() => {
    if( myResult === true ) {
        fulfill( "This Promise was fulfilled." );    
    } else {
        reject( new Error( "This Promise has been rejected." ) );
    }
  }, 100);
});

myPromise.then( successfulResult => console.log( successfulResult ), failedResult => console.error( failedResult ) );
> "This Promise was successful."

Bạn cũng có thể sử dụng then() để chỉ xử lý trạng thái đã hoàn tất và catch để xử lý trạng thái bị từ chối. Gọi catch với một đối số duy nhất chứa giá trị được cung cấp trong phương thức từ chối của Promise:

const myPromise = new Promise( ( fulfill, reject ) => {
  const myResult = false;
  setTimeout(() => {
    if( myResult === true ) {
        fulfill( "This Promise was fulfilled." );    
    } else {
        reject( new Error( "This Promise has been rejected." ) );
    }
  }, 100);
});

myPromise
  .then( fulfilledResult => console.log(fulfilledResult ) )
  .catch( rejectedResult => console.log( rejectedResult ) )
  .finally( () => console.log( "The Promise has settled." ) );
> "Error: This Promise has been rejected."
> "The Promise has settled."

Không giống như thencatch, cho phép một hàm xử lý chạy khi Promise (Lời hứa) được thực hiện hoặc bị từ chối, một hàm được truyền dưới dạng đối số cho finally được gọi bất kể Lời hứa đã được thực hiện hay bị từ chối. Hàm xử lý được gọi mà không có đối số vì hàm này không nhằm làm việc với các giá trị được chuyển từ Lời hứa, chỉ thực thi mã sau Lời hứa đã hoàn tất.

Đồng thời

Hàm khởi tạo Promise cung cấp 4 phương thức để xử lý nhiều phương thức liên quan Lời hứa, sử dụng đối tượng có thể lặp lại chứa đối tượng Promise. Các mỗi phương thức đều trả về một Promise, được thực hiện hoặc bị từ chối dựa trên trạng thái những Lời hứa được gửi đến nó. Ví dụ: Promise.all() tạo một Lời hứa chỉ được thực hiện nếu mọi Lời hứa được truyền đến phương thức đó được thực hiện:

const firstPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const secondPromise = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const thirdPromise  = new Promise( ( fulfill, reject ) => fulfill( "Successful. ") );
const failedPromise = new Promise( ( fulfill, reject ) => reject( "Failed.") );
const successfulPromises = [ firstPromise, secondPromise, thirdPromise ];
const oneFailedPromise = [ failedPromise, ...successfulPromises ];

Promise.all( successfulPromises )
  .then( ( allValues ) => {
    console.log( allValues );
  })
  .catch( ( failValue ) => {
    console.error( failValue );
  });
> Array(3) [ "Successful. ", "Successful. ", "Successful. " ]

Promise.all( oneFailedPromise  )
    .then( ( allValues ) => {
      console.log( allValues );
    })
    .catch( ( failValue ) => {
     console.error( failValue );
    });
> "Failed."

Sau đây là các phương thức đồng thời Promise:

Promise.all()
Chỉ thực hiện nếu tất cả Lời hứa đã cung cấp đều được thực hiện.
Promise.any()
Đã thực hiện nếu có một trong những Lời hứa đã cung cấp được thực hiện và chỉ bị từ chối nếu tất cả Lời hứa đều bị từ chối.
Promise.allSettled()
Thực hiện lời hứa khi Lời hứa đã thực hiện, bất kể kết quả là gì.
Promise.race()
Bị từ chối hoặc thực hiện dựa trên kết quả của Lời hứa thanh toán đầu tiên, bỏ qua tất cả các Lời hứa đã thực hiện sau đó.

async/await

Khi bạn sử dụng từ khoá async trước khai báo hàm hoặc biểu thức hàm, bất kỳ mà hàm trả về sẽ được trả về dưới dạng một Lời hứa đã thực hiện có chứa giá trị. Điều này cho phép bạn chạy và quản lý các hoạt động không đồng bộ bằng cách sử dụng cùng một làm công việc phát triển đồng bộ.

async function myFunction() {
  return "This is my returned value.";
}

myFunction().then( myReturnedValue => console.log( myReturnedValue ) );
> "This is my returned value."

Biểu thức await tạm dừng việc thực thi hàm không đồng bộ trong khi Lời hứa liên quan đã được thanh toán. Sau khi Lời hứa được thanh toán, giá trị của biểu thức await là giá trị đã thực hiện hoặc bị từ chối của Lời hứa.

async function myFunction() {
  const myPromise  = new Promise( ( fulfill, reject ) => { setTimeout( () => fulfill( "Successful. "), 5000 ); });
  const myPromisedResult = await myPromise;
  return myPromisedResult;
}

myFunction()
  .then( myResult => console.log( myResult ) )
  .catch( myFailedResult => console.error( myFailedResult ) );
> "Successful."

Mọi giá trị không phải Promise có trong biểu thức await đều được trả về dưới dạng một đã thực hiện lời hứa:

async function myFunction() {
  const myPromisedResult = await "String value.";
  return myPromisedResult;
}

myFunction()
  .then( myResult => console.log( myResult ) )
  .catch( myFailedResult => console.error( myFailedResult ) );
> "String value."

Kiểm tra kiến thức

Bạn sử dụng loại vòng lặp nào để lặp lại một số lượng đã biết?

for
while
do...while