JavaScript入門

負責人:黃丰嘉
授課時間:2019-09-15 (日)


參考資源

  1. What’s the difference between JavaScript and ECMAScript?
  2. JavaScript Numbers - w3schools
  3. 重新認識 JavaScript: Day 03 變數與資料型別

課程大綱


§ 什麼是 JavaScript?

  • JavaScript 是瀏覽器唯一指定的內建程式語言。
  • JavaScript 跟 Java 毫無關係!
    • JavaScript 只是搭上當時正火紅的 Java 這股熱潮,而命名的。
  • 應用範圍:
    • 網頁瀏覽器
    • MongoDB 和 CouchDB 使用 JavaScript 作為腳本語言及查詢語言。
    • Node.js:運行於伺服器端的 JavaScript。

JavaScript 歷史

  • 1995年,JavaScript誕生,由 Netscape (網景公司,當時知名瀏覽器廠商) 著手開發一門在瀏覽器上執行的語言系統。
    • 當時為了處理網頁表單的簡單驗證。
  • 隨之,Netscape以外的瀏覽器也漸漸採用,亦在1997年建立了統一標準 ECMAScript standard

JavaScript 版本

JavaScript Versions

版本 正式名稱 備註
1 ECMAScript 1 (1997) First Edition.
3 ECMAScript 3 (1999) 廣泛支援 (2000 - 2010)
5 ECMAScript 5 (2009) JS ES5
6 ECMAScript 2015 JS ES6 添加letconst、可以預設參數的值
8 ECMAScript 2017 添加 Object
9 ECMAScript 2018 最新版

§ 值、型別與運算子

本節介紹JavaScript中,值(Value)的型別(types)和運算子(operators)。

  • JavaScript 是個「弱型別」的語言。

    • 變數沒有型別,值(value)才有。
      • 型別的資訊只在值(value)或物件(object)本身。
      • 變數本身無需宣告型別,變數只用來作為取得值或物件的參考。
  • ES5標準,JavaScript 內建型別:

    • 基本型別 (Primitives)
      1. Number
      2. String
      3. Boolean
      4. Null
      5. Undefined
    • 物件型別 (Object)
      • function

    ES6 之後多了新的型別:Symbol

1. 數字 (Number)

  • JavaScript 只有一種數值的型別,就是 number。
    • JavaScript 使用固定的 64 bits 來儲存數值。

      值 (分數/尾數) 指數 (表示次方數) 正負號
      52 bits (0 - 51) 11 bits (52 - 62) 1 bit (63)
    • 不管整數或帶有小數點的數字,還是科學記號表示都是。

      ​​​​​​​​var x = 3.14; // A number with decimals ​​​​​​​​var y = 3; // A number without decimals ​​​​​​​​var x = 123e5; // 12300000 ​​​​​​​​var y = 123e-5; // 0.00123 ​​​​​​​​var z = 2.998e8; // 2.998 × 10^8 = 299800000
    • 除了常見的數字外,還有3種特殊的數字。

      1. Infinity:正無限大。

        ​​​​​​​​​​​​console.log(Infinity - 1); ​​​​​​​​​​​​// Infinity ​​​​​​​​​​​​console.log(123 / 0); ​​​​​​​​​​​​// Infinity
      2. -Infinity:負無限大。

      3. NaN:不是數值(not a number),儘管它是 number type 的一種值(value)。

        • NaN 與任何數字作數學運算,結果都是 NaN。

        • NaN 並不等於任何的數字,甚至是自己。NaN === NaN; // false

          ​​​​​​​​​​​​​​​​console.log(typeof NaN); ​​​​​​​​​​​​​​​​//number ​​​​​​​​​​​​​​​​console.log(0 / 0); ​​​​​​​​​​​​​​​​// NaN ​​​​​​​​​​​​​​​​console.log(Infinity - Infinity); ​​​​​​​​​​​​​​​​// NaN
    • 運算子 (Operators):+-*/%

    • 運算順序:

      • 有括號(),先運算
      • 先乘*/或取餘數%,後加+-

