Loops and iteration

Vòng lặp giúp thực hiện một hành động nhiều lần một cách nhanh chóng và đơn giản. Chương Hướng dẫn JavaScript này giới thiệu về các câu lệnh lặp trong JavaScript.

Hãy tưởng tượng vòng lặp giống như phiên bản vi tính của trò chơi mà bạn bảo một người đi X bước sang một hướng rồi đi Y bước sang một hướng khác; chẳng hạn, ý tưởng trò "Tiến 5 bước về phía Đông" có thể được biểu diễn dưới dạng vòng lặp như sau:

var step;
for (let step = 0; step < 5; step++) {
  // Chạy 5 lần, với giá trị chạy từ 0 đến 4.
  console.log('Walking east one step');
}

Có nhiều kiểu vòng lặp khác nhau, nhưng tất cả đều thực hiện cùng một việc: làm lại một hành động trong số lần nhất định (số lần đó có thể là 0). Mỗi vòng lặp có các cơ chế khác nhau, tương ứng với đó là sự khác nhau giữa cách xác định điểm bắt đầu và kết thúc của vòng lặp. Tuỳ theo trường hợp xác định mà lựa kiểu vòng lặp cho phù hợp..

Các lệnh lặp trong JavaScript là:

Lệnh for

Vòng lặp for thực hiện liên tục cho tới khi điều kiện ban đầu trả về false. Vòng for trong JavaScript tương tự như vòng for trong Java hoặc C. Lệnh for có dạng như sau:

for ([biểu_thức_khởi_tạo]; [điều_kiện]; [biểu_thức_tăng_tiến])
  lệnh

Vòng lặp for thực thi như sau:

  1. Biểu thức khởi tạo (biểu_thức_khởi_tạo), nếu có, được thực thi. Biểu thức này thường khởi tạo một hoặc nhiều biến đếm cho vòng lặp, nhưng cú pháp của nó vẫn có thể tạo ra biểu thức có độ phức tạp. Biểu thức này còn có thể khai báo biến.
  2. Biểu thức điều kiện (điều_kiện) được tính toán. Nếu giá trị của điều_kiện trả về true, các lệnh trong vòng lặp được thực thi. Nếu giá trị của điều_kiện trả về false, vòng lặp for bị dừng lại. Nếu biểu thức điều_kiện bị khuyết (bỏ qua không đặt), điều kiện sẽ được gán giả định là true.
  3. lệnh sẽ được thực thi. Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các lệnh này.
  4. Nếu không có lỗi nào xảy ra sau khi thực thi lệnh, biểu thức cập nhật (biểu_thức_tăng_tiến) được thực thi.
  5. Quay lại bước 2.

Ví dụ

Hàm dưới đây chứa lệnh for đếm số option được chọn trong danh sách cuộn (phần tử <select> cho phép chọn nhiều). Lệnh for khai báo biến i và khởi tạo cho nó là 0. Điều kiện kiểm tra i có nhỏ hơn số option mà phần tử <select> sở hữu hay không, nếu thoả mãn điều kiện, chương trình sẽ chạy vào lệnh if, và tăng i thêm một.

<form name="selectForm">
  <p>
    <label for="musicTypes">Choose some music types, then click the button below:</label>
    <select id="musicTypes" name="musicTypes" multiple="multiple">
      <option selected="selected">R&B</option>
      <option>Jazz</option>
      <option>Blues</option>
      <option>New Age</option>
      <option>Classical</option>
      <option>Opera</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="How many are selected?" /></p>
</form>

<script>
function howMany(selectObject) {
  var numberSelected = 0;
  for (var i = 0; i < selectObject.options.length; i++) {
    if (selectObject.options[i].selected) {
      numberSelected++;
    }
  }
  return numberSelected;
}

var btn = document.getElementById('btn');
btn.addEventListener('click', function() {
  alert('Number of options selected: ' + howMany(document.selectForm.musicTypes));
});
</script>

Lệnh do...while

Lệnh do...while thực hiện liên tục cho tới khi điều kiện xác định trả về false. Lệnh do...while có dạng như sau:

do
  lệnh
while (điều_kiện);

lệnh luôn được thực thi một lần trước khi kiểm tra điều kiện (và rồi cứ thế cho tới khi điều kiện trả về false). Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các câu lệnh. Nếu điều_kiện trả về true, lệnh lại được thực thi một lần nữa. Sau mỗi lần thực thi, chương trình kiểm tra lại điều kiện. Khi điều kiện trả về false, chương trình dừng thực thi vòng lặp và truyền điều khiển xuống lệnh liền sau vòng do...while.

Ví dụ

