《 JavaScript程序設計》—— 第四章 表達式語句

4.1 聲明語句

聲明語句也叫變量語句,這種語句會建立新變量。能夠在聲明變量時給出初始值,若是沒有明確給出,變量的值就是undefined。git

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 表達式語句

表達式語句對錶達式求值但,忽略獲得的值。這聽起來有點傻,由於能夠編寫出相似於下面的無用腳本;api

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的值不會變。dom


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 ifelse部分)

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。this

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]。幾乎能夠確定它有什麼錯誤?

    1. 可能存在語法錯誤,方括號運算符中若是不是變量,必須用引號括起


4.3.5 短路執行

以前的布爾值一章中,若是x和y都是布爾值,則AND-ALSO和OR-ELSE運算符具備如下特性:

  • 當且僅當xy爲真(或均爲假)時,x || y爲真

  • 當且僅當xy均爲真時,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的顏色。若是favoriteColorundefined爲假),則根據定義,||表達式的值就是它的第二個(右側)操做數,也就是黑色。

練習

  • 對錶達式 3 && "xyz" || null求值。

    1. 首先對3 && "xyz"求值,3轉換爲true,繼續對右邊表達式求值,爲true,形成 ||短路,不對null進行計算,返回"xyz"

  • 對錶達式3 && "" || null求值。

    1. 首先對3 && ""求值計算,空字符串轉換爲false,而因||須要,繼續對右邊表達式求值,返回null

  • 判斷正誤:對於變量xyx && y老是等同於x ? y : x

    1. 正確

    2. 首先對x求值,判斷是否短路,x爲真,求值yx爲假,那只有x已求值

  • 判斷正誤:對於變量xyx || y老是等同於x ? x : y

    1. 正確

    2. 首先對x求值,判斷是否短路,x爲真,短路並返回,x爲假,繼續求值y


4.4 迭代

上一節研究了不一樣狀況下作不一樣事情的方法。如今來看一遍又一遍地重複同一事情的方法。

4.4.1 whiledo-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<words.length;i++) {
            result += words[i].charAt(0).toUpperCase();
        }
        alert(result);
        // 爲何使用i,由於數組元素索引(index),因而使用其首字母,一個傳統了。

下面是一個另外一個與數組有關的例子,它演示了一種常見模式,用於檢查每一個數組元素,看它是否知足某一條件。 // 顯示一個數組中的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<n;i++) {
            if (a[i]===0) {
                numberOfZeros++;
            }
        }
        alert(numberOfZeros);

注意這個示例還演示瞭如何在for循環的初始化部分聲明兩個變量。將n初始化爲數組的長度,循環終止檢測變得簡單一點。
除了迭代數值範圍和數組元素以外,for語句還常常用於迭代一個字符串中的各個字符。

  1. 若是字符是數字,則直接"將它傳送"給結果字符串

  2. 若是字符在A~Z之間,則查找對應的手機鍵盤數字,並傳送該數字。

  3. 若是字符是其餘內容,則忽略它。

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<phoneText.length;i++) {
            var c = phoneText.charAt(i);
            if (/\d/.test(c)) {
                result += c;
            } else if (c in LETTER_TO_NUMBER) {
                result += LETTER_TO_NUMBER[c];
            }
        }
        alert("The phone number is: "+result);

在迭代另外一類序列時也常常看到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.scorescores.next.scorescores.next.next.score

下一個例子,演示嵌套循環。

var SIZE = 9    ;            
        document.write("<table border='1' cellspacing='0'>")
        for (var i=1;i<=SIZE;i++) {
            document.write("<tr>")
            for (var j=1;j<=SIZE;j++) {
                document.write("<td>"+i*j+"</td>");
            }
            document.write("<tr>")    
        }
        document.write("</table>")

圖片描述

練習

  • 編寫一個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、一、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]+"<br />")
        }
        // dog[property]注意property是字符串類型,而不是變量。

4.5中斷

