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
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 đề case
và default
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:
- Biểu thức sẽ được đánh giá khi vòng lặp bắt đầu
- 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
- 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 đồ và
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ó value
là undefined
và done
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ư then
và catch
, 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
do...while
while