javascript語句——條件語句、迴圈語句和跳轉語句
前面的話
預設情況下,javascript直譯器依照語句的編寫順序依次執行。而javascript中的很多語句可以改變語句的預設執行順序。本文介紹可以改變語句預設執行順序的條件語句、迴圈語句和跳轉語句
條件語句
指令碼的威力體現在它們可以根據人們給出的各種條件做出決策,javascript使用條件語句來做判斷
條件語句(conditianal statement)通過判斷表示式的值來決定執行還是跳過某些語句,包括if語句和switch語句
【if語句】
最常見的條件語句是if語句。if語句的條件必須放在if後面的圓括號內,條件的求值結果永遠是一個布林值,即只能是true或false。花括號中的語句,不管它們有多少條,只有在給定條件的求值結果是true的情況下才會執行
if(expression){ statements; }
[注意]if語句中,括住expression的圓括號在語法上是必需的
實際上,if語句中的花括號不是必不可少的。如果if語句中的花括號部分只包含一條語句,可以不使用花括號。但因為花括號可以提高指令碼的可讀性,所以在if語句中總是使用花括號是個好習慣
//可行,但不推薦 if(1>2)alert('1'); //推薦寫法 if(1>2){ alert('1'); }
if語句根據表示式的值改變程式流程。當expression的值為true時執行跟在其後的程式碼塊,當expression的值為false時,執行else的邏輯
if(expression) statement1 else statement2
當在if/else語句中巢狀使用if語句時,必須注意確保else語句匹配正確的if語句
//錯誤暗示
if( i == j) if(j == k) console.log('i == k'); else console.log('i != j');
javascript中的if/else匹配規則是:else總是和就近的if語句匹配
//實際解釋 if( i == j){ if(j == k) console.log('i == k'); else console.log('i != j');//錯誤 }
//使用花括號 if(i == j){ if(j == k){ console.log('i == k'); } }else{ console.log('i != j'); }
當代碼有多條分支時,需要使用else if語句。else if語句並不是真正的javascript語句,它是多條if/else語句連在一起時的一種慣用寫法
if(n == 1){ //程式碼1 }else if(n == 2){ //程式碼2 }else if(n == 3){ //程式碼3 }else{ //其他程式碼 }
可以用if語句的巢狀形式來完成在語法上等價的程式碼,但沒有else if語句清晰
if(n == 1){ //程式碼1 }else{ if(n == 2){ //程式碼2 }else{ if(n == 3){ //程式碼3 }else{ //其他程式碼 } } }
【switch語句】
當所有的分支都依賴於同一個表示式的值時,else if並不是最佳解決方案。在這種情況下,重複計算多條if語句中的條件表示式是非常浪費的做法,而switch語句正適合處理這種情況
switch語句執行一個多路分支,首先計算expression的值,然後查詢case子句的表示式是否和expression的值相同。如果找到匹配的case,那麼將會執行這個case對應的程式碼塊。如果找不到匹配的case,那麼將會執行default標籤中的程式碼塊。如果沒有default標籤,switch語句將跳過它的所有程式碼塊
switch (expression) case value1: statement1; break; case value2: statement2; break; case value3: statement3; break; default: statement4;
if(n == 1){ //程式碼1 }else if(n == 2){ //程式碼2 }else if(n == 3){ //程式碼3 }else{ //其他程式碼 }
//等價於 switch(n){ case 1: //程式碼1 break; case 2: //程式碼2 break; case 3: //程式碼3 break; default: //程式碼4 }
每一個case語句塊的結尾處都使用了關鍵字break。break語句可以使直譯器跳出switch語句或迴圈語句
在switch語句中,case只是指明瞭要執行的程式碼起點,但並沒有指明終點。如果沒有break語句,那麼switch語句就會從與expression的值相匹配的case標籤處的程式碼塊開始執行,依次執行後續的語句,一直到整個switch程式碼塊的結尾
//如果沒有break語句,若switch語句匹配1,則不僅執行程式碼1,也會執行程式碼2和程式碼3 switch(n){ case 1: //程式碼1 case 2: //程式碼2 default: //程式碼3 }
如果確實需要混合幾種情形,要在程式碼中添加註釋,說明是有意省略了break關鍵字
switch(i){ //合併兩種情形 case 25: case 35: console.log('25 or 35'); break; case 45: console.log('45'); break; default: console.log('other'); }
如果在函式中使用switch語句,可以使用return來代替break,return和break都用於終止switch語句,也會防止一個case語句塊執行完後繼續執行下一個case語句塊
//函式中使用switch語句 function convert(x){ switch(typeof x){ case 'number': return x.toString(16); case 'string': return '"' + x + '"'; default: return String(x); } }
雖然ECMAScript中的switch語句借鑑自其他語言,但這個語句也有自己的特色。可以在switch語句中使用任何資料型別(在很多其他語言中只能使用數值),而且每個case的值不一定是常量,可以是變數或表示式
var num = 25; switch(true){ case num < 0: console.log('less than 0.'); break; case num >=0 && num <=10: console.log('between 0 and 10.'); break; case num >10 && num <=20: console.log('between 10 and 20.'); break; default: console.log('more than 20.'); }
使用switch語句時,要注意以下幾點:
【1】由於每次執行switch語句時,並不是所有的case表示式都能執行到,因此,應該避免使用帶有副作用的case表示式,比如函式呼叫表示式和賦值表示式,最安全的做法是在case表示式中使用常量表達式
【2】default標籤一般都出現在switch的末尾,位於所有case標籤之後,當然這是最合理也是最常用的寫法,實際上,default標籤可以放置在switch語句內的任何地方
【3】switch語句中,對每個case的匹配操作實際上是'==='恆等運算子比較,而不是'=='相等運算子比較,因此,表示式和case的匹配並不會做任何型別轉換
//由於1並不會轉換為'1',所以結果是3 var n = 1; switch(n){ case '1': console.log(1); break; case 2: console.log(2); break; default: console.log(3); }
迴圈語句
條件語句把javascript中的程式碼變成一條條的分支路徑,而迴圈語句(looping statement)就是程式路徑的一個迴路,可以讓一部分程式碼重複執行
javascript有4種迴圈語句:while、do/while、for、for/in,它們的工作原理幾乎一樣:只要給定條件仍能得到滿足,包含在迴圈語句裡的程式碼就將重複地執行下去。一旦給定條件的求值結果不再是true,迴圈也就到此為止。其中最常用的迴圈就是對陣列元素的遍歷
【while語句】
while語句屬於前測試迴圈語句,也就是說,在迴圈體內的程式碼被執行之前,就會對出口條件求值
while(expression){ statement }
當表示式expression是真值時則迴圈執行statement,直到expression的值為假值為止;如果是假值,那麼程式將跳過迴圈
[注意]使用while(true)會建立一個死迴圈
大多數迴圈都會有一個像count這樣的計數器變數。儘管迴圈計數器常用i、j、k這樣的變數名,但如果想要讓程式碼可讀性更強,就應當使用更具語義的變數名
var count = 0; while(count < 10){ console.log(count); count++; }
【do while語句】
do while語句是後測試迴圈,即退出條件在執行迴圈內部的程式碼之後計算。這意味著在計算表示式之前,至少會執行迴圈主體一次
do{ statement }while(expression);
do/while迴圈和普通的while迴圈有兩點語法方面不同:
【1】do/while迴圈要求必須使用關鍵字do來標識迴圈的開始,用while來標識迴圈的結尾並進入迴圈條件判斷
【2】do/while迴圈用分號結尾。如果while迴圈體使用花括號括起來,則while迴圈也不用使用分號做結尾
function printArray(a){ var len = a.length,i=0; if(len == 0){ console.log('empty'); }else{ do{ console.log(a[i]); }while(++i<len); } }
【for語句】
for語句提供了一種比while語句更加方便的迴圈控制結構,用for迴圈來重複執行一些程式碼的好處是迴圈控制結構更加清晰
大部分的迴圈都具有特定的計數器變數,計數器的三個關鍵操作是初始化、檢測和更新。for語句將這三步明確宣告為迴圈語法的一部分,各自使用一個表示式來表示
for(initialize;test;increment){ statement; }
//等價於: initialize; while(test){ statement increment; }
[注意]使用continue語句時,while迴圈和for迴圈並不等價
initialize、test和increment三個表示式之間用分號分隔,它們分別負責初始化操作、迴圈條件判斷和計數器變數的更新。將它們放在迴圈的第一行會更容易理解for迴圈正在做什麼,而且也可以防止忘記初始化或者遞增計數器變數
initialize表示式只在迴圈開始之前執行一次;每次迴圈執行之前會執行test表示式,並判斷表示式的結果來決定是否執行迴圈體,如果test計算結果為真值,則執行迴圈體中的statement,最後,執行increment表示式
在for迴圈的變數初始化表示式中,也可以不使用var關鍵字,該變數的初始化可以在外部執行
var count = 10; var i; for(i = 0; i < count; i++){ console.log(i); }
由於ECMAScript中不存在塊級作用域,因此在迴圈內部定義的變數也可以在外部訪問到
var count = 10; for(var i = 0; i < count; i++){ console.log(i); } console.log(i);//10
for迴圈常見用途是對某個數組裡的全體元素進行遍歷處理
var beatles = Array('John','Paul','George','Ringo'); for(var count = 0; count < beatles.length; count++){ alert(beatles[count]); }
如果迴圈中的一次迭代會改變多個變數,則需要用到逗號運算子,它將初始化表示式和自增表示式合併入一個表示式中以用於for迴圈
var i,j; for(i = 0,j =10; i<10; i++,j--) sum+= i*j;
程式碼中的迴圈變數除了是數字外,也可以是其他型別
可以使用for迴圈來遍歷連結串列資料結構,並返回連結串列中的最後一個物件(也就是第一個不包含next屬性的物件)
function tail(o){ for(;o.next;o = o.next)/*empty*/; return o; }
for迴圈中的initialize、test和increment這三個表示式中的任何一個都可以忽略,但是兩個分號必不可少。如果省略test表示式,那麼這將是一個死迴圈。同樣,和while(true)類似,死迴圈的另外一種寫法是
for(;;)
【for in語句】
for/in語句也使用for關鍵字,但它和常規的for迴圈是完全不同的一類迴圈
for(variable in object){ statement }
variable通常是一個變數名,也可以是一個可以產生左值的表示式或一個通過var語句宣告的變數,總之必須是一個適用於賦值表示式左側的值。object是一個表示式,這個表示式的計算結果是一個物件。同樣,statement是一個語句或語句塊,它構成了迴圈的主體
for/in迴圈可以用來更方便地遍歷物件屬性成員
for(var p in o){ console.log(o[p]); }
在執行for/in語句的過程中,javascript直譯器首先計算object表示式。如果表示式為null或undefined。javascript直譯器將會跳過迴圈並執行後續的程式碼。如果表示式等於一個原始值,這個原始值將會轉換為與之對應的包裝物件(wrapper object)。否則,expression本身已經是物件了。javascript會依次列舉物件的屬性來執行迴圈。然而,在每次迴圈前,javascript都會先計算variable表示式的值,並將屬性名(一個字串)賦值給它
[注意]只要for/in迴圈中variable的值可以當做賦值表示式的左值,它可以是任意表達式,每次迴圈都會計算這個表示式,也就是說每次迴圈它計算的值有可能不同
var obj = { x: 1, y: 2 }; var props = []; var i = 0; for (props[i++] in obj); props // ['x', 'y']
javascript陣列不過是一種特殊的物件,因此,for/in迴圈可以像列舉物件屬性一樣列舉陣列索引
var o = {a: 1, b: 2, c: 3}; for (var i in o) { console.log(o[i]); } // 1 // 2 // 3
[注意]for/in迴圈並不會遍歷物件的所有屬性,只有可列舉(enumerable)的屬性才會遍歷到
ECMAScript規範並沒有指定for/in迴圈按照何種順序來列舉物件屬性。但實際上,主流瀏覽器廠商的javascript實現是按照屬性定義的先後順序來列舉簡單物件的屬性,先定義的屬性先列舉。如果使用物件直接量的形式建立物件,則將按照直接量中屬性的出現順序列舉。有一些網站和javascript庫是依賴於這種列舉順序的,瀏覽器廠商不大可能會修改這個順序
跳轉語句
跳轉語句(jump statement)可以讓直譯器跳轉到程式的其他部分繼續執行,包括break、continue和return語句
【label語句】
介紹跳轉語句不得不提到標籤(label)語句,通過給語句定義標籤,就可以在程式的任何地方通過標籤名引用這條語句
標籤語句通常與break語句和continue語句配合使用,跳出特定的迴圈
identifier: statement
[注意]用做標籤的identifier必須是一個合法的javascript識別符號,而不能是一個保留字
mainloop: while(token != null){ //Todo continue mainloop; }
標籤的名稱空間和變數或函式的名稱空間是不同的,因此可以使用同一個識別符號作為語句標籤和作為變數名或函式名
語句標籤只有在它所起作用的語句(當然也可以在它的子句中)內是有定義的。一個語句標籤不能和它內部的語句標籤重名,但在兩個程式碼段不相互巢狀的情況下,是可以出現同名的語句標籤的。帶有標籤的語句還可以帶有標籤,也就是說,任何語句可以有很多個標籤
【break語句】
單獨使用break語句的作用是立即退出最內層的迴圈或switch語句
break;
for(var i = 0; i < a.length; i++){ if(a[i] == target) break; }
break語句只有出現在迴圈語句或switch語句中才合法,出現在其他語句中會報錯
//報錯 if(true){ break; } //報錯 function test(){ var i = 0; break; } test();
當希望通過break來跳出非就近的迴圈體或switch語句時,就會用到帶標籤的break語句
break labelname;
當break和標籤一塊使用時,程式將跳轉到這個標籤所標識的語句塊的結束,或者直接終止這個閉合語句塊的執行。當沒有任何閉合語句塊指定了break所用的標籤,這時會產生一個語法錯誤
top: for (var i = 0; i < 3; i++){ for (var j = 0; j < 3; j++){ if (i === 1 && j === 1) break top; console.log( i, j); } }
[注意]不管break語句帶不帶標籤,它的控制權都無法越過函式的邊界。比如,對於一條帶標籤的函式定義語句來說,不能從函式內部通過這個標籤來跳轉到函式外部
【continue語句】
continue語句和break語句非常類似,但它不是退出迴圈,而是轉而執行下一次迴圈
continue; continue labelname;
不管continue語句帶不帶標籤,它只能在迴圈體內使用。在其他地方使用將會報語法錯誤
當執行到continue語句時,當前的迴圈邏輯就終止了,隨即執行下一次迴圈。但在不同型別的迴圈中,continue行為也有所不同:
【1】while迴圈中,迴圈開始處指定的exression會重複檢測,如果檢測結果為true,則迴圈體會從頭開始執行
【2】do while迴圈中,程式執行直接跳到迴圈結尾處,這時會重新判斷迴圈條件,之後才會繼續下一次迴圈
【3】for迴圈中,首先計算自增表示式,然後再次檢測test表示式,用以判斷是否執行迴圈體
【4】在for/in迴圈中,迴圈開始遍歷下一個屬性名,這個屬性名賦給了指定的變數
[注意]continue語句在while和for迴圈中的區別:while迴圈直接進入下一輪的迴圈條件判斷,但for迴圈首先計算其increment表示式,然後判斷迴圈條件
//1 3 for(i = 0; i < 5; i++){ if(i % 2 === 0)continue; console.log(i); }
//1 3 5 var i = 0; while(i<5){ i++; if(i % 2 === 0)continue; console.log(i); }
由於continue在這兩種迴圈中的行為表現不同,因此使用while迴圈不可能完美地模擬等價的for迴圈
和break語句類似,帶標籤的continue語句可以用在巢狀的迴圈中,用以跳出多層巢狀的迴圈體邏輯
top: for (var i = 0; i < 3; i++){ for (var j = 0; j < 3; j++){ if (i === 1 && j === 1) continue top; console.log('i=' + i + ', j=' + j); } }
【return語句】
函式呼叫是一種表示式,而所有表示式都有值。函式中的return語句就是指定函式呼叫後的返回值
return expression;
return語句只能出現在函式體內,如果不是會報語法錯誤。當執行到return語句時,函式終止執行,並返回expression的值給呼叫程式
function square(x) { return x*x }; square(2);
如果沒有return語句,則函式呼叫僅僅依次執行函式體內的每一條語句直到函式結束,最後返回呼叫程式。這種情況下,呼叫表示式的結果是undefined。return語句經常作為函式內的最後一條語句出現,但並不是說一定要放在函式最後,即使在執行return語句的時候還有很多後續程式碼沒有執行到,這時函式也還是會返回呼叫程式
return語句可以單獨使用而不必帶有expression,這樣的話也會向呼叫程式返回undefined
function display_object(o){ if(!o) return; }
在javascript詞法結構中,已經提到過分號的用法。所有的跳轉語句包括break、continue、return等語句,都不可以在關鍵字和表示式之間換行,因為javascript會在換行處填補分號
//以及return語句舉例,break、continue語句也類似 return true;
javascript將其解析為:
return;true;
而程式碼的本意是:
return true;