Trong ví dụ sau, lặp do lặp ít nhất một lần và tái duyệt cho tới khi i không nhỏ hơn 5.

var i = 0;
do {
  i += 1;
  console.log(i);
} while (i < 5);

Lệnh while

Lệnh while thực thi các lệnh bên trong nó ngay khi điều kiện xác định trả về true. Lệnh while có dạng như sau:

while (điều_kiện)
  lệnh

Nếu điều kiện trả về false, chương trình sẽ ngừng thực thi lệnh bên trong vòng lặp và truyền điều khiển xuống lệnh liền sau vòng lặp.

Chương trình kiểm tra điều kiện trước khi thực thi lệnh bên trong. Nếu điều kiện trả về true, lệnh sẽ được thực thi và kiểm tra lại điều kiện. Nếu điều kiện trả về false, ngừng thực thi và truyền điều khiển xuống lệnh liền sau vòng lặp while.

Để thực thi nhiều lệnh, dùng khối lệnh ({ ... }) để gom nhóm các câu lệnh.

Ví dụ 1

Vòng while lặp lại cho tới khi n nhỏ hơn 3:

var n = 0;
var x = 0;
while (n < 3) {
  n++;
  x += n;
}

Ứng với mỗi lần lặp, tăng n thêm một và cộng giá trị đó vào x. Bởi vậy, xn có giá trị như sau:

  • Sau lần lặp thứ nhất: n = 1 và x = 1
  • Sau lần lặp thứ hai: n = 2 và x = 3
  • Sau lần lặp thứ ba: n = 3 và x = 6

Sau khi hoàn thành lần lặp thứ ba, điều kiện n < 3 không còn trả về true nữa nên vòng lặp bị kết thúc.

Ví dụ 2

Hãy tránh lặp vô hạn. Hãy đảm bảo rằng điều kiện của vòng lặp sẽ dần trả về false; bằng không, vòng lặp sẽ không bao giờ kết thúc. Lệnh trong vòng lặp while dưới đây được thực thi mãi mãi vì điều kiện không bao giờ trả về false:

while (true) {
  console.log('Hello, world!');
}

Lệnh gán nhãn

label tạo ra một lệnh đi kèm với một định danh, thông qua định danh giúp bạn tham chiếu tới nó từ những nơi khác trong chương trình của bạn. Chẳng hạn, bạn có thể dùng nhãn để định danh một vòng lặp, rồi dùng lệnh break hoặc continue để chương trình xác định có nên dừng hay tiếp tục thực thi vòng lặp hay không.

Cú pháp của lệnh gán nhãn có dạng như sau:

nhãn :
   lệnh

Giá trị của nhãn có thể là định danh JavaScript bất kỳ nhưng không phải từ dành riêng (tên biến, tên hàm hoặc nhãn khác). lệnh được gán với nhãn có thể là bất kỳ lệnh nào.

Ví dụ

Trong ví dụ này, nhãn markLoop giúp định danh cho một vòng lặp while.

markLoop:
while (theMark == true) {
   doSomething();
}

Lệnh break

Dùng lệnh break để kết thúc vòng lặp, kết thúc switch, hoặc liên hợp với một lệnh được gán nhãn.

  • Khi dùng break không kèm với nhãn, chương trình kết thúc ngay tức thì vòng while, do-while, for, hoặc switch trong cùng bọc lệnh break và truyền điều khiển xuống lệnh liền sau.
  • Khi dùng break kèm với nhãn, nó sẽ chấm dứt việc thực thi lệnh được gắn nhãn đó.

Cú pháp lệnh break có dạng như sau:

break;
break [nhãn];

Kiểu cú pháp đầu tiên kết thúc vòng lặp trong cục bao bọc hoặc switch; kiểu thứ hai kết thúc lệnh gắn với nhãn.

Ví dụ 1

Trong ví dụ sau, lệnh lặp sẽ lặp qua từng phần tử của mảng (thông qua chỉ mục của từng phần tử) cho tới khi gặp phần tử có giá trị bằng theValue:

for (var i = 0; i < a.length; i++) {
  if (a[i] == theValue) {
    break;
  }
}

Ví dụ 2: Áp dụng break cho nhãn

