摘要:每個候選項都是大括號中的語句序列。短路運算符有一個很重要的功能它們并不真的需要布爾值操作數,注意換句話說,不會將數字轉換為布爾值。練習對表達式求值。首先對求值,轉換為繼續對右邊表達式求值,為,造成短路,不對進行計算,返回對表達式求值。
4.1 聲明語句
聲明語句也叫變量語句,這種語句會創建新變量。可以在聲明變量時給出初始值,如果沒有明確給出,變量的值就是undefined。
var count = 0; var dogs = ["Sparky","Spot","Spike"]; var response; var melta = {latitude:35.8,longitude:14.6}; var finished = false; var winner = null;
這段腳本明確給出了幾個變量的初始值,事實上,除response之外都有了初始值。這時候,變量response的值就是undefined。
可以在一個語句中聲明多個變量,可以給部分變量賦初始值。
var start = 0,finish = 72,input,output = [];
這個語句會將input初始化為undefined值。
4.2 表達式語句表達式語句對表達式求值但,忽略得到的值。這聽起來有點傻,因為可以編寫出類似于下面的無用腳本;
2+2; // 計算2+2,僅此而已 "hello"; // 也有效,但完全沒用 Math.sqrt(100); // 完成計算,也僅此而已 "a".toLowerCase(); // 得到一個新字符串,但忽略它
但是可以借助表達式的副作用來創建有用的語句,所謂"副作用",就是一些操作,可以生成可見結果、改變變量或對象屬性中保存的值,或者修改對象的結構等。之前的delete運算符與alert和賦值一樣,都會產生副作用
var x = 2; // 聲明x,初始化為2 alert(x); // 顯示2 alert(10*x); // 顯示20 var y; // 聲明y,但不明確賦值 alert(y); // 顯示undefined
記住,變量聲明和賦值就是簡單地把一個值放到一個變量里,僅此而已。除此之外,沒有任何其他后果,比如不會設定變量與變量之間的關系。在前面的腳本中,聲明var z = y不會讓z和y成為同一個變量,也不會要求它們在之后始保存相同的值。這個語句意思僅僅是"新建一個變量z,讓它的初始值等于y的當前值"。之后再給y重新賦一個新值時,z的值不會變。
4.3 條件執行JavaScript提供了一些語句和幾個運算符,可以讓我們編寫的腳本在特定條件下執行一操作,在不同條件下則執行另一組操作。
4.3.1 if語句if語句會根據你提供的條件,最多執行許多候選操作中的一種。這個語句的最一般形式是有一個if部分,0個或多個else if部分,還可根據需要帶有else部分。每個候選項都是大括號中的語句序列。這些條件會自上而下一次評估。只要一個條件為真,就會執行它對應的候選操作,然后結束整個if語句。
var score = Math.round(Math.random()*100); if (score>=90) { grade = "A"; } else if (score>=80) { grade = "B"; } else if (score>=70) { grade = "C"; } else if (score>=60) { grade = "D"; } else { grade = "F"; }; alert(score+" is a "+grade)
下圖用一種UMKL(Unified Modeling Language,統一建模語言)活動圖解讀了上述語句。UML是一種非常流行的軟件系統可視化語言。這張圖傳達的事實就是:會逐個檢測每個條件,一旦發現一個條件成立,就不再進行測試。
編寫一個if語句,當變量s沒有包含長度為16的字符串時,提示一條消息(注意,這個語句沒有else if或else部分)
var s = prompt("Enter a something"); if (s.length!==16) { alert("string length isn"t 16") };
編寫一個if語句,檢查變量x的值,(1)如果x>0,則將它加到數組變量values的末尾;(2)如果x<0,則將它加到values的前面;(3)如果x===0,則遞增變量zeroCount;(4)柔則,什么也不做。
var values = []; var zeroCount = 0; var x = Number(prompt("Enter a number")); if (x>0) { values.push(x); } else if (x<0) { values.unshift(x); } else if (x===0) { zeroCount++; };4.3.2 ?:條件表達式
有時你可能更喜歡使用條件表達式,而不是if語句。條件表達式就是"x?y:z",當x為真時,表達式的結果為y,當x為假時,表達式的結果為z。
if (latitude>=0) { hemisphere = "north"; } else { hemisphere = "south"; } // 寫為: hemisphere = (latitude>=0) ? "north" : "south";
條件表達式有時會用在構建字符串的表達式中,如下(這里的present假定是一個保存布爾值的變量:)
var notice = "She is "+(present? "" : "n"t")+" here.";4.3.3 switch語句
另一種條件語句 —— switch語句,將一個值一系列case進行比較,知道找出一個與它的值相等(===)的case,然后從該處開始執行語句。可以根據需要包含一個default case,它會匹配所有值。
switch (direction.toLowerCase()){ case "north" : row -= 1; break; case "south" : row += 1; break; case "east" : column += 1; break; case "west" : column -= 1; break; default: alert("Illegal direction"); }
break語句會終止整個switch語句。每種case都以一個break結束,這是一種很好的做法;否則,執行會"直落"到下一個case(不會再與剩下的case表達式進行比較)。例如,上面的腳本我們省略了break,而且方向是"east",會發生什么情況呢?column中的值會被遞增,然后遞減,然后彈出一個提示框,告訴你"east"不是一個合法方向。
有時,"直落"正是我們想要的。假定在一群參加競賽的人中,每個人都獲得一個證書,但達到一級獎勵的人還會得到一個背包,達到二級獎勵的人還會得到一次滑雪度假,而達到三級獎勵的人還會獎勵一倆汽車。這種情況就可以使用沒有break語句的編碼。
/* * 這個腳本會為一個給定獎勵級別生別一組獎勵 * 每個獎勵級別的參賽者會得到該級獎勵和所有 * 低級獎勵。它使用了一種很丑陋的switch語句 * 形式,其中的各個case之間沒有相互距離。 */ var level = +prompt("Enter your prize level,1-3"); var prizes = []; switch (level){ case 3 : prizes.push("car"); case 2 : prizes.push("ski vacation"); case 1 : prizes.push("backpack"); default: prizes.push("certificate"); } alert(prizes);
一個具有直落效果的switch語句的活動圖
像這樣的計算很少會在現實中發生所以一般來說,還是避免使用那些不以break(或其他某一中斷語句)結束的case,以防與switch更常見的形式混淆。事實上,之前介紹的JSLint代碼質量檢查工具就是將"直落"看作錯誤!我們總能為直落switch找出替代方法。
4.3.4 用查詢避免條件代碼來看一種情景,至少對于許多程序員新手來說,這種情景似乎是需要使用if語句,?:運算符或者switch語句。假定有一個變量state,保存德國一個州名,我們希望將這個州的首府名賦值給變量capital,如果不能識別州名,則為其賦值undefined。
// 丑陋的代碼 —— 不要使用這一代碼 if (state === "Baden-Wurttemberg") { capital = "Strttgart"; } else if (state = "Bayern") { capital = "Munchen"; } else if (state = "Berlin") { capital = "Berlin"; } else if (state = "Potsdam") { capital = "Potsdam"; } else if (state = "Bremen") { capital = "Bremen"; } else if (state = "Hamburg") { capital = "Hessen"; } else { capital = undefined; }
此代碼對每個州都重復兩段內容:state === 和 capital = 。switch語句可以消除前者,但必須為每個州都增加一個break —— 沒有實質性的改進。
// 丑陋的代碼 —— 不要使用這一代碼 switch (state){ case "Baden-Wurttemberg" : capital = "Stuttgart"; break; case "Bayern" : capital = "Munchen"; break; case "Berlin" : capital = "Berlin"; break; case "Brandenburg" : capital = "Potsdam"; break; case "Bremen" : capital = "Bremen"; break; case "Hamburg" : capital = "Hambrug"; break; case "Hessen" : capital = "Wiesbaden" break; default:capital = undefined; }
我們將在第三次嘗試中使用的條件表達式可以消除賦值的重復作用,但無法消除相等檢測中的重復。
capital = (state === "Baden-Wurttemberg") ? "Stuttgart" : (state === "Bayern") ? "Munchen" : (state === "Berlin") ? "Berlin" : (state === "Brandenburg") ? "Potsdam" : (state === "Bremen") ? "Bremen" : (state === "Hamburg") ? "Hamburg" : (state === "Hessen") ? "Wiesbaden" : undefined;
我們不能將所有重復片段排除在外,這一定意味著存在一種更好的方法來查找首府,事實上也的確存在。如果剔除了所有關于測試與賦值的內容,還剩下什么呢?就是州和首府!我們并不需要什么精確的計算來將州和首府關聯在一起,可以在數據而非代碼中定義這種關系。我們所需要的就是一個簡單對象。
var CAPITALS = { "Baden-Wurttemberg" : "Stuttgart", "Bayern" : "Munchen", "Brandenburg" : "Potsdam", "Bremen" : "Bremen", "Hamburg" : "Hamburg", "Hessen" : "Wiesbaden" };
現在,只需寫出:
capital = CAPITALS[state];
就能獲得變量state中所包含州的首府。可以認為這一代碼是在一個由州及首府組成的表格中查詢首府。在這樣使用一個對象時,就說它是一個查詢表、一個詞典、一個映射、一個關聯數組、或一個數列。查詢表通常是將鍵映射到值(key = value)。這里的州是鍵,首府是值。在適用時,查詢代碼要優于條件代碼,原因有如下幾個。
沒有冗余的代碼片段,是腳本更短、更易于理解。
州和首府的顯示位置緊挨在一起,更容易"看出"它們之間的關聯。
代碼(查詢)和數據(查詢表)在本質上是不同的東西,不應當混放在一起。
JavaScript引擎執行查詢的速度要遠快與條件代碼。
還有一個例子,我們使用詞典來關聯手機鍵盤上的數字和字母。
為了將帶有字母的電話號碼轉換為號碼,我們可以使用:
var LETTER_TO_NUMBER = { A:2, B:2, C:2, D:3, E:3, F:3, G:4, H:4, I:4, J:5, K:5, L:5, M:6, N:6, O:6, P:7, Q:7, R:7, S:7, T:8, U:8, V:8, W:9, X:9, Y:9, Z:9 };
比如LETTER TO NUMBER["C"] === 2。能不能在另一個方向上使用這個映射呢?單個數字可以映射為多個字母,所以我們將使用字符串:
var NUMBER_TO_LETTER = { 2:"ABC", 3:"DEF", 4:"GHI", 5:"JKL", 6:"MNO", 7:"PQRS", 8:"TUV", 9:"WXYZ" };
這里,我們說:“給定一個數字n,NUMBER TO LETTER[n]”的值是一個字符串,其中包含了與鍵盤上的n相關聯的所有字母(而且也只有這些字母)。”在后續介紹處理整個手機號碼腳本時間,將會用到這些詞典中的第一個。
練習
用你自己的語言,說明可以用查詢代替條件代碼結構的情景(為之前的分數與成績示例使用查詢策略)
var score = { 100:"A", 99:"A", 98:"A", 80:"B", 85:"B", 89:"B", }; alert(score[100])
情景:查詢電話歸屬地、郵編。元素周期表......
假定你注意到某一代碼中存在表達式CAPITALS[Saarland]。幾乎可以肯定它有什么錯誤?
可能存在語法錯誤,方括號運算符中如果不是變量,必須用引號括起
4.3.5 短路執行之前的布爾值一章中,如果x和y都是布爾值,則AND-ALSO和OR-ELSE運算符具有以下特性:
當且僅當x或y為真(或均為假)時,x || y為真
當且僅當x和y均為真時,x && y為真
if (t<0 && t>100) { /**執行某些操作**/ } // 與下面代碼的含義完全相同 if (t<0) { if (t>100) { /**執行某些操作**/ } }
如果第一部分的求值已經獲得了足夠多的信息,那就講第二部分的求值短路,也就是跳過。
我們現在明白AND-ALSO和OR-ELSE名字的緣由了。
x and also y就是說:若(if)x為真,則(then)(且僅在此時)去查看y是否也(also)為真。
x or else y 就是說:若(if)x為真,那很好;否則(else)必須去查看y是否為真。
第二部分根據條件決定是否求值。
短路運算符有一個很重要的功能:它們并不真的需要布爾值操作數,注意:alert(27 && 52); // 52 alert(0 && 52); // 0 alert(27 || 52); // 27 alert(0 || 52); // 52
換句話說,JavaScript不會將數字轉換為布爾值。
為計算x && y的值,JavaScript首先對x求值。如果x為假(false或可以轉換為false),則整個表達式得出x的值(不再計算y)。否則整個表達式得出y的值。
為計算x || y的值,JavaScript首先對x求值。如果x為真(true或可以轉換為true),則整個表達式得出x的值(不再計算y)。否則,整個表達式得出y的值。
利用這一行為,可以編寫一些小巧的代碼。假定有一個變量favoriteColor,我們知道,如果你有最喜歡的顏色,這個變量就包含了這種顏色的名稱,如果沒有最愛的顏色,那它的值就是undefined。如果有喜歡的顏色,就用這種顏色為汽車噴漆,如果沒有,則噴為黑色;
car.color = favoriteColor || "black";
這一行代碼是有效的,因為favoriteColor中包含了一個非空字符串,它為真,所以會為其指定car的顏色。如果favoriteColor為undefined(為假),則根據定義,||表達式的值就是它的第二個(右側)操作數,也就是黑色。
練習
對表達式 3 && "xyz" || null求值。
首先對3 && "xyz"求值,3轉換為true,繼續對右邊表達式求值,為true,造成 ||短路,不對null進行計算,返回"xyz"
對表達式3 && "" || null求值。
首先對3 && ""求值計算,空字符串轉換為false,而因||需要,繼續對右邊表達式求值,返回null
判斷正誤:對于變量x和y,x && y總是等同于x ? y : x
正確
首先對x求值,判斷是否短路,x為真,求值y,x為假,那只有x已求值
判斷正誤:對于變量x和y,x || y總是等同于x ? x : y、
正確
首先對x求值,判斷是否短路,x為真,短路并返回,x為假,繼續求值y
4.4 迭代上一節研究了不同情況下做不同事情的方法。現在來看一遍又一遍地重復同一事情的方法。
4.4.1 while和do-while語句JavaScript的while語句會在條件為真時重復執行代碼。下面的例子要求用戶輸入一個恰有五個字符的字符串,并一直詢問,知道用戶遵守指令為止。當用戶輸入不滿足要求時,腳本會計算嘗試次數(并重復提示)。只有在輸入了可接受的字符串時,while語句才會結束。
var numberOfTries = 1; while (prompt("Enter a 5-charater string").length !== 5){ numberOfTries += 1; } if (numberOfTries>1) { alert("Took you "+numberOfTries+" tries to get this right"); }
while語句的一般形式為:
while (test) { stmts }
首先執行第一個test,如果它為真,則執行循環體,重復整個語句。這就是說,條件測試出現在循環體之前,循環體可能一次也不會執行。而在do-while語句中國,循環體至少會執行一次。一般形式為:
do {stmts} while (test)
可以用do-while語句重寫以上腳本:
var numberOfTries = 0; do { var input = prompt("Enter a 5-character string"); numberOfTries++; } while (input.length!==5); if (numberOfTries>1) { alert("Took you "+numberOfTries+" tries to get this right"); }4.2.2 for語句
第二種循環 —— for語句,也就是在條件為真時一直循環,但它通常用于迭代遍歷一個固定的項目集,比如一個數值范圍、一個字符串中的字符、一個數組的索引、一個對象鏈,等等。這個語句一般形式是:
for (init ; test ; each) {stmts}
JavaScript引擎首先運行init代碼,然后,只要test為真,則運行循環體,然后運行each。init部分通常會聲明一或多個變量(但并非總是如此)。
// 顯示4,6,8,10,...,20為偶數 for (var Number=4;Number<=20;Number+=2) { alert(Number+" is even ") }
這里number被初始化為4,而且因為它小于等于20,所以將提示"4 is even",然后將number直接設置為6,接下來將顯示"6 is even",number變為8。最后顯示20,因為最后number將跳到22,條件為false。
在處理數組時會使用for語句。下面例子中,處理一個單詞列表,也就是字符串words[0]、words[1]、words[2],等等。如何"逐步遍歷"這個列表呢?可以創建一個變量i,它會在每次的迭代中遞增1.
// 顯示一個字符串,又每個數組項目的首個字母組成 var words = ["as","far","as","i","know"]; var result = ""; for (var i=0;i下面是一個另一個與數組有關的例子,它演示了一種常見模式,用于檢查每個數組元素,看它是否滿足某一條件。 // 顯示一個數組中的0的個數
var a = [7,3,0,0,9,-5,2,1,0,1,7]; var numberOfZeros = 0; for (var i=0,n=a.length;i注意這個示例還演示了如何在for循環的初始化部分聲明兩個變量。將n初始化為數組的長度,循環終止檢測變得簡單一點。
除了迭代數值范圍和數組元素之外,for語句還經常用于迭代一個字符串中的各個字符。如果字符是數字,則直接"將它傳送"給結果字符串
如果字符在A~Z之間,則查找對應的手機鍵盤數字,并傳送該數字。
如果字符是其他內容,則忽略它。
var LETTER_TO_NUMBER = {A:2,B:2,C:2,D:3,E:3,F:3,G:4,H:4,I:4,J:5,K:5,L:5,M:6,N:6,O:6,P:7,Q:7,R:7,S:7,T:8,U:8,V:8,W:9,X:9,Y:9,Z:9}; var phoneText = prompt("Enter a phone number (letters permitted)").toUpperCase(); var result = ""; for (var i=0;i練習在迭代另一類序列時也經常看到for循環:對象的鏈接結構
var scores = { score:29, next:{ score:99, next:{ score:47, next:null } } }; var total = 0; for (var p=scores;p!=null;p=p.next) { total += p.score; }; alert(total);變量p引用了對象scores,并將p.score屬性運算后賦值給變量total,并且每次執行完代碼塊后,p引用的對象的屬性變為p.next.score,下一次就是p.netx.next.score(scores.next.score→scores.next.next.score)下一個例子,演示嵌套循環。
var SIZE = 9 ; document.write("") for (var i=1;i<=SIZE;i++) { document.write("
")") for (var j=1;j<=SIZE;j++) { document.write(" "+i*j+" "); } document.write("") } document.write(" 編寫一個for語句,顯示10,然后顯示9,以此類推,知道最后顯示0。
for (var i=10;i>=0;i--) { console.log(i); }
var i = 10; while (i!==-1){ console.log(i); i--; }編寫一個小腳本,計算從1至20(含)的整數乘積值。使用for語句。
for (var i=1,n=1;i<21;i++) { n *= i; console.log(n); };4.4.3 for-in語句JavaScript包含一個迭代對象屬性名的語句。這門語言將這一操作稱為屬性名的枚舉。
var dog = { name:"Lisichka", breed:"G-SHEP", birthday:"2011-12-01" }; for (var p in dog) { alert(p) } // name breed birthday這個腳本將生成三條提示:一個給出name,一個給出breed,一個給出birthday。這些屬性的出現順序是任意的。試試另一個對象:
var colors = ["red","amber","green"]; for (var c in colors) { alert(c) } // 0 1 2枚舉是對屬性名進行的,不是針對值。變量colors引用的對象的屬性名是0、1、2和length。但是,在運行這一代碼時,你只會看到顯示了0,1,2。為什么沒有length?
其實,一個對象的每個屬性,除了擁有值以外,還有幾個特性(attribute)。// 特性 在為真時的含義 writable 屬性的值可以修改 enumerable 這個屬性將出現在屬性的for-in枚舉中 configurable 可以從其對象中刪除這個屬性,它的特性值是可以改變的湊巧,數組的length屬性的enumerable特性被設置為false。由前面的例子知道,它的writable特性為true。
練習
修改與狗有關的小腳本,提示以下內容:必須用for-in語句生成這些提示,并用object[property]符號提示屬性值。
var dog = {name:"Lisichka",breed:"G-SHEP",birthday:"2011-12-01"}; for (var i in dog) { document.write("The dog"s "+i+" is "+dog[i]+"4.5中斷
") } // dog[property]注意property是字符串類型,而不是變量。通常,語句都是按順序一次執行一條。條件語句和迭代語句會稍微偏離這種有時被稱作直線式代碼的形式,但這種偏離是語句結構本身決定的,很容易識別。但還四種語句的結構化不是這么強,他們對控制流的效果是中斷性的。有這些:
break,立即放棄執行當前正在執行的switch或迭代語句;
continue,立即放棄一個迭代語句中當前迭代的剩余部分;
return,立即放棄當前執行的函數;
thorw,將在后面介紹;
4.5.1 break和continuebreak語句將立即終止整個循環。在搜索數組(或對象鏈),這一語句特別有用,它會在你找到正在查找的內容之后立即終止搜索。
// 查找第一個偶數的索引位置 for (var i=0;icontinue語句立即開始循環的下一次迭代,而不在完成當前迭代。當循環中的一些(而非全部)迭代生成有用信息時,這一語句非常有用。continue語句就是說“嗨,這次迭代里沒有什么要做的事情了,我馬上要開始下一次迭代了”。
// 計算一個數組中所有正數值之和 var array = [-1,3,12,3,-23,-23,-2,0,1,-12]; if (array[i]<=0) { continue; // 跳過非正數 } sum += array[i]; } alert("Sumof positives is "+sum)這里接一個鏈接開源中國問題,是一個社友提的一個問題。剛好我在MDN里找到了一些文檔MDN-JS-break語句
那位社友的問題在于,在while循環中,當i=4時,就continue跳過了自增操作,按照MDN的說法,它將回到條件繼續執行,這便成了一個死循環。而在for循環就直接each更新表達式了。
接下來寫一個腳本:用戶輸入一個數字,判斷是否為質數。
var SmallLest = 2; var BigGest = 9E15; var n = prompt("輸入一個數字"); var condition = isNaN(n) || n % 1 !== 0 || n < SmallLest || n > BigGest; if (condition) { alert("只能判斷2~9e15之間的整數"); } else { var foundDivisor = "是"; // default,是質數 for (var k = 2,last = Math.sqrt(n);k <= last;k++) { if (n%k === 0) { // 2 ~ Math.sqrt(n)之間任何一個數可以整除n,則不是質數 foundDivisor = "不是"; break; } } } alert(n+" 是否為質數? "+" : "+foundDivisor);最后一個關于break和continue的示例要研究一個問題:如何中斷外層循環?一種方法是對循環進行標記,然后在break語句中提及這個標記。假定有一個對象紀錄了一組人選擇的彩票信息。
var picks = { Alice:[4,52,9,1,30,2], Boris:[14,9,3,6,22,40], Chi:[51,53,48,21,17,8], Dinh:[1,2,3,4,5,6], };另外,假定我們希望知道是否有人選擇了數字53。可以依次查看每個人選擇的數字,但只要找到53,就希望停止整個搜索,而不只是終止對當前人員選擇數字的掃描。
var picks = { Alice:[4,52,9,1,30,2], Boris:[14,9,3,6,22,40], Chi:[51,53,48,21,17,8], Dinh:[1,2,3,4,5,6], }; var found = false; Search:for (var person in picks) { var choices = picks[person]; for (var i=0;i練習
重寫計算數組例子,改為使用break語句。
// continue重寫:計算一個數組所有正數值之和 var num = [1,3,4,-2,-12,-3,0,-3,-1]; var sum = 0; for (var i=0;i4.5.2 異常 有時,運行腳本會出現一些問題,可能是因為編碼錯誤導致,如希望用乘法時卻編寫了加法。在這些情況下,腳本會一直運行,但可能會給出錯誤的結果。我們說這種腳本帶有bug。如果幸運(居然叫幸運 - -)的話,還是能看到輸出結果,并會想到“這個結果不可能正確”,然后再去復查腳本,糾正錯誤。 但有時運行腳本,js遇到一個不能執行的語句,或者不能求值的表達式。這是,腳本就不能繼續運行了。引擎會拋出一個異常。如果沒有捕獲這個異常,腳本會立即停止運行。我們稱之為崩潰 alert("Welcome to my script"); var message = printer+1; alert("The script is now ending")在腳本運行時,出現第一次提示,但由于第二條語句需要一個未聲明變量printer的值,所以引擎會拋出異常。因為這一異常未被捕獲,所以整個腳本都將被放棄,最后一條提示永遠不會被執行。
除了使用未聲明變量這種情況外,還有哪些錯誤會被看作錯誤,并導致JavaScript拋出異常呢?將數組長度設置為負值(RangeError)
從null值中讀取屬性(TypeError),因為只有對象擁有屬性,JavaScript不能將null轉換為對象
執行不合乎JavaScript語法的代碼,或對其求值(SyntaxError)
var a = [10,20,30]; a.length = -5; // "Uncaught RangeError: Invalid array length" var a = null; alert(a[3]); // "Uncaught TypeError: Cannot read property "3" of null" alert(3/-) // "Uncaught SyntaxError: Unexpected token )"我們可以使用JavaScript的throw語句在自己的代碼中顯式的拋出異常。可以拋出自己想要的任何值;在下面的例子中,將拋出一個字符串:
alert("Welcome to my script") throw "Ha ha ha"; // "Uncaught Ha ha ha" alert("You will never see this message")我們提到,未捕獲的異常會導致腳本崩潰。要捕獲異常,可以使用try-catch語句。
try { // 這是一個人為設計的示例,只說明了一個點 alert("Welcome to my script"); throw "Ha ha ha"; alert("You will never see this script"); } catch(e) { alert("Caught : "+e); }catch子句將一個變量(在本例中是e,這是一個相當常見的選擇)初始化(或理解為將拋出的值賦值給e?)為被拋出的值。在實踐中,許多JavaScript程序員都需要拋出帶有各種屬性的對象,用以提供一些信息,用以提供一些信息,來為其描述拋出此異常的問題。例如:
throw {reason:"class full",limit:20,date:"2012-12-22"} // "Uncaught #異常為特定的編程問題提供了非常自然的解決方案。只要你意識到,利用你當前擁有的數據,一個計算不能正常進行,那就應該拋出異常。
和大多數程序設計特性一樣,異常也可能被濫用。下面這個腳本要求用戶從三扇分別標有1,2,3的門中選擇一扇,并贏得藏在這扇門之后的獎勵。// 如果沒有異常這將是更好的一個腳本 try{ var PRIZES = ["a new car","a broken stapler","a refrigerator"]; var door = prompt("Enter a door number(1,2,or3)"); var prize = PRIZES[door-1]; alert("You have won "+prize.toUpperCase()+"!!"); }catch(e){ alert("Sorry, no such door.") }如果用戶輸入除1,2,3之外的任何值,prize的值都將是undefined,對undefined調用toUpperCase將會拋出一個異常。這個異常被捕獲,并報告一條錯誤。這一點很難通過研究代碼來發現,因此,我們說這一腳本的邏輯有些費解。他依賴于我們調用toUpperCase這一事實,它與輸入無效門牌號的"問題"沒有什么關系!我們最后用一個簡單的if語句立即核實輸入。
練習在JavaScript中除以零是否會拋出異常?
正數除0結果為Infinity,負數除0為-Infinity,0除以0為NaN。
重寫關于三個獎勵的例子,不使用異常
var PRIZES = ["a new car","a broken stapler","a refrigerator"]; var door = prompt("Enter a door number(1,2,or3)"); if (door==1 || door==2 || door==3) { alert("You have won "+PRIZES[door-1]+"!!"); } else { alert("Please Input number 1,2or3") }4.6 應該避免的編碼風格我們已經給出了以下一般形式的if、while、do-while、for和for-in語句
if (test) { stmts } if (test) { stmts } else { stmts } if (test) { stmts } else if (test) { stmts } if (test) { stmts } else if (test) { stmts } else { stmts } while (test) { stmts } do { stmts } while (test); for (init;test;each) { stmts } for (var variable in object) { stmts }但事實上,上面使用語句序列(放在大括號)的位置,JavaScript都允許使用單個語句,下面這種寫法完全合法;
if (count === 0) break; // 或者 if (count === 0) break; // 但不一定是最好的 if (count === 0) { break; }從技術角度來說,任何一個放在大括號中的語句序列,其本身就是單條語句,成為塊語句。因此,在任何需要使用單條語句的地方都可以使用塊語句,但在實踐中,如果只是為了使用塊語句而使用塊語句,看起來會很傻。下面的腳本雖然有點傻,確實合法的;
{{{{alert("Hello");}}}}強烈建議遵循現在編碼約定,僅在if語句和迭代語句中使用塊語句。我們還強烈建議,應當始終使用塊語句來構建這兩種語句,哪怕簡短形式可以減少鍵入工作。主要理由如下:如果代碼中有些語句使用大括號,有些不使用,視覺上還會顯得有些不協調。當在同一條if語句中,有些候選項帶有大括號,而另一些沒有時,看起來尤其糟糕。缺乏一致性會讓代碼顯得不平衡、不整潔、需要花費大量不必要的精力來領會其意圖。
如果缺少大括號,在修改代碼時更容易引入錯誤。這里有一個示范。
// 下面的腳本顯示數字0至9的平方 for (var i=0;i<=10;i++) { alert(i+" squared is "+(i*i)) };程序員決定向循環體中添加一條語句,定義一個新變量,用來保存計算所得的平方,但卻忘了添加大括號
for (var i=0;i<10;i++) var square = i * i; alert(i+" squared is "+square);因為for循環體總是跟在控制表達式(放在小括號)之后的單條語句,所以這個腳本聲明了變量square,并重復為它賦值,最后一次為81.在for語句完成之后將出現提示。這時,i的值為10,所以整個腳本給出單條提示:10 squared is 81,如果養成了符合語句中使用大括號的習慣,就永遠不會犯這樣的錯誤。
僅在if語句和迭代語句中使用塊語句,而且在這兩種語句中也總要使用塊語句
4.6.2 隱式分號官方的JavaScript定義聲明,以下語句應當以分號(;)結束:變量聲明語句、表達式語句(包括賦值)、do-while語句、continue語句、break、return語句和throw語句。
4.6.3 隱式聲明
但是這門語言的設計者允許程序員根據自己的一元省略語句末尾的分號,依靠JavaScript引擎來指出哪個地方應當有分號。遺憾的是,這一規則降低了我們將長語句分跨在多行代碼的靈活性。關于處理缺失分號的技巧細節,可在官文中找到。當你嘗試使用一個尚未聲明的變量時,JavaScript引擎會拋出一個異常。但是,如果嘗試為一個未聲明的變量賦值,JavaScript會自動為你聲明這個變量。
// 假定變量next_song從來沒有聲明過 next_song = "Purple Haze";這個腳本不會拋出異常!許多人可能會說它應該拋出異常的,許多專家認為這個隱式聲明是語言設計缺陷。在賦值中意外地錯誤拼寫一個變量名,會導致一個新變量的聲明,它不同與你本來想要賦值的變量。這個腳本將一直運行,只到發生了某些“跑偏道路”的事情,是錯誤很難查找。如果引擎在賦值時拋出異常,哪會更好一些,因為這個錯誤的偵測就很容易了。
4.6.4 遞增和遞減運算符 這里可能用到優先級表文檔:MDNJavaScript運算符優先級
千萬不要依賴這一“功能“,JSLint很明智的將它的應用報告為錯誤。var x = 5; x++; // x:6 var y = x++; // y:6 x:7 等號右結合 var z = ++x; // x:8 z:8 var w = ++y + z++; // y:7,w:15,z:9等號結合性為右結合,故對=右側開始計算,最后賦值給變量x。x:5
后置遞增,先計算x(使用x),最后再進行自增。x:6
出現的符號:=、后置++,后置自增無結合性,則先賦值(使用x)y:6,后自增x:7
出現的符號:=、++前置,兩者結合性都是右結合,則先自增x:8,后賦值z:8
出現的符號:=、前置++、+、后置++,加括號應該是:(var w = ((++y) + z))++,先對前置自增運算y:7,然先使用z與y相加w:15,賦值給變量w,最后z才自增。所以y:7,w:15,z:9
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91673.html
摘要:語句包含聲明語句表達式語句條件語句循環語句和中斷語句我們可以將表達式的求值結果存儲在變量中,在將來提取它們。變量在使用之前應當聲明。程序員可以用語句顯式拋出異常。程序員需要保護自己總是明確使用分號來終結聲明語句。 主要總結: 一個腳本就是一個語句序列,其中每條語句都會生成某一操作。JavaScript語句包含:聲明語句、表達式語句、條件語句、循環語句和中斷語句 我們可以將表達式的求...
摘要:這時候控制臺看到的是對象的快照,然而點開看詳情的話是這段代碼在運行的時候,瀏覽器可能會認為需要把控制臺延遲到后臺,這種情況下,等到瀏覽器控制臺輸出對象內容時,可能已經運行,因此會在點開的時候顯示,這是的異步化造成的。 本書屬于基礎類書籍,會有比較多的基礎知識,所以這里僅記錄平常不怎么容易注意到的知識點,不會全記,供大家和自己翻閱; 上中下三本的讀書筆記: 《你不知道的JavaScri...
摘要:先推薦一個我自己寫的模板引擎,項目地址。下面就是總結的編寫模板引擎的幾個步驟例如一個模板為最終會編譯成為一個函數可以觀察到模板中的所有的變量名都被指定成了參數對象的屬性,并且該函數自始至終只做了一件事,就是構建字符串,并將其返回。 showImg(https://segmentfault.com/img/remote/1460000007498588?w=300&h=113); 先推薦...
摘要:對象被傳遞到從句中被捕獲。一些語言提供了尾遞歸優化。這意味著如果一個函數返回自身遞歸調用的結果,那么調用的過程會被替換為一個循環,可以顯著提高速度。構建一個帶尾遞歸的函數。語言精粹讀書筆記函數 第四章 函數 Functions (二) 參數 arguments arguments數組: 函數可以通過此參數訪問所有它被調用時傳遞給它的參數列表,包括哪些沒有被分配給函數聲明時定義的形式參數...
摘要:在中雖然對象通過標記清除的方式進行垃圾收,但與對象卻是通過引用計數回收垃圾的,也就是說只要涉及及就會出現循環引用問題。如果垃圾收集例程回收的內存分配量低于,則變量字面量和或數組元素的臨界值就會加倍。 只挑本人重要的寫(有夾雜其他補充) 基本類型和引用類型的值 描述:基本類型值指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。 動態的屬性 引用類型的值,我們可以為其添加屬性和...
閱讀 2472·2021-11-24 09:39
閱讀 3518·2019-08-30 15:53
閱讀 594·2019-08-29 15:15
閱讀 2903·2019-08-26 13:23
閱讀 3212·2019-08-26 10:48
閱讀 643·2019-08-26 10:31
閱讀 748·2019-08-26 10:30
閱讀 2359·2019-08-23 18:32