2. 字串(String)

  • JavaScript沒有char(字元)的概念,只有字串。
    • 使用""(雙引號)、''(單引號)或 `,將字串包起來。

      • 多行字串,可透過\(反斜線)繼續。但\後面不能有任何東西,包括空白字元。

        ​​​​​​​​​​​​console.log("Lie on the ocean"); ​​​​​​​​​​​​console.log('Float on the ocean'); ​​​​​​​​​​​​console.log(`Down on the sea`); //Backtick-quoted strings ​​​​​​​​​​​​console.log('這不是一行文 \ ​​​​​​​​​​​​ 這是第二行 \ ​​​​​​​​​​​​ 這是第三行');
    • 倘若要在單引號內包覆單引號,或是雙引號內包覆雙引號就會出現問題。

      • Ex: console.log('Let's go!'); //SyntaxError
      • 解決方法:
        • 更換引號 console.log("Let's go!");
        • 使用\(跳脫字元, escape character)處理 console.log('Let\'s go!');
    • 樣板字面值(Template literal):

      • 外部:由` `包裹。

      • 內部:由${}構成佔位符(${expression}),允許字串嵌入運算式並返回運算結果。

        ​​​​​​​​​​​​console.log(`half of 100 is ${100 / 2}`); ​​​​​​​​​​​​// half of 100 is 50
    • 運算子 (Operators):+

      • Ex: console.log("con" + "cat" + "e" + "nate"); //concatenate

3. 布林值 (Boolean value)

  • 布林值的值只有兩種:true 和 false,通常用於判斷式,作為控制程式流程的用途。

    • 比較運算子:><>=<===!====!==

      ​​​​​​​​console.log(3 > 2) // → true ​​​​​​​​console.log("a" < "Z") // → false ​​​​​​​​console.log("Aardvark" < "Zoroaster") // → true ​​​​​​​​console.log(NaN == NaN) // → false
      • 字串比大小:

        • 依據 Unicode 編碼表,將字串由左而右比大小。
          • 小寫字母"a" > 大寫字母"Z" > 非字母的字元("!", "-" )
        • NaN ("not a number") 表示無效計算的結果,它不等於任何其他無效計算的結果。
      • 嚴格相等(===) vs. 一般相等(==)

        • === (或稱 "三等於"、"全等"):
          • 不會將型別一致化後比較(若型別不同,就回傳 fasle)。
        • == (或稱 "雙等於"):
          • 將型別一致化後比較。
    • 邏輯運算子:&& (and)、|| (or)、! (not)

      ​​​​​​​​console.log(true && false); // → false ​​​​​​​​console.log(false || true); // → true ​​​​​​​​console.log(!false); // → true
    • Unary operators (一元運算子):只需要單個運算元,即可完成運算。

      • 如:typeof運算子,用於判斷型別種類。
      ​​​​​​​​console.log( -(-10) ); // → 10 ​​​​​​​​console.log(typeof 4.5); // → number ​​​​​​​​console.log(typeof "x"); // → string
    • Binary operators (二元運算子):需要兩個運算元,才可完成運算。

      ​​​​​​​​console.log( 10 - 2); // → 8
    • ternary operator (三元運算子):需要三個運算元,才可完成運算。

      • 條件運算子
        • 語法:condition ? exprIfTrue : exprIfFalse

        • 當條件符合,回傳中間的值(exprIfTrue),反之回傳右邊的值(exprIfFalse)。

          ​​​​​​​​​​​​​​​​console.log(true ? 1 : 2); ​​​​​​​​​​​​​​​​// → 1 ​​​​​​​​​​​​​​​​console.log(false ? 1 : 2); ​​​​​​​​​​​​​​​​// → 2

4. 空值 (Empty values)

  • Denote the absence of a meaningful value. They are themselves values, but they carry no information.

  • Two special values:

    1. undefined (未定義)

      • undefined,代表「(此變數) 尚未給值,所以不知道是什麼」。
      • 資料型別為 undefined。
        • console.log(typeof undefined); //undefined
      • 布林強制轉換:Boolean(undefined) //false
      ​​​​​​​​//當變數 a 被宣告時,在沒有給變數任何數值的情況下,變數的預設值是 undefined。 ​​​​​​​​var a; ​​​​​​​​console.log(typeof a); //undefined ​​​​​​​​ ​​​​​​​​//將變數 a 的值,設定為""。資料型別變為 string。 ​​​​​​​​var a = ""; ​​​​​​​​console.log(typeof a); //string
    2. null (沒有值)

      • null,代表「(此變數可能曾經有值,可能沒有值) 現在沒有值」。
      • 資料型別為 object。
        • console.log(typeof null); //object
      • 布林強制轉換:Boolean(null) //false
      ​​​​​​​​//當變數 b 被宣告時,則直接被明確地設定為 null。 ​​​​​​​​var b = null; ​​​​​​​​console.log(typeof b); //object ​​​​​​​​//將變數 b 的值,設定為 null。資料型別仍為 object。 ​​​​​​​​var b = {firstName:"John", lastName:"Doe"}; ​​​​​​​​b = null; ​​​​​​​​console.log(typeof b); //object

自動轉換型別

  • 當運算元的型別不同時,JavaScript 會自動依規則 type coercion (強制轉型)並運算。

    ​​​​console.log(8 * null); //null => 0 ​​​​// → 0 ​​​​console.log("5" - 1); //5 (from string to number) ​​​​// → 4 ​​​​console.log("5" + 1); //1 (from number to string) ​​​​// → 51 ​​​​console.log("five" * 2); ​​​​// → NaN ​​​​console.log(false == 0 && "" == false); ​​​​// → true ​​​​console.log(null == 0); ​​​​// → false
  • Three-character comparison operators

    • 避免 JavaScript 強制轉換型別
      • ===:嚴格相等

      • !==:嚴格不相等

        ​​​​​​​​​​​​console.log(null == undefined); //true ​​​​​​​​​​​​console.log(null === undefined); //false (型別不同,直接回傳 fasle)

Summary

  • JavaScript values 的四種型別(types):

    Such values are created by typing in their name (true, null) or value (13, "abc").

    1. numbers
    2. strings
    3. Booleans
    4. undefined values
  • Operators
    • Binary operators
      • arithmetic (+, -, *, /, %)
      • string concatenation (+)
      • comparison (==, !=, ===, !==, <, >, <=, >=)
      • logic (&&, ||)
    • Unary operators
      • - to negate a number
      • ! to negate logically
      • typeof to find a value’s type)
    • Ternary operator (?:) to pick one of two values based on a third value.

§ 程式結構

本節將學習JavaScript的語法結構。

運算式與敘述句

JavaScript是以;來判斷一段程式碼的結束。
有無縮排,在JavaScript並不影響。建議還是縮排,因為方便閱讀程式碼區塊。

  • Expression (運算式)
    • 產生值(value)的不完整程式碼片段。
    • Ex: !false"SIRLA"-( 2+3 )

      相當於一個句子的某部分片段。

  • Statement (敘述句)
    • 一段完整的程式碼,執行某個動作。
    • Ex: console.log( -( 2+3 ) );

      相當於一個完整的句子。

變數宣告

  • 用以方便抓取或是儲存值(value)。

    「弱型別」的JavaScript,變數本身不帶有資料型別的資訊,變數只用來作為取得值或物件的參考
    在執行時期,透過變數來參考至物件或值,才會得知此變數有什麼操作方法。

  • 用於宣告變數的保留字(keyword)

    • let
      • 宣告區域變數。

        ​​​​​​​​​​​​let a = 5 * 5; ​​​​​​​​​​​​console.log(a); // → 25 ​​​​​​​​​​​​let one = 1, two = 2; ​​​​​​​​​​​​console.log(one + two); // → 3 ​​​​​​​​​​​​let mood = "light"; ​​​​​​​​​​​​mood = "dark"; ​​​​​​​​​​​​console.log(mood); // → dark
      • 當參考到未給值的變數時,將得到undefined

        ​​​​​​​​​​​​let a; ​​​​​​​​​​​​console.log(a); // → undefined
    • var (short for “variable”)
      • ES5 的遺物,應避免使用var
      • 宣告全域變數。
    • const (constant)
      • 最常使用。

      • 宣告常數,不能再作修改。

      • 一經宣告,就永遠存在。

        ​​​​​​​​​​​​var name = "Ayda"; ​​​​​​​​​​​​const greeting = "Hello "; ​​​​​​​​​​​​console.log(greeting + name); // → Hello Ayda

變數名稱的命名

  • 可以是任何字母、數字,但開頭不可為數字。

    駝峰式命名:FuzzyLittleTurtlefuzzyLittleTurtle
    JavaScript 區分大小寫。

  • 可以包含$_,但不可有其他標點符號、特殊字元或空白(space)。
    • Ex:fuzzy_little_turtle
  • 不可為保留字 (keywords)。
    • 當你用保留字命名時,會出現syntax error。

      ​​​​​​​​break case catch class const continue debugger default ​​​​​​​​delete do else enum export extends false finally for ​​​​​​​​function if implements import interface in instanceof let ​​​​​​​​new package private protected public return static super ​​​​​​​​switch this throw true try typeof var void while with yield

註解

  • 單行註解://
  • 多行註解:/* */

執行 console.log 函式

  • 現今所有網頁瀏覽器和Node.js,皆可執行console.log函式於瀏覽器的控制台 (console)。
    • Windows 請按 F12

    • Mac 請按 command-option-I

      ​​​​​​​​let x = 30; ​​​​​​​​console.log("the value of x is", x); // → the value of x is 30

流程控制

  1. 直線型的流程:

    ​​​​let Num = Number(prompt("輸入數字:")); ​​​​console.log("平方後的結果:" + Num * Num);
  2. 條件型的流程:

    • if

      ​​​​​​​​let Num = Number(prompt("輸入數字:")); ​​​​​​​​if (!Number.isNaN(Num)) { ​​​​​​​​ console.log("平方後的結果:" + Num * Num); ​​​​​​​​}
      ​​​​​​​​if (1 + 1 == 2) console.log("It's true"); // → It's true
    • if ... else ...

      ​​​​​​​​let Num = Number(prompt("輸入數字:")); ​​​​​​​​ ​​​​​​​​if (!Number.isNaN(Num)) { ​​​​​​​​ console.log("平方後的結果:" + Num * Num); ​​​​​​​​} else { ​​​​​​​​ console.log("Hey. Why didn't you give me a number?"); ​​​​​​​​}
    • if ... else if ... else ...

      ​​​​​​​​let Num = Number(prompt("輸入數字:")); ​​​​​​​​ ​​​​​​​​if (Num < 10) { ​​​​​​​​ console.log("Small"); ​​​​​​​​} else if (Num < 100) { ​​​​​​​​ console.log("Medium"); ​​​​​​​​} else { ​​​​​​​​ console.log("Large"); ​​​​​​​​}
  3. 迴圈型(loop)的流程:

    • while

      • 符合條件後才做

        ​​​​​​​​​​​​let number = 0; ​​​​​​​​​​​​while (number < 8) { ​​​​​​​​​​​​ console.log(number); ​​​​​​​​​​​​ number = number + 2; ​​​​​​​​​​​​} ​​​​​​​​​​​​// → 0, 2, 4, 6
    • do ... while

      • 先做再說

        ​​​​​​​​​​​​let yourName; ​​​​​​​​​​​​do { ​​​​​​​​​​​​ yourName = prompt("Who are you?"); ​​​​​​​​​​​​} while (!yourName); ​​​​​​​​​​​​console.log(yourName);
    • for

      num = num + 2 相當於 num += 2
      counter = counter + 1 相當於 counter += 1 相當於 counter ++

      ​​​​​​​​for (let num = 0; num < 7; num = num + 2) { ​​​​​​​​ console.log(num); ​​​​​​​​} ​​​​​​​​// → 0, 2, 4, 6 ​​​​​​​​ ​​​​​​​​let result = 1; ​​​​​​​​for (let counter = 0; counter < 10; counter = counter + 1) { ​​​​​​​​ result = result * 2; ​​​​​​​​} ​​​​​​​​console.log(result); // → 1024
      • break:跳出迴圈。

        以下範例程式碼的for迴圈內,缺少終止條件的檢查。
        代表此迴圈將永不停止(無限迴圈),除非break statement被執行。

        ​​​​​​​​​​​​for (let current = 20; ; current ++) { ​​​​​​​​​​​​ if (current % 7 == 0) { ​​​​​​​​​​​​ console.log(current); ​​​​​​​​​​​​ break; ​​​​​​​​​​​​ } ​​​​​​​​​​​​} ​​​​​​​​​​​​// → 21
      • continue:跳出loop body,但仍在迴圈內繼續跑。

        ​​​​​​​​​​​​for (let current = 20; current < 30; current ++) { ​​​​​​​​​​​​ if (current % 2 == 0) { ​​​​​​​​​​​​ continue; ​​​​​​​​​​​​ } ​​​​​​​​​​​​ console.log(current); ​​​​​​​​​​​​} ​​​​​​​​​​​​// → 21, 23, 25, 27, 29
  4. 分派型(dispatch)的流程:

    • switch

      若case敘述內缺少break,switch將持續執行至含有break的case才停止。

      ​​​​​​​​switch (prompt("What is the weather like?")) { ​​​​​​​​ case "rainy": ​​​​​​​​ console.log("Remember to bring an umbrella."); ​​​​​​​​ break; ​​​​​​​​ ​​​​​​​​ case "sunny": ​​​​​​​​ console.log("Dress lightly."); ​​​​​​​​ ​​​​​​​​ case "cloudy": ​​​​​​​​ console.log("Go outside."); ​​​​​​​​ break; ​​​​​​​​ ​​​​​​​​ default: ​​​​​​​​ console.log("Unknown weather type!"); ​​​​​​​​ break; ​​​​​​​​} ​​​​​​​​ ​​​​​​​​Input: sunny ​​​​​​​​Output: Dress lightly. Go outside.

§ 函式

本節將學習JavaScript函式的語法。

  • 函式(function)
    • 物件(Object)的一種。
    • 將一或多段程式指令包裝起來,藉由呼叫來重複使用,也方便維護。
    • 一個函式會包含三個部分:
      1. 函式的名稱 (也可能沒有名稱)

      2. ( )中的部分,稱為「參數」。若有多個參數,則用,隔開。

      3. { }內的部分,放需要重複執行的運算。

        ​​​​​​​​​​​​function square(number) { ​​​​​​​​​​​​ return number * number; ​​​​​​​​​​​​} ​​​​​​​​​​​​square(2); // 4

定義與呼叫函式

  • 定義函式 (3種方式)

    透過return回傳結果。若無,則預設會回傳 undefined。

    1. 函式宣告

      ​​​​​​​​function 名稱([參數]) { ​​​​​​​​ ... ​​​​​​​​}
      ​​​​​​​​function square(x) { ​​​​​​​​ return x * x; ​​​​​​​​}
    2. 函式運算式

      • 匿名函式

        ​​​​​​​​​​​​變數名稱 = function ([參數]) { ​​​​​​​​​​​​ ... ​​​​​​​​​​​​}
        1. 無參數

          ​​​​​​​​​​​​​​​​const makeNoise = function() { ​​​​​​​​​​​​​​​​ console.log("Pling!"); ​​​​​​​​​​​​​​​​}; ​​​​​​​​​​​​​​​​makeNoise(); // → Pling! ​​​​​​​​​​​​​​​​ ​​​​​​​​​​​​​​​​console.log(typeof makeNoise); //function
        2. 有參數

          若有兩個以上的參數,則用,隔開。

          ​​​​​​​​​​​​​​​​const square = function(x) { ​​​​​​​​​​​​​​​​ return x * x; ​​​​​​​​​​​​​​​​};
    3. 箭頭函式

      • 當箭頭函式無參數時,()仍保留。

        ​​​​​​​​​​​​變數名稱 = ([參數]) => { ​​​​​​​​​​​​ ... ​​​​​​​​​​​​};
        ​​​​​​​​​​​​const power = (base, exponent) => { ​​​​​​​​​​​​ let result = 1; ​​​​​​​​​​​​ for (let count = 0; count < exponent; count++) { ​​​​​​​​​​​​ result *= base; ​​​​​​​​​​​​ } ​​​​​​​​​​​​ return result; ​​​​​​​​​​​​};
  • 在函式定義中,設定參數預設值

    ​​​​function power(base, exponent = 2) { ​​​​ let result = 1; ​​​​ for (let count = 0; count < exponent; count++) { ​​​​ result *= base; ​​​​ } ​​​​ return result; ​​​​} ​​​​console.log(power(4)); // → 16
  • 呼叫函式

    • 放在函式定義的前面或後面。

      因為JavaScript在編譯階段,自動將變數和函式定義先放入記憶體,
      再去執行函式呼叫。

    ​​​​名稱([參數]);
    ​​​​const square = function(x) { ​​​​ return x * x; ​​​​}; ​​​​square(3); //9
    • 若多給參數,則JavaScript會自動忽略多餘的參數,執行時不會出問題。

      ​​​​​​​​function square(x) { ​​​​​​​​ return x * x; ​​​​​​​​} ​​​​​​​​console.log(square(4, true, "sirla")); // → 16
    • 若少給參數,則預設缺少參數的值為undefined,執行時會出問題。

      ​​​​​​​​function minus(a, b) { ​​​​​​​​ if (b === undefined) return -a; ​​​​​​​​ else return a - b; ​​​​​​​​} ​​​​​​​​console.log(minus(10)); // → -10 ​​​​​​​​console.log(minus(10, 5)); // → 5

全域與區域

  • 變數作用的範圍,分為:

    1. 全域 (global):可在整份程式碼的任何地方使用。

      全域變數:var

    2. 區域 (local):只可在函式區塊內使用。

      區域變數:letconst

    ​​​​let x = 10; //這裡的x為global ​​​​if (true) { ​​​​ let y = 20; ​​​​ var z = 30; ​​​​ console.log(x + y + z); // → 60 ​​​​} ​​​​// y is not visible here ​​​​console.log(x + z); // → 40
    ​​​​const halve = function(n) { ​​​​ return n / 2; //這裡的n為local,與global的n之間無關係 ​​​​}; ​​​​let n = 10; //這裡的n為global ​​​​console.log(halve(100)); // → 50 ​​​​console.log(n); // → 10
  • 巢狀結構:

    • 函式區塊內,再包裹函式。

    • 外部函式區塊內的參數,可作用於內部函式。

      ​​​​​​​​const hummus = function(factor) { ​​​​​​​​ const ingredient = function(amount, unit, name) { ​​​​​​​​ let ingredientAmount = amount * factor; ​​​​​​​​ if (ingredientAmount > 1) { ​​​​​​​​ unit += "s"; ​​​​​​​​ } ​​​​​​​​ console.log(`${ingredientAmount} ${unit} ${name}`); ​​​​​​​​ }; ​​​​​​​​ ingredient(1, "can", "chickpeas"); ​​​​​​​​ ingredient(0.25, "cup", "lemon juice"); ​​​​​​​​ ingredient(2, "tablespoon", "olive oil"); ​​​​​​​​ ingredient(0.5, "teaspoon", "cumin"); ​​​​​​​​}; ​​​​​​​​console.log(hummus(2)); ​​​​​​​​Output: ​​​​​​​​ 2 cans chickpeas ​​​​​​​​ 0.5 cup lemon juice ​​​​​​​​ 4 tablespoons olive oil ​​​​​​​​ 1 teaspoon cumin

閉包(Closure)

  • 透過閉包讓 function 能夠有 private 變數。

    深入淺出瞭解 JavaScript 閉包(closure)

  • 不使用閉包(closure)的情況
    • 狗跟貓的計數函式,輸出結果混在一起了

      ​​​​​​​​// 狗的計數函式 ​​​​​​​​var count = 0 ​​​​​​​​function countDogs () { ​​​​​​​​ count += 1 ​​​​​​​​ console.log(count + ' dog(s)') ​​​​​​​​} ​​​​​​​​// 貓的計數函式 ​​​​​​​​var count = 0 ​​​​​​​​function countCats () { ​​​​​​​​ count += 1 ​​​​​​​​ console.log(count + ' cat(s)') ​​​​​​​​} ​​​​​​​​countCats() // 1 cat(s) ​​​​​​​​countCats() // 2 cat(s) ​​​​​​​​countDogs() // 3 dog(s) ​​​​​​​​countDogs() // 4 dog(s) ​​​​​​​​countDogs() // 5 dog(s) ​​​​​​​​countCats() // 6 cat(s)
  • 利用閉包(closure)的作法,讓函式有自己私有變數
    • 讓狗的計數函式只計算狗的,貓的計數函式只計算貓的。

    • 讓變數保留在該函式中而不會被外在環境干擾。

      ​​​​​​​​// 狗的計數程式 ​​​​​​​​function dogHouse () { ​​​​​​​​ var count = 0 ​​​​​​​​ function countDogs () { ​​​​​​​​ count += 1 ​​​​​​​​ console.log(count + ' dog(s)') ​​​​​​​​ } ​​​​​​​​ return countDogs ​​​​​​​​} ​​​​​​​​const countDogs = dogHouse() ​​​​​​​​// 貓的計數函式 ​​​​​​​​function catHouse () { ​​​​​​​​ var count = 0 ​​​​​​​​ function countCats () { ​​​​​​​​ count += 1 ​​​​​​​​ console.log(count + ' cat(s)') ​​​​​​​​ } ​​​​​​​​ return countCats ​​​​​​​​} ​​​​​​​​const countCats = catHouse() ​​​​​​​​countCats() // 1 cat(s) ​​​​​​​​countCats() // 2 cat(s) ​​​​​​​​countDogs() // 1 dog(s) ​​​​​​​​countDogs() // 2 dog(s) ​​​​​​​​countDogs() // 3 dog(s) ​​​​​​​​countCats() // 3 cat(s)
      • 進一步簡化程式,直接 return function

        ​​​​​​​​​​​​// 狗的計數程式 ​​​​​​​​​​​​function dogHouse () { ​​​​​​​​​​​​ var count = 0 ​​​​​​​​​​​​ return function () { ​​​​​​​​​​​​ count += 1 ​​​​​​​​​​​​ console.log(count + ' dog(s)') ​​​​​​​​​​​​ } ​​​​​​​​​​​​} ​​​​​​​​​​​​const countDogs = dogHouse() ​​​​​​​​​​​​// 貓的計數函式 ​​​​​​​​​​​​function catHouse () { ​​​​​​​​​​​​ var count = 0 ​​​​​​​​​​​​ return function () { ​​​​​​​​​​​​ count += 1 ​​​​​​​​​​​​ console.log(count + ' cat(s)') ​​​​​​​​​​​​ } ​​​​​​​​​​​​} ​​​​​​​​​​​​const countCats = catHouse() ​​​​​​​​​​​​countCats() // 1 cat(s) ​​​​​​​​​​​​countCats() // 2 cat(s) ​​​​​​​​​​​​countDogs() // 1 dog(s) ​​​​​​​​​​​​countDogs() // 2 dog(s) ​​​​​​​​​​​​countDogs() // 3 dog(s) ​​​​​​​​​​​​countCats() // 3 cat(s)
    • 甚至運用同一個 dogHouse時,變數間也都是獨立的執行環境不會干擾。

      ​​​​​​​​function dogHouse () { ​​​​​​​​ var count = 0 ​​​​​​​​ function countDogs () { ​​​​​​​​ count += 1 ​​​​​​​​ console.log(count + ' dog(s)') ​​​​​​​​ } ​​​​​​​​ return countDogs ​​​​​​​​} ​​​​​​​​var countGolden = dogHouse() ​​​​​​​​var countBlack = dogHouse() ​​​​​​​​var countPuppy = dogHouse() ​​​​​​​​countBlack() // 1 dog(s) ​​​​​​​​countBlack() // 2 dog(s) ​​​​​​​​countPuppy() // 1 dog(s) ​​​​​​​​countBlack() // 3 dog(s) ​​​​​​​​countGolden() // 1 dog(s) ​​​​​​​​countPuppy() // 2 dog(s) ​​​​​​​​countGolden() // 2 dog(s)

遞迴

  • 函式內不斷呼叫自己本身。

  • 缺點:

    • 以遞迴的方式執行程式,其速度比迴圈慢三倍。
    ​​​​function power(base, exponent) { ​​​​ if (exponent == 0) { ​​​​ return 1; ​​​​ } else { ​​​​ return base * power(base, exponent - 1); ​​​​ } ​​​​} ​​​​console.log(power(2, 3)); // → 8

函式進化論

function printFarmInventory(cows, chickens) { let cowString = String(cows); while (cowString.length < 3) { cowString = "0" + cowString; } console.log(`${cowString} Cows`); let chickenString = String(chickens); while (chickenString.length < 3) { chickenString = "0" + chickenString; } console.log(`${chickenString} Chickens`); } printFarmInventory(7, 11); Output: 007 Cows 011 Chickens
function printZeroPaddedWithLabel(number, label) { let numberString = String(number); while (numberString.length < 3) { numberString = "0" + numberString; } console.log(`${numberString} ${label}`); } function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Cows"); printZeroPaddedWithLabel(chickens, "Chickens"); printZeroPaddedWithLabel(pigs, "Pigs"); } printFarmInventory(7, 11, 3); Output: 007 Cows 011 Chickens 003 Pigs
function zeroPad(number, width) { let string = String(number); while (string.length < width) { string = "0" + string; } return string; } function printFarmInventory(cows, chickens, pigs) { console.log(`${zeroPad(cows, 3)} Cows`); console.log(`${zeroPad(chickens, 3)} Chickens`); console.log(`${zeroPad(pigs, 3)} Pigs`); } printFarmInventory(7, 16, 3); Output: 007 Cows 016 Chickens 003 Pigs

Summary

  • 定義並呼叫函式的寫法
    1. Declare g to be a function

      ​​​​​​​​function g(a, b) { ​​​​​​​​ return a * b * 3.5; ​​​​​​​​} ​​​​​​​​console.log(g(1, 2));
    2. Define f to hold a function value

      ​​​​​​​​const f = function(a) { ​​​​​​​​ console.log(a + 2); ​​​​​​​​}; ​​​​​​​​f(1);
    3. A less verbose function value

      ​​​​​​​​let h = a => a % 3; ​​​​​​​​console.log(h(2));
  • 範圍
    • 全域 (global):var
    • 區域 (local):letconst

Exercises

  1. Minimum

    ​​​​function min(a, b) { ​​​​ if (a < b){ ​​​​ return a; ​​​​ }else{ ​​​​ return b; ​​​​ } ​​​​} ​​​​console.log(min(0, 10)); // → 0 ​​​​console.log(min(0, -10)); // → -10
    ​​​​const min = function(a, b) { ​​​​ if (a < b){ ​​​​ return a; ​​​​ }else{ ​​​​ return b; ​​​​ } ​​​​};
    ​​​​let min = (a, b) => { ​​​​ if (a < b){ ​​​​ return a; ​​​​ }else{ ​​​​ return b; ​​​​ } ​​​​}
  2. Recursion

    ​​​​function isEven(n) { ​​​​ if (n == 0) return true; ​​​​ else if (n == 1) return false; ​​​​ else if (n < 0) return isEven(-n); ​​​​ else return isEven(n - 2); ​​​​} ​​​​console.log(isEven(50)); // → true ​​​​console.log(isEven(75)); // → false ​​​​console.log(isEven(-1)); // → false
  3. Bean counting

    ​​​​function countChar(string, ch) { ​​​​ let counted = 0; ​​​​ for (let i = 0; i < string.length; i++) { ​​​​ if (string[i] == ch) { ​​​​ counted += 1; ​​​​ } ​​​​ } ​​​​ return counted; ​​​​} ​​​​function countBs(string) { ​​​​ return countChar(string, "B"); ​​​​} ​​​​console.log(countBs("BBC")); // → 2 ​​​​console.log(countChar("kakkerlak", "k")); // → 4