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;
參考資料
【1】 ES5/語句 https://www.w3.org/html/ig/zh/wiki/ES5/statements
【2】 阮一峰Javascript標準參考教程——語法概述 http://javascript.ruanyifeng.com/grammar/basic.html#toc12
【3】 W3School-Javascript高級教程——語句 http://www.w3school.com.cn/js/pro_js_statements_if.asp
【4】《javascript權威指南(第6版)》第5章 語句
【5】《javascript高級程序設計(第3版)》第3章 基本概念
【6】《javascript DOM編程藝術(第2版)》第2章 Javascript語法
【7】《javascript語言精粹》 第2章 語法
javascript語句