<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第一章:進入編程 歡迎來到 *你不懂JS*(*YDKJS*)系列。 *入門與進階* 是一個對幾種編程基本概念的介紹 —— 當然我們是特別傾向于JavaScript(經常略稱為JS)的 —— 以及如何看待與理解本系列的其他書目。特別是如果你剛剛接觸編程和/或JavaScript,這本書將簡要地探索你需要什么來 *入門與進階*。 這本書從很高的角度來解釋編程的基本原則開始。它基本上假定你是在沒有或很少的編程經驗的情況下開始閱讀 *YDKJS* 的,而且你期待這些書可以透過JavaScript的鏡頭幫助你開啟一條理解編程的道路。 第一章應當作為一個快速的概覽來閱讀,它講述為了 *進入編程* 你將想要多加學習和實踐的東西。有許多其他精彩的編程介紹資源可以幫你在這個話題上走得更遠,而且我鼓勵你學習它們來作為這一章的補充。 一旦你對一般的編程基礎感到適應了,第二章將指引你熟悉JavaScript風格的編程。第二章介紹了JavaScript是什么,但是同樣的,它不是一個全面的指引 —— 那是其他 *YDKJS* 書目的任務! 如果你已經相當熟悉JavaScript,那么就首先看一下第三章作為 *YDKJS* 內容的簡要一瞥,然后一頭扎進去吧! ## 代碼 讓我們從頭開始。 一個程序,經常被稱為 *源代碼* 或者只是 *代碼*,是一組告訴計算機要執行什么任務的特殊指令。代碼通常保存在文本文件中,雖然你也可以使用JavaScript在一個瀏覽器的開發者控制臺中直接鍵入代碼 —— 我們一會兒就會講解。 合法的格式與指令的組合規則被稱為一種 *計算機語言*,有時被稱作它的 *語法*,這和英語教你如何拼寫單詞,和如何使用單詞與標點創建合法的句子差不多是相同的。 ### 語句 在一門計算機語言中,一組單詞,數字,和執行一種具體任務的操作符構成了一個 *語句*。在JavaScript中,一個語句可能看起來像下面這樣: ```js a = b * 2; ``` 字符`a`和`b`被稱為 *變量*(參見“變量”),它們就像簡單和盒子,你可以把任何東西存儲在其中。在程序中,變量持有將被程序使用的值(比如數字`42`)。可以認為它們就是值本身的標志占位符。 相比之下,`2`本身只是一個值,稱為一個 *字面值*,因為它沒有被存入一個變量,是獨立的。 字符`=`和`*`是 *操作符*(見“操作符”) —— 它們使用值和變量實施動作,比如賦值和數學乘法。 在JavaScript中大多數語句都以末尾的分號(`;`)結束。 語句`a = b * 2;`告訴計算機,大致上,去取得當前存儲在變量`b`中的值,將這個值乘以`2`,然后將結果存回到另一個我們稱為`a`變量里面。 程序只是許多這樣的語句的集合,它們一起描述為了執行你的程序的意圖所要采取的所有步驟。 ### 表達式 語句是由一個或多個 *表達式* 組成的。一個表達式是一個引用,指向變量或值,或者一組用操作符組合的變量和值。 例如: ```js a = b * 2; ``` 這個語句中有四個表達式: * `2`是一個 *字面量表達式* * `b`是一個 *變量表達式*,它意味著取出它的當前值 * `b * 2`是一個 *算數表達式*,它意味著執行乘法 * `a = b * 2`是一個 *賦值表達式*,它意味著將表達式`b * 2`的結果賦值給變量`a`(稍后有更多關于賦值的內容) 一個獨立的普通表達式也被稱為一個 *表達式語句*,比如下面的: ```js b * 2; ``` 這種風格的表達式語句不是很常見也沒什么用,因為一般來說它不會對程序的運行有任何影響 —— 它將取得`b`的值并乘以`2`,但是之后不會對結果做任何事情。 一種更常見的表達式語句是 *調用表達式* 語句(見“函數”),因為整個語句本身是一個函數調用表達式: ```js alert( a ); ``` ### 執行一個程序 這些程序語句的集合如何告訴計算機要做什么?這個程序需要被 *執行*,也稱為 *運行這個程序*。 在開發者們閱讀與編寫時,像`a = b * 2`這樣的語句很有幫助,但是它實際上不是計算機可以直接理解的形式。所以一個計算機上的特殊工具(不是一個 *解釋器* 就是一個 *編譯器*)被用于將你編寫的代碼翻譯為計算機可以理解的命令。 對于某些計算機語言,這種命令的翻譯經常是在每次程序運行時從上向下,一行接一行完成的,這通常成為代碼的 *解釋*。 對于另一些語言,這種翻譯是提前完成的,成為代碼的 *編譯*,所以當程序稍后 *運行* 時,實際上運行的東西已經是編譯好,隨時可以運行的計算機指令了。 JavaScript通常被斷言為是 *解釋型* 的,因為你的JavaScript源代碼在它每次運行時都被處理。但這并不是完全準確的。JavaScript引擎實際上在即時地 *編譯* 程序然后立即運行編譯好的代碼。 **注意:** 更多關于JavaScript編譯的信息,參見本系列的 *作用域與閉包* 的前兩章。 ## 親自嘗試 這一章將用簡單的代碼段來介紹每一個編程概念,它們都是用JavaScript寫的(當然!)。 有一件事情怎么強調都不過分:在你通讀本章時 —— 而且你可能需要花時間讀好幾遍 —— 你應當通過自己編寫代碼來實踐這些概念中的每一個。最簡單的方法就是打開你手邊的瀏覽器(Firefox,Chrome,IE,等等)的開發者工具控制臺。 **提示:** 一般來說,你可以使用快捷鍵或者菜單選項來啟動開發者控制臺。更多關于啟動和使用你最喜歡的瀏覽器的控制臺的細節,參見“精通開發者工具控制臺”(http://blog.teamtreehouse.com/mastering-developer-tools-console)。要在控制臺中一次鍵入多行,可以使用`<shift> + <enter>` 來移動到下一行。一旦你敲擊 `<enter>`,控制臺將運行你剛剛鍵入的任何東西。 讓我們熟悉一下在控制臺中運行代碼的過程。首先,我建議你在瀏覽器中打開一個新的標簽頁。我喜歡在地址欄中鍵入`about:blank`來這么做。然后,確認你的開發者控制臺是打開的,就像我們剛剛提到的那樣。 現在,鍵入如下代碼看看它是怎么運行的: ```js a = 21; b = a * 2; console.log( b ); ``` 在Chrome的控制臺中鍵入前面的代碼應該會產生如下的東西: ![](https://box.kancloud.cn/7468843f951296985a6a49673c968276_771x337.png) 繼續,試試吧。學習編程的最佳方式就是開始編碼! ### 輸出 在前一個代碼段中,我們使用了`console.log(..)`。讓我們簡單地看看這一行代碼在做什么。 你也許已經猜到了,它正是我們如何在開發者控制臺中打印文本(也就是向用戶 *輸出*)的方法。這個語句有兩個性質,我們應當解釋一下。 首先,`log( b )`部分被稱為一個函數調用(見“函數”)。這里發生的事情是,我們將變量`b`交給這個函數,它向變量`b`要來它的值,并在控制臺中打印。 第二,`console.`部分是一個對象引用,這個對象就是找到`log(..)`函數的地方。我們會在第二章中詳細講解對象和它們的屬性。 另一種創建你可以看到的輸出的方式是運行`alert(..)`語句。例如: ```js alert( b ); ``` 如果你運行它,你會注意到它不會打印輸出到控制臺,而是顯示一個內容為變量`b`的“OK”彈出框。但是,一般來說與使用`alert(..)`相比,使用`console.log(..)`會使學習編碼和在控制臺運行你的程序更簡單一些,因為你可以一次輸出許多值,而不必干擾瀏覽器的界面。 在這本書中,我們將使用`console.log(..)`來輸出。 ### 輸入 雖然我們在討論輸出,你也許還想知道 *輸入*(例如,從用戶那里獲得信息)。 對于HTML網頁來說,輸入發生的最常見的方式是向用戶顯示一個他們可以鍵入的form元素,然后使用JS將這些值讀入你程序的變量中。 但是為了單純的學習和展示的目的 —— 也就是你在這本書中將通篇看到的 —— 有一個獲取輸入的更簡單的方法。使用`prompt(..)`函數: ```js age = prompt( "Please tell me your age:" ); console.log( age ); ``` 正如你可能已經猜到的,你傳遞給`prompt(..)`的消息 —— 在這個例子中,`"Please tell me your age:"` —— 被打印在彈出框中。 它應當和下面的東西很相似: ![](https://box.kancloud.cn/78e938220669a626f29b54d89b44c7ad_772x425.png) 一旦你點擊“OK”提交輸入的文本,你將會看到你輸入的值被存儲在變量`age`中,然后我們使用`console.log(..)`把它 *輸出*: ![](https://box.kancloud.cn/281812a7033ae36fe627d88f16966abf_771x293.png) 為了讓我們在學習基本編程概念時使事情保持簡單,本書中的例子不要求輸入。但是現在你已經看到了如何使用`prompt(..)`,如果你想挑戰一下自己,你可以試著在探索這些例子時使用輸入。 ## 操作符 操作符是我們如何在變量和值上實施操作的方式。我們已經見到了兩種JavaScript操作符,`=`和`*`。 `*`操作符實施數學乘法。夠簡單的,對吧? `=`操作符用于 *賦值* —— 我們首先計算`=` *右手邊* 的值(源值)然后將它放進我們在 *左手邊* 指定的變量中(目標變量)。 **警告:** 對于指定賦值,這看起來像是一種奇怪的倒置。與`a = 42`不同,一些人喜歡把順序反轉過來,于是源值在左而目標變量在右,就像`42 -> a`(這不是合法的JavaScript!)。不幸的是,`a = 42`順序的形式,和與其相似的變種,在現代編程語言中是十分流行的。如果它讓你覺得不自然,那么就花些時間在腦中演練這個順序并習慣它。 考慮如下代碼: ```js a = 2; b = a + 1; ``` 這里,我們將值`2`賦值給變量`a`。然后,我們取得變量`a`的值(還是`2`),把它加`1`得到值`3`,然后將這個值存儲到變量`b`中。 雖然在技術上說`var`不是一個操作符,但是你將在每一個程序中都需要這個關鍵字,因為它是你 *聲明*(也就是 *創建*)變量(見“變量”)的主要方式。 你應當總是在使用變量前用名稱聲明它。但是對于每個 *作用域*(見“作用域”)你只需要聲明變量一次;它可以根據需要使用任意多次。例如: ```js var a = 20; a = a + 1; a = a * 2; console.log( a ); // 42 ``` 這里是一些在JavaScript中最常見的操作符: * 賦值:比如`a = 2`中的`=`。 * 數學:`+`(加法),`-`(減法),`*`(乘法),和`/`(除法),比如`a * 3`。 * 復合賦值:`+=`,`-=`,`*=`,和`/=`都是復合操作符,它們組合了數學操作和賦值,比如`a += 2`(與`a = a + 2`相同)。 * 遞增/遞減:`++`(遞增),`--`(遞減),比如`a++`(和`a = a + 1`很相似)。 * 對象屬性訪問:比如`console.log()`的`.`。 對象是一種值,它可以在被稱為屬性的,被具體命名的位置上持有其他的值。`obj.a`意味著一個稱為`obj`的對象值有一個名為`a`的屬性。屬性可以用`obj["a"]`這種替代的方式訪問。參見第二章。 * 等價性:`==`(寬松等價),`===`(嚴格等價),`!=`(寬松不等價),`!==`(嚴格不等價),比如`a == b`。 參見“值與類型”和第二章。 * 比較:`<`(小于),`>`(大于),`<=`(小于或寬松等價),`>=`(大于或寬松等價),比如`a <= b`。 參見“值與類型”和第二章。 * 邏輯:`&&`(與),`||`(或),比如`a || b`它選擇`a`*或*`b`中的一個。 這些操作符用于表達復合的條件(見“條件”),比如如果`a`*或者*`b`成立。 **注意:** 更多細節,以及在此沒有提到的其他操作符,可以參見Mozilla開發者網絡(MDN)的“表達式與操作符”(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators)。 ## 值與類型 如果你問一個手機店的店員一種特定手機的價格,而他們說“九十九塊九毛九”(即,$99.99),他們給了你一個實際的美元數字來表示你需要花多少錢才能買到它。如果你想兩部這種手機,你可以很容易地心算這個值的兩倍來得到你需要花費的$199.98。 如果同一個店員拿起另一部相似的手機說它是“免費的”(也許在用手比劃引號),那么他們就不是在給你一個數字,而是你的花費($0.00)的另一種表達形式 —— “免費”這個詞。 當你稍后問到這個手機是否帶充電器時,回答可能僅僅是“是”或者“不”。 以同樣的方式,當你在程序中表達一個值時,你根據你打算對這些值做什么來選擇不同的表達形式。 在編程術語中值的這些不同的表達形式稱為 *類型*。JavaScript中對這些所謂的 *基本類型* 值都有內建的類型: * 但你需要做數學計算時,你需要一個`number`。 * 當你需要在屏幕上打印一個值時,你需要一個`string`(一個或多個字符,單詞,句子)。 * 當你需要在你的程序中做決定時,你需要一個`boolean`(`true`或`false`)。 在源代碼中直接包含的值稱為 *字面量*。`string`字面量被雙引號`"..."`或單引號(`'...'`)包圍 —— 唯一的區別是風格上的偏好。`number`和`boolean`字面量用它們本身來表示(即,`42`,`true`,等等)。 考慮如下代碼: ```js "I am a string"; 'I am also a string'; 42; true; false; ``` 在`string`/`number`/`boolean`值的類型以外,編程語言通常會提供 *數組*,*對象*,*函數* 等更多的類型。我們會在本章和下一章中講解更多關于值和類型的內容。 ### 類型間轉換 如果你有一個`number`但需要將它打印在屏幕上,那么你就需要將這個值轉換為一個`string`,在JavaScript中這種轉換稱為“強制轉換”。類似地,如果某些人在一個電商網頁的form中輸入一系列數字,那么它是一個`string`,但是如果你需要使用這個值去做數學運算,那么你就需要將它 *強制轉換* 為一個`number`。 為了在 *類型* 之間強制轉換,JavaScript提供了幾種不同的工具。例如: ```js var a = "42"; var b = Number( a ); console.log( a ); // "42" console.log( b ); // 42 ``` 使用上面展示的`Number(..)`(一個內建函數)是一種從任意其他類型到`number`類型的 *明確的* 強制轉換。這應當是相當直白的。 但是一個具有爭議的話題是,當你試著比較兩個還不是相同類型的值時發生的事情,它需要 *隱含的* 強制轉換。 當比較字符串`"99.99"`和數字`99.99`時,大多數人同意它們是等價的。但是他們不完全相同,不是嗎?它們是相同的值的兩種不同表現形式,兩個不同的 *類型*。你可以說它們是“寬松地等價”的,不是嗎? 為了在這些常見情況下幫助你,JavaScript有時會啟動 *隱含的* 強制轉換來把值轉換為匹配的類型。 所以如果你使用`==`寬松等價操作符來進行`"99.99" == 99.99`比較,JavaScript會將左手邊的`"99.99"`轉換為它的`number`等價物`99.99`。所以比較就變成了`99.99 == 99.99`,這當然是成立的。 雖然隱含強制轉換是為了幫助你而設計,但是它也可能把你搞糊涂,如果你沒有花時間去學習控制它行為的規則。大多數開發者從沒有這么做,所以常見的感覺是隱含的強制轉換是令人困惑的,并且會產生意外的bug危害程序,因此應當避免使用。有時它甚至被稱為這種語言中的設計缺陷。 然而,隱含強制轉換是一種 *可以被學習* 的機制,而且是一種 *應當* 被所有想要認真對待JavaScript編程的人學習的機制。一旦你學習了這些規則,它不僅是消除了困惑,而且它實際上是你的程序變得更好!這種努力是值得的。 **注意:** 關于強制轉換的更多信息,參見本書第二章和本系列 *類型與文法* 的第四章。 ## 代碼注釋 手機店店員可能會寫下一些筆記,記下新出的手機的特性或者他們公司推出的新套餐。這些筆記僅僅是給店員使用的 —— 他們不是給顧客讀的。不管怎樣,通過記錄下為什么和如何告訴顧客他應當說的東西,這些筆記幫助店員更好的工作。 關于編寫代碼你要學的最重要的課程之一,就是它不僅僅是寫給計算機的。代碼的每一個字節都和寫給編譯器一樣,也是寫給開發者的。 你的計算機只關心機器碼,一系列源自 *編譯* 的0和1。你幾乎可以寫出無限多種可以產生相同0和1序列的代碼。所以你對如何編寫程序作出的決定很重要 —— 不僅是對你,也對你的團隊中的其他成員,甚至是你未來的自己。 你不僅應當努力去編寫可以正確工作的程序,而且應當努力編寫檢視起來有道理的程序。你可以通過給變量(見“變量”)和函數(見“函數”)起一個好名字在這條路上走很遠。 但另外一個重要的部分是代碼注釋。它們純粹是為了向人類解釋一些事情而在你的程序中插入的一點兒文本。解釋器/編譯器將總是忽略這些注釋。 關于什么是良好注釋的代碼有許多意見;我們不能真正地定義絕對統一的規則。但是一些意見和指導是十分有用的: * 沒有注釋的代碼是次優的。 * 過多的注釋(比如,每行都有注釋)可能是代碼編寫的很爛的標志。 * 注釋應當解釋 *為什么*,而不是 *是什么*。它們可以選擇性地解釋 *如何做*,如果代碼特別令人困惑的話。 在JavaScript中,有兩種可能的注釋類型:單行注釋和多行注釋 考慮如下代碼: ```js // 這是一個單行注釋 /* 而這是 一個多行 注釋。 */ ``` 如果你想在一個語句的正上方,或者甚至是在行的末尾加一個注釋,`//`單行注釋是很合適的。這一行上`//`之后的所有東西都將被視為注釋(因此被編譯器忽略),一直到行的末尾。在單行注釋內部可以出現的內容沒有限制。 考慮: ```js var a = 42; // 生命的意義是 42 ``` 如果你想在注釋中用好幾行來解釋一些事情,`/* .. */`多行注釋就很合適。 這是多行注釋的一個常見用法: ```js /* 使用下面的值是因為 它回答了 全宇宙中所有的問題。 */ var a = 42; ``` 它還可以出現在一行中的任意位置,甚至是一行的中間,因為`*/`終結了它。例如: ```js var a = /* 隨機值 */ 42; console.log( a ); // 42 ``` 在多行注釋中唯一不能出現的就是`*/`,因為這將干擾注釋的結尾。 你絕對會希望通過養成注釋代碼的習慣來開始學習編程。在本書剩余的部分中,你將看到我使用注釋來解釋事情,請也在你自己的實踐中這么做。相信我,所有閱讀你的代碼的人都會感謝你! ## 變量 大多數有用的程序都需要在程序運行整個過程中,追蹤由于你的程序所意圖的任務被調用的底層不同的操作而發生的值的變化。 要這樣做的最簡單的方法是將一個值賦予一個符號容器,稱為一個 *變量* —— 因為在這個容器中的值可以根據需要不時 *變化* 而得名。 在某些編程語言中,你可以聲明一個變量(容器)來持有特定類型的值,比如`number`或`string`。因為防止了意外的類型轉換,*靜態類型*,也被稱為 *類型強制*,通常被認為是對程序正確性有好處的。 另一些語言在值上強調類型而非在變量上。*弱類型*,也被稱為 *動態類型*,允許變量在任意時刻持有任意類型的值。因為它允許一個變量在程序邏輯流程中代表一個值,而不論這個值在任意給定的時刻是什么類型,所以它被認為是對程序靈活性有好處的。 JavaScript使用的是后者,*動態類型*,這意味著變量可以持有任意 *類型* 的值而沒有任何 *類型* 強制約束。 正如我們剛才提到的,我們使用`var`語句來聲明一個變量 —— 注意在這種聲明中沒有其他的 *類型* 信息。考慮這段簡單的代碼: ```js var amount = 99.99; amount = amount * 2; console.log( amount ); // 199.98 // 將 `amount` 轉換為一個字符串, // 并在開頭加一個 "$" amount = "$" + String( amount ); console.log( amount ); // "$199.98" ``` 變量`amount`開始時持有數字`99.99`,然后持有`amount * 2`的`number`結果,也就是`199.98`。 第一個`console.log(..)`命令不得不 *隱含地* 將這個`number`值強制轉換為一個`string`才能夠打印出來。 然后語句`amount = "$" + String(amount)` *明確地* 將值`199.98`強制轉換為一個`string`并且在開頭加入一個`"$"`字符。這時,`amount`現在就持有這個`string`值`$199.98`,所以第二個`console.log(..)`語句無需強制轉換就可以把它打印出來。 JavaScript開發者將會注意到為值`99.99`,`199.98`,和`"$199.98"`都使用變量`amount`的靈活性。靜態類型的擁護者們將偏好于使用一個分離的變量,比如`amountStr`來持有這個值最后的`"$199.98"`表達形式,因為它是一個不同的類型。 不管哪種方式,你將會注意到`amount`持有一個在程序運行過程中不斷變化的值,這展示了變量的主要目地:管理程序 *狀態*。 換句話說,在你程序運行的過程中 *狀態* 追蹤著值的改變。 變量的另一種常見用法是將值的設定集中化。當你為一個在程序中通篇不打算改變的值聲明了一個變量時,它更一般地被稱為 *常量*。 你經常會在程序的頂部聲明這些 *常量*,這樣提供了一種方便:如果你需要改變一個值時你可以到唯一的地方去尋找。根據慣例,用做常量的JavaScript變量通常是大寫的,在多個單詞之間使用下劃線`_`連接。 這里是一個呆萌的例子: ```js var TAX_RATE = 0.08; // 8% sales tax var amount = 99.99; amount = amount * 2; amount = amount + (amount * TAX_RATE); console.log( amount ); // 215.9784 console.log( amount.toFixed( 2 ) ); // "215.98" ``` **注意:** `console.log(..)`是一個函數`log(..)`作為一個在值`console`上的對象屬性被訪問,與此類似,這里的`toFixed(..)`是一個可以在值`number`上被訪問的函數。JavaScript `number`不會被自動地格式化為美元 —— 引擎不知道你的意圖,而且也沒有通貨類型。`toFixed(..)`讓我們指明四舍五入到小數點后多少位,而且它如我們需要的那樣產生一個`string`。 變量`TAX_RATE`只是因為慣例才是一個 *常量* —— 在這個程序中沒有什么特殊的東西可以防止它被改變。但是如果這座城市將它的消費稅增至9%,我們仍然可以很容地通過在一個地方將`TAX_RATE`被賦予的值改為`0.09`來更新我們的程序,而不是在程序通篇中尋找許多值`0.08`出現的地方然后更新它們全部。 在寫作本書時,最新版本的JavaScript(通常稱為“ES6”)引入了一個聲明常量的新方法,用`const`代替`var`: ```js // 在ES6中: const TAX_RATE = 0.08; var amount = 99.99; // .. ``` 常量就像帶有不變的值的變量一樣有用,常量還防止在初始設置之后的某些地方意外地改變它的值。如果你試著在第一個聲明之后給`TAX_RATE`賦予一個不同的值,你的程序將會拒絕這個改變(而且在Strict模式下,會產生一個錯誤 —— 見第二章的“Strict模式”)。 順帶一提,這種防止編程錯誤的“保護”與靜態類型的類型強制很類似,所以你可以看到為什么在其他語言中的靜態類型很吸引人。 **注意:** 更多關于如何在你程序的變量中使用不同的值,參見本系列的 *類型與文法*。 ## 塊兒 在你買你的新手機時,手機店店員必須走過一系列步驟才能完成結算。 相似地,在代碼中我們經常需要將一系列語句一起分為一組,這就是我們常說的 *塊兒*。在JavaScript中,一個塊兒被定義為包圍在一個大括號`{ .. }`中的一個或多個語句。考慮如下代碼: ```js var amount = 99.99; // 一個普通的塊兒 { amount = amount * 2; console.log( amount ); // 199.98 } ``` 這種獨立的`{ .. }`塊兒是合法的,但是在JS程序中并不常見。一般來說,塊兒是添附在一些其他的控制語句后面的,比如一個`if`語句(見“條件”)或者一個循環(見“循環”)。例如: ```js var amount = 99.99; // 數值夠大嗎? if (amount > 10) { // <-- 添附在`if`上的塊兒 amount = amount * 2; console.log( amount ); // 199.98 } ``` 我們將在下一節講解`if`語句,但是如你所見,`{ .. }`塊兒帶著它的兩個語句被添附在`if (amount > 10)`后面;塊兒中的語句將會僅在條件成立時被處理。 **注意:** 與其他大多數語句不同(比如`console.log(amount);`),一個塊兒語句不需要分號(`;`)來終結它。 ## 條件 “你想來一個額外的屏幕貼膜嗎?只要$9.99。” 熱心的手機店店員請你做個決定。而你也許需要首先咨詢一下錢包或銀行帳號的 *狀態* 才能回答這個問題。但很明顯,這只是一個簡單的“是與否”的問題。 在我們的程序中有好幾種方式可以表達 *條件*(也就是決定)。 最常見的一個就是`if`語句。實質上,你在說,“*如果* 這個條件成立,做后面的……”。例如: ```js var bank_balance = 302.13; var amount = 99.99; if (amount < bank_balance) { console.log( "I want to buy this phone!" ); } ``` `if`語句在括號`( )`之間需要一個表達式,它不是被視作`true`就是被視作`false`。在這個程序中,我們提供了表達式`amount < bank_balance`,它確實會根據變量`bank_balance`中的值被求值為`true`或`false`。 如果條件不成立,你甚至可以提供一個另外的選擇,稱為`else`子句。考慮下面的代碼: ```js const ACCESSORY_PRICE = 9.99; var bank_balance = 302.13; var amount = 99.99; amount = amount * 2; // 我們買得起配件嗎? if ( amount < bank_balance ) { console.log( "I'll take the accessory!" ); amount = amount + ACCESSORY_PRICE; } // 否則: else { console.log( "No, thanks." ); } ``` 在這里,如果`amount < bank_balance`是`true`,我們將打印出`"I'll take the accessory!"`并在我們的變量`amount`上加`9.99`。否則,`else`子句說我們將禮貌地回應`"No, thanks."`,并保持`amount`不變。 正如我們在早先的“值與類型”中討論的,一個還不是所期望類型的值經常會被強制轉換為那種類型。`if`語句期待一個`boolean`,但如果你傳給它某些還不是`boolean`的東西,強制轉換就會發生。 JavaScript定義了一組特定的被認為是“falsy”的值,因為在強制轉換為`boolean`時,它們將變為`false` —— 這些值包括`0`和`""`。任何不再這個`falsy`列表中的值都自動是“truthy” —— 當強制轉換為`boolean`時它們變為`true`。truthy值包括`99.99`和`"free"`這樣的東西。更多信息參見第二章的“Truthy與Falsy”。 除了`if` *條件* 還以其他形式存在。例如,`switch`語句可以被用作一系列`if..else`語句的縮寫(見第二章)。循環(見“循環”)使用一個 *條件* 來決定循環是否應當繼續或停止。 **注意:** 關于在 *條件* 的測試表達式中可能發生的隱含強制轉換的更深層的信息,參見本系列的 *類型與文法* 的第四章。 ## 循環 在繁忙的時候,有一張排隊單,上面記載著需要和手機店店員談話的顧客。雖然排隊單上還有許多人,但是她只需要持續服務下一位顧客就好了。 重復一組動作直到特定的條件失敗 —— 換句話說,僅在條件成立時重復 —— 就是程序循環的工作;循環可以有不同的形式,但是它們都符合這種基本行為。 一個循環包含測試條件和一個塊兒(通常是`{ .. }`)。每次循環塊兒執行,都稱為一次 *迭代*。 例如,`while`循環和`do..while`循環形式就說明了這種概念 —— 重復一塊兒語句直到一個條件不再求值得`true`: ```js while (numOfCustomers > 0) { console.log( "How may I help you?" ); // 服務顧客…… numOfCustomers = numOfCustomers - 1; } // 與 do { console.log( "How may I help you?" ); // 服務顧客…… numOfCustomers = numOfCustomers - 1; } while (numOfCustomers > 0); ``` 這些循環之間唯一的實際區別是,條件是在第一次迭代之前(`while`)還是之后(`do..while`)被測試。 在這兩種形式中,如果條件測試得`false`,那么下一次迭代就不會運行。這意味著如果條件初始時就是`false`,那么`while`循環就永遠不會運行,但是一個`do..while`循環將僅運行一次。 有時你會為了計數一組特定的數字來進行循環,比如從`0`到`9`(十個數)。你可以通過設定一個值為`0`的循環迭代變量,比如`i`,并在每次迭代時將它遞增`1`。 **警告:** 由于種種歷史原因,編程語言幾乎總是用從零開始的方式來計數的,這意味著計數開始于`0`而不是`1`。如果你不熟悉這種思維模式,一開始它可能十分令人困惑。為了更適應它,花些時間練習從`0`開始數數吧! 條件在每次迭代時都會被測試,好像在循環內部有一個隱含的`if`語句一樣。 你可以使用JavaScript的`break`語句來停止一個循環。另外,我們可以看到如果沒有`break`機制,就會極其容易地創造一個永遠運行的循環。 讓我們展示一下: ```js var i = 0; // 一個 `while..true` 循環將會永遠運行,對吧? while (true) { // 停止循環? if ((i <= 9) === false) { break; } console.log( i ); i = i + 1; } // 0 1 2 3 4 5 6 7 8 9 ``` **警告:** 這未必是你想在你的循環中使用的實際形式。它是僅為了說明的目的才出現在這里的。 雖然一個`while`(或`do..while`)可以手動完成任務,但是為了同樣的目的,還有一種稱為`for`循環的語法形式: ```js for (var i = 0; i <= 9; i = i + 1) { console.log( i ); } // 0 1 2 3 4 5 6 7 8 9 ``` 如你所見,對于這兩種循環形式來說,前10次迭代(`i`的值從`0`到`9`)的條件`i <= 9`都是`true`,而且一旦`i`值為`10`就變為`false`。 `for`循環有三個子句:初始化子句(`var i=0`),條件測試子句(`i <= 9`),和更新子句(`i = i + 1`)。所以如果你想要使用循環迭代來計數,`for`是一個更緊湊而且更易理解和編寫的形式。 還有一些意在迭代特定的值的特殊循環形式,比如迭代一個對象的屬性(見第二章),它隱含的測試條件是所有的屬性是否都被處理過了。無論循環是何種形式,“循環直到條件失敗”的概念是它們共有的。 ## 函數 手機店的店員可能不會拿著一個計算器到處走,用它來搞清稅費和最終的購物款。這是一個她需要定義一次然后一遍又一遍地重用的任務。很有可能的是,公司有一個帶有內建這些“功能”的收銀機(電腦,平板電腦,等等)。 相似地,幾乎可以肯定你的程序想要將代碼的任務分割成可以重用的片段,而不是頻繁地多次重復自己。這么做的方法是定義一個`function`。 一個函數一般來說是一段被命名的代碼,它可以使用名稱來被“調用”,而每次調用它內部的代碼就會運行。考慮如下代碼: ```js function printAmount() { console.log( amount.toFixed( 2 ) ); } var amount = 99.99; printAmount(); // "99.99" amount = amount * 2; printAmount(); // "199.98" ``` 函數可以選擇性地接收參數值(也就是參數)—— 你傳入的值。而且它們還可以選擇性地返回一個值。 ```js function printAmount(amt) { console.log( amt.toFixed( 2 ) ); } function formatAmount() { return "$" + amount.toFixed( 2 ); } var amount = 99.99; printAmount( amount * 2 ); // "199.98" amount = formatAmount(); console.log( amount ); // "$99.99" ``` 函數`printAmount(..)`接收一個參數,我們稱之為`amt`。函數`formatAmount()`返回一個值。當然,你也可以在同一個函數中組合這兩種技術。 函數經常被用于你打算多次調用的代碼,但它們對于僅將有關聯的代碼組織在一個命名的集合中也很有用,即便你只打算調用它們一次。 考慮如下代碼: ```js const TAX_RATE = 0.08; function calculateFinalPurchaseAmount(amt) { // 計算帶有稅費的新費用 amt = amt + (amt * TAX_RATE); // 返回新費用 return amt; } var amount = 99.99; amount = calculateFinalPurchaseAmount( amount ); console.log( amount.toFixed( 2 ) ); // "107.99" ``` 雖然`calculateFinalPurchaseAmount(..)`只被調用了一次,但是將它的行為組織進一個分離的帶名稱的函數,讓使用它邏輯的代碼(`amount = calculateFinal...`語句)更干凈。如果函數中擁有更多的語句,這種好處將會更加明顯。 ### 作用域 如果你向手機店的店員詢問一款她們店里沒有的手機,那么她就不能賣給你你想要的。她只能訪問她們店庫房里的手機。你不得不到另外一家店里去看看能不能找到你想要的手機。 編程對這種概念有一個術語:*作用域*(技術上講稱為 *詞法作用域*)。在JavaScript中,每個函數都有自己的作用域。作用域基本上就是變量的集合,也是如何使用名稱訪問這些變量的規則。只有在這個函數內部的代碼才能訪問這個函數 *作用域內* 的變量。 在同一個作用域內變量名必須是唯一的 —— 不能有兩個不同的變量`a`并排出現。但是相同的變量名`a`可以出現在不同的作用域中。 ```js function one() { // 這個 `a` 僅屬于函數 `one()` var a = 1; console.log( a ); } function two() { // 這個 `a` 僅屬于函數 `two()` var a = 2; console.log( a ); } one(); // 1 two(); // 2 ``` 另外,一個作用域可以嵌套在另一個作用域中,就像生日Party上的小丑在一個氣球的里面吹另一個氣球一樣。如果一個作用域嵌套在另一個中,那么在內部作用域中的代碼就可以訪問這兩個作用域中的變量。 考慮如下代碼: ```js function outer() { var a = 1; function inner() { var b = 2; // 我們可以在這里同時訪問 `a` 和 `b` console.log( a + b ); // 3 } inner(); // 我們在這里只能訪問 `a` console.log( a ); // 1 } outer(); ``` 詞法作用域規則說,在一個作用域中的代碼既可以訪問這個作用域中的變量,又可以訪問任何在它外面的作用域的變量。 所以,在函數`inner()`內部的代碼可以同時訪問變量`a`和`b`,但是僅在`outer()`中的代碼只能訪問`a` —— 它不能訪問`b`因為這個變量僅存在于`inner()`內部。 回憶一下先前的這個代碼段: ```js const TAX_RATE = 0.08; function calculateFinalPurchaseAmount(amt) { // 計算帶有稅費的新費用 amt = amt + (amt * TAX_RATE); // 返回新費用 return amt; } ``` 因為詞法作用域,常數`TAX_RATE`(變量)可以從`calculateFinalPurchaseAmount(..)`函數中訪問,即便它沒有被傳入這個函數。 **注意:** 關于詞法作用域的更多信息,參見本系列的 *作用域與閉包* 的前三章。 ## 練習 在編程的學習中絕對沒有什么可以替代練習。我寫的再好也不可能使你成為一個程序員。 帶著這樣的意識,讓我們試著練習一下我們在本章學到的一些概念。我將給出“需求”,而你首先試著實現它。然后參考下面的代碼清單來看看我是怎么處理它的。 * 寫一個程序來計算你購買手機的總價。你將不停地購買手機直到你的銀行賬戶上的錢都用光(提示:循環!)。你還將為每個手機購買配件,只要你的花費低于你心理預算。 * 在你計算完購買總價之后,加入稅費,然后用合適的格式打印出計算好的購買總價。 * 最后,將總價與你銀行賬戶上的余額作比較,來看看那你是否買的起。 * 你應當為“稅率”,“手機價格”,“配件價格”和“花費預算”設置一些常數,也為你的“銀行賬戶余額”設置一個變量。 * 你應當為稅費的計算和價格的格式化 —— 使用一個“$”并四舍五入到小數點后兩位 —— 定義函數。 * **加分挑戰:** 試著在這個程序中利用輸入,也許是使用在前面的“輸入”中講過的`prompt(..)`。比如,你可能會提示用戶輸入它們的銀行賬戶余額。發揮創造力好好玩兒吧! 好的,去吧。試試看。在你自己實踐過之前不要偷看我的代碼清單! **注意:** 因為這是一本JavaScript書,很明顯我將使用JavaScript解決這個聯系。但是目前你可使用其他的語言,如果你感覺更適應的話。 對于這個練習,這是我的JavaScript解決方案: ```js const SPENDING_THRESHOLD = 200; const TAX_RATE = 0.08; const PHONE_PRICE = 99.99; const ACCESSORY_PRICE = 9.99; var bank_balance = 303.91; var amount = 0; function calculateTax(amount) { return amount * TAX_RATE; } function formatAmount(amount) { return "$" + amount.toFixed( 2 ); } // 只要你還有錢就不停地買手機 while (amount < bank_balance) { // 買個新手機 amount = amount + PHONE_PRICE; // 還買得起配件嗎? if (amount < SPENDING_THRESHOLD) { amount = amount + ACCESSORY_PRICE; } } // 也別忘了給政府交錢 amount = amount + calculateTax( amount ); console.log( "Your purchase: " + formatAmount( amount ) ); // Your purchase: $334.76 // 你買的起嗎? if (amount > bank_balance) { console.log( "You can't afford this purchase. :(" ); } // 你買不起 :( ``` **注意:** 運行這個JavaScript程序的最簡單的方法是將它鍵入到你手邊的瀏覽器的開發者控制臺中。 你做的怎么樣?看了我的代碼之后,現在再試一次也沒什么不好。而且你可以改變某些常數來看看使用不同的值時這個程序運行的如何。 ## 復習 學習編程不一定是個復雜而且巨大的過程。你只需要在腦中裝進幾個基本的概念。 它們就像構建塊兒。要建一座高塔,你就要從堆砌構建塊兒開始。編程也一樣。這里是一些編程中必不可少的構建塊兒: * 你需要 *操作符* 來在值上實施動作。 * 你需要值和 *類型* 來試試不同種類的動作,比如在`number`上做數學,或者使用`string`輸出。 * 你需要 *變量* 在你程序執行的過程中存儲數據(也就是 *狀態*)。 * 你需要 *條件*,比如`if`語句來做決定。 * 你需要 *循環* 來重復任務,直到一個條件不再成立。 * 你需要 *函數* 來將你的代碼組織為有邏輯的和可復用的塊兒。 代碼注釋是一種編寫更好可讀性代碼的有效方法,它使你的代碼更易理解,維護,而且如果稍后出現問題的話更易修改。 最后,不要忽視練習的力量。學習寫代碼的最好方法就是寫代碼。 現在,我很高興看到你在學習編碼的道路上走得很好!保持下去。不要忘了看看其他編程初學者的資源(書,博客,在線教學,等等)。這一章和這本書是一個很好的開始,但它們只是一個簡要的介紹。 下一章將會復習許多本章中的概念,但是是從更加專門于JavaScript的視角,這將突出將在本系列的剩余部分將要深度剖析的大多數主要話題。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看