一般,語句都是按順序一次執行一條。條件語句和迭代語句會稍微偏離這種有時被稱做直線式代碼的形式,但這種偏離是語句結構自己決定的,很容易識別。但還四種語句的結構化不是這麼強,他們對控制流的效果是中斷性的。有這些:

  • break,當即放棄執行當前正在執行的switch或迭代語句;

  • continue,當即放棄一個迭代語句中當前迭代的剩餘部分;

  • return,當即放棄當前執行的函數;

  • thorw,將在後面介紹;

4.5.1 breakcontinue

break語句將當即終止整個循環。在搜索數組(或對象鏈),這一語句特別有用,它會在你找到正在查找的內容以後當即終止搜索。

// 查找第一個偶數的索引位置
        for (var i=0;i<array.length;i++) {
            if (array[i]%2===0) {
                alert("Even number found at position "+i);
                break;
            }                
        }

continue語句當即開始循環的下一次迭代,而不在完成當前迭代。當循環中的一些(而非所有)迭代生成有用信息時,這一語句很是有用。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<choices.length;i++) {
                if (choices[i]===53) {
                    found = true;
                    break Search;
                }
            }
        }

練習

  • 重寫計算數組例子,改成使用break語句。

    // continue重寫:計算一個數組全部正數值之和
        var num = [1,3,4,-2,-12,-3,0,-3,-1];
        var sum = 0;
        for (var i=0;i<num.length;i++) {
            if (num[i]<0) {
                break;    // break後,回到條件更新表達式,這裏是自增
            };
            sum += num[i];
        }
        alert(sum)

4.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 #<Object>"

異常爲特定的編程問題提供了很是天然的解決方案。只要你意識到,利用你當前擁有的數據,一個計算不能正常進行,那就應該拋出異常。
和大多數程序設計特性同樣,異常也可能被濫用。下面這個腳本要求用戶從三扇分別標有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中除以零是否會拋出異常?

    1. 正數除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 應該避免的編碼風格

咱們已經給出瞭如下通常形式的ifwhiledo-whileforfor-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語句。
可是這門語言的設計者容許程序員根據本身的一元省略語句末尾的分號,依靠JavaScript引擎來指出哪一個地方應當有分號。遺憾的是,這一規則下降了咱們將長語句分跨在多行代碼的靈活性。關於處理缺失分號的技巧細節,可在官文中找到。

4.6.3 隱式聲明

當你嘗試使用一個還沒有聲明的變量時,JavaScript引擎會拋出一個異常。可是,若是嘗試爲一個未聲明的變量賦值,JavaScript會自動爲你聲明這個變量。

// 假定變量next_song歷來沒有聲明過
        next_song = "Purple Haze";

這個腳本不會拋出異常!許多人可能會說它應該拋出異常的,許多專家認爲這個隱式聲明是語言設計缺陷。在賦值中意外地錯誤拼寫一個變量名,會致使一個新變量的聲明,它不一樣與你原本想要賦值的變量。這個腳本將一直運行,只到發生了某些「跑偏道路」的事情,是錯誤很難查找。若是引擎在賦值時拋出異常,哪會更好一些,由於這個錯誤的偵測就很容易了。
千萬不要依賴這一「功能「,JSLint很明智的將它的應用報告爲錯誤。


4.6.4 遞增和遞減運算符

這裏可能用到優先級表文檔:MDNJavaScript運算符優先級

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
  • 等號結合性爲右結合,故對=右側開始計算,最後賦值給變量xx:5

  • 後置遞增,先計算x(使用x),最後再進行自增。x:6

  • 出現的符號:=後置++,後置自增無結合性,則先賦值(使用x)y:6,後自增x:7

  • 出現的符號:=++前置,二者結合性都是右結合,則先自增x:8,後賦值z:8

  • 出現的符號:=前置+++後置++,加括號應該是:(var w = ((++y) + z))++,先對前置自增運算y:7,然先使用zy相加w:15,賦值給變量w,最後z才自增。因此y:7,w:15,z:9

相關文章
相關標籤/搜索