2015年12月10日 星期四

JavaScript 迭代與循環

循環提供了一種快速和簡單的方法去重複的做一些事。JavaScript Guide的這個章節會介紹在JavaScript中存在哪些不同的迭代語句。

你可以把循環想成一種小算盤化的遊戲,告訴某人在一個方向上走X步,然後在另一個方向上走Y步;例如,「向東走5步」可以用一個循環來這樣表達:

var step;
for (step = 0; step < 5; step++) {
  // Runs 5 times, with values of step 0 through 4.
  console.log('Walking east one step');
}

循環有很多種類,但本質上它們都做的是同一件事:它們把一個動作重複了很多次(實際上重複的次數有可能為0)。各種循環機制提供了不同的方法去確定循環的開始和結束。不同情況下,某一種類型循環會比其它的循環用起來更簡單。

JavaScript中提供了這些循環語句:

    for statement
    do...while statement
    while statement
    label statement
    break statement
    continue statement
    for...in statement
    for...of statement

for 語句

一個for循環會一直重複執行,直到指定的循環條件為fasle。 JavaScript的for循環和Java與C的for循環是很相似的。一個for語句是這個樣子的:

for ([initialExpression]; [condition]; [incrementExpression])
  statement

當一個for循環執行的時候,會發生以下事件:

    如果有初始化表達式initialExpression,它將被執行。這個表達式通常會初始化一個或多個循環計數器,但語法上是允許一個任意複雜度的表達式的。這個表達式也可以聲明變數。
    計算condition表達式的值。如果condition的值是true,循環中的statement會被執行。如果condition的值是false,for循環終止。如果condition表達式整個都被省略掉了,condition的值會被認為是true。
    循環中的statement被執行。如果需要執行多條語句,可以使用塊 ({ ... })來包裹這些語句。
    如果有更新表達式incrementExpression,執行它,然後流程回到步驟2。

例子

下面的函式包含一個含有for循環去計算一個滑動列表中被選中項目的個數(一個 <select> 元素允許選擇多項)。for循環聲明了變數i並將它的初始值設為0。它檢查i比 <select> 元素中的選項數量少,執行了隨後的if語句,然後在每次完成循環以後i的值增加1。

<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>爵士</option>
      <option>布魯斯</option>
      <option>新紀元</option>
      <option>古典</option>
      <option>戲劇</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="選擇了多少個選項?" /></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('選擇選項的數量是: ' + howMany(document.selectForm.musicTypes))
});
</script>

do...while 語句

do...while 語句一直重複直到指定的條件求值得到假(false)。 一個 do...while 語句看起來像這樣:

do
  statement
while (condition);

statement 在檢查條件之間會執行一次。要執行多條語句(語句塊),要使用塊語句 ({ ... }) 包括起來。 如果 condition 為真(true),statement 將再次執行。 在每個執行的結尾會進行條件的檢查。當 condition 為假(false),執行會停止並且把控制權交回給 do...while 後面的語句。
例子

在下面的例子中, 這個 do 循環將至少重複一次並且一直重複直到 i 不再小於 5。

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

while 語句

一個 while 語句只要指定的條件求值為真(true)就會一直執行它的語句塊。一個 while 語句看起來像這樣:

while (condition)
  statement

如果這個條件變為假,循環裡的 statement 將會停止執行並把控制權交回給 while 語句後面的程式碼。

條件檢測會在每次 statement 執行之前發生。如果條件返回為真, statement 會被執行並緊接著再次測試條件。如果條件返回為假,執行將停止並把控制權交回給 while 後面的語句。

要執行多條語句(語句塊),要使用塊語句 ({ ... }) 包括起來。
例子 1

下面的 while 循環只要 n 小於 3就會一直執行:

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

在每次循環裡, n 會增加1並被加到 x 上。所以, x 和 n 的變化是:

    第一次完成後: n = 1 和 x = 1
    第二次完成後: n = 2 和 x = 3
    第三次完成後: n = 3 和 x = 6

在三次完成後, 條件 n < 3 結果不再為真,所以循環終止了。
例子 2

避免無窮循環(無限循環)。保證循環的條件結果最終會變成假;否則,循環永遠不會停止。下面這個 while 循環會永遠執行因為條件永遠不會變成假:

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

label 語句