var x = 0;
var z = 0;
labelCancelLoops: while (true) {
  console.log('Outer loops: ' + x);
  x += 1;
  z = 1;
  while (true) {
    console.log('Inner loops: ' + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}

Lệnh continue

Lệnh continue có thể dùng để tái duyệt các vòng while, do-while, for, hoặc label.

  • Khi dùng continue không kèm theo label, chương trình ngừng thực thi lần lặp hiện tại của vòng while, do-while, hoặc for gần nhất bọc lệnh continue và tiếp tục thực thi lần lặp tiếp theo. Trái với lệnh break, continue không dừng vòng lặp hoàn toàn. Trong vòng lặp while, chương trình nhảy về đoạn xét điều kiện. Trong vọng lặp for, chương trình nhảy tới biểu_thức_tăng_tiến.
  • Khi dùng continue có kèm theo label, lệnh continue sẽ áp dụng cho vòng lặp được gán nhãn đó.

Cú pháp của lệnh continue có dạng như sau:

continue [label];

Ví dụ 1

Trong ví dụ sau, vòng while với lệnh continue thực thi khi giá trị của i bằng 3. Thế nên, n sẽ có giá trị là 1, 3, 7 và 12.

var i = 0;
var n = 0;
while (i < 5) {
  i++;
  if (i == 3) {
    continue;
  }
  n += i;
  console.log(n);
}
//1,3,7,12


var i = 0; 
var n = 0; 
while (i < 5) { 
  i++; 
  if (i == 3) { 
     // continue; 
  } 
  n += i; 
  console.log(n);
}
// 1,3,6,10,15

Ví dụ 2

Lệnh nhãn checkiandj chứa nhãn checkj. Nếu chương trình chạy tới continue, chương trình sẽ ngừng thực hiện lần lặp hiện tại của checkj và bắt đầu thực hiện lần lặp kế tiếp. Mỗi khi chạy tới continue, checkj tái duyệt cho tới khi biểu thức điều kiện của nó trả về false. Khi false được trả về, phần còn lại của lệnh checkiandj được hoàn thành, và checkiandj tái duyệt cho tới khi điều kiện của nó trả về false. Khi false được trả về, chương trình tiếp tục thực thi lệnh liền sau checkiandj.

Nếu continue gắn với nhãn checkiandj, chương trình sẽ tiếp tục ở đầu lệnh checkiandj .

var i = 0;
var j = 10;
checkiandj:
  while (i < 4) {
    console.log(i);
    i += 1;
    checkj:
      while (j > 4) {
        console.log(j);
        j -= 1;
        if ((j % 2) == 0) {
          continue checkj;
        }
        console.log(j + ' is odd.');
      }
      console.log('i = ' + i);
      console.log('j = ' + j);
  }

Lệnh for...in

Lệnh for...in lặp một biến (variable) đã được xác định trước, qua mọi thuộc tính có thể đếm được (enumerable properties) của đối tượng (object). Với từng phần tử riêng biệt, JavaScript thực thi các câu lệnh mà bạn định sẵn. Lệnh for...in có dạng như sau:

for (variable in object)
  statement

Ví dụ

Hàm sau nhận vào tham số là một object và tên của object đó. Sau đó nó lặp qua mọi thuộc tính của object, với mỗi lần lặp nó trả về một chuỗi ký tự liệt kê các tên thuộc tính và giá trị của chúng.

function dump_props(obj, obj_name) {
  var result = '';
  for (var i in obj) {
    result += obj_name + '.' + i + ' = ' + obj[i] + '<br>';
  }
  result += '<hr>';
  return result;
}

Chẳng hạn với object car có thuộc tính là makemodel, result sẽ là:

car.make = Ford
car.model = Mustang

Mảng

Mặc dù có thể dùng cách này để lặp qua từng phần tử của mảng Array, lệnh for...in sẽ trả về tên của thuộc tính mà người dùng tự định nghĩa kèm theo chỉ mục của mảng.

Bởi vậy, tốt hơn hết hãy sử dụng cách truyền thống là dùng vòng lặp for với chỉ mục dạng số để lặp qua từng phần tử của mảng, bởi lệnh for...in còn lặp qua cả thuộc tính mà người dùng tự định nghĩa (chẳng hạn như khi thêm một thuộc tính vào mảng, sẽ có ví dụ ở phía dưới).

for...of statement

Lệnh for...of tạo ra một vòng lặp, áp dụng với các đối tượng khả duyệt (iterable object) (bao gồm ArrayMap, Set, arguments object, v.v...), và gọi lên một hook lặp tùy chỉnh (custom iteration hook) với các câu lệnh sẽ được thực thi cho giá trị của từng thuộc tính.

for (variable of object)
  statement

Ví dụ sau chỉ ra sự khác biệt giữa hai vòng lặp: for...offor...in. Trong khi for...in lặp qua tên thuộc tính, for...of lặp qua giá trị thuộc tính:

var arr = [3, 5, 7];
arr.foo = 'hello';

for (var i in arr) {
   console.log(i); // logs "0", "1", "2", "foo"
}

for (var i of arr) {
   console.log(i); // logs 3, 5, 7
}