一個 label 提供了一個可以讓你引用到您程式別的位置的標識符。例如,你可以用 label 標識一個循環, 然後使用 break 或者 continue 來指出程式是否該停止循環還是繼續循環。

label 語句的語法看起來像這樣:

label :
   statement

label 的值可以是任何的非保留字的 JavaScript 標識符, statement 可以是任意你想要標識的語句(塊)。
例子

在這個例子裡,標記 markLoop 標識了一個 while 循環。

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

break 語句

使用 break 語句來終止循環,switch, 或者是鏈接到 label 語句。

    當你使用不帶 label 的 break 時, 它會立即終止當前所在的 while,do-while,for,或者 switch 並把控制權交回這些結構後面的語句。
    當你使用帶 label 的 break 時,它會終止指定的標記(label)了的語句。

break 語句的語法看起來像這樣:

    break;
    break label;

第一種形式的語法終止當前所在的循環或 switch; 第二種形式的語法終止指定的 label 語句。
例子 1

下面的例子循環陣列裡的元素直到找到一個值是等於 theValue 的:

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

例子 2: 終止一個 label

var x = 0;
var z = 0
labelCancelLoops: while (true) {
  console.log("外部循環: " + x);
  x += 1;
  z = 1;
  while (true) {
    console.log("內部循環: " + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}

continue 語句

這個 continue 語句可以用來重新開始一個 while, do-while, for, 或者 label 語句。

    當你使用不帶 label 的 continue 時, 它終止當前 while,do-while,或者 for 語句到結尾的這次的循環並且繼續執行下一次循環。
    當你使用帶 label 的 continue 時, 它會套用被 label 標識的循環語句。

continue 的語法看起來像這樣:

    continue;
    continue label;

例子 1

下面的例子展示了帶有一個當 i 等於 3的 continue 語句的循環。 於是, n 取到的值是 1, 3, 7, 12。

var i = 0;
var n = 0;
while (i < 5) {
  i++;
  if (i == 3) {
    continue;
  }
  n += i;
}

例子 2

一個被標籤為checkiandj 的語句包含了一個標籤為checkj 的語句。如果遇到continue語句,程式會結束當前chechj的迭代並開始下一輪的迭代。每次遇到continue語句,checkj 語句會一直重複執行知道checkj語句的條件為false。. 當返回false後,checkiandj的剩餘語句將會執行,checkiandj會一直執行指導checkiandj的條件為false。當checkiandj的返回值為false時,將會執行checkiandj 的下面的語句。

如果 continue 有一個標記 checkiandj, 程式將會從 checkiandj 語句塊的頂部繼續執行。

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);
  }

for...in 語句

這個 for...in 語句循環一個指定的變數來循環一個物件所有可枚舉的屬性。JavaScript 會為每一個不同的屬性執行指定的語句。

for (variable in object) {
  statements
}

例子

下面的函式通過它的參數得到一個物件和這個物件的名字。然後循環這個物件的所有屬性並且返回一個列出屬性名和該屬性值的字串。

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

對於一個物件擁有 make 和 model 屬性的 car 物件來說,執行結果是:

car.make = Ford
car.model = Mustang

陣列

雖然用for...in來迭代Array元素很誘人,但是它返回的除了數字索引外還有可能是你自定義的屬性名字。因此還是用帶有數字索引的傳統的for 循環來迭代一個陣列比較好,因為如果你想改變陣列物件,比如增加屬性或者方法,for...in 語句迭代的是 自定義的屬性而不是陣列的元素。
for...of statement

該新特性屬於 ECMAScript 2015(ES6)規範,在使用時請注意瀏覽器兼容性。

for...of語句在可迭代的物件上新增了一個循環(包括Array, Map, Set, 參數物件( arguments) 等等),對值的每一個獨特的屬性呼叫一個將被執行的自定義的和語句掛鉤的迭代。

for (variable of object) {
  statement
}

下面的這個例子展示了 for...of 和 for...in 兩種循環語句質檢的區別。與 for...in 循環迭代的結果是陣列元素的下標不同的是, for...of 迭代的結果是元素的值:

let arr = [3, 5, 7];
arr.foo = "hello";

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

for (let i of arr) {
   console.log(i); // logs "3", "5", "7" // 注意這裡沒有 hello
}

沒有留言:

張貼留言