--- title: JavaScript入門 tags: sirla, class, JavaScript description: 1. title 請改為 [授課名稱]課程 2. tag 請刪去template,加上活動內容類型或名稱 3. 下方會議記錄請使用會議記錄範本 4. 加上"{%hackmd BkVfcTxlQ %}"意為套用黑色模板 --- {%hackmd BkVfcTxlQ %} # **_JavaScript入門_** > 負責人:黃丰嘉 > 授課時間:2019-09-15 (日) > --- # **參考資源** > 1. [What’s the difference between JavaScript and ECMAScript?](https://www.freecodecamp.org/news/whats-the-difference-between-javascript-and-ecmascript-cba48c73a2b5/) > 2. [JavaScript Numbers - w3schools](https://www.w3schools.com/js/js_numbers.asp) > 3. [重新認識 JavaScript: Day 03 變數與資料型別](https://ithelp.ithome.com.tw/articles/10190873) --- # **課程大綱** [TOC] --- ## (P) **什麼是 JavaScript?** * JavaScript 是瀏覽器唯一指定的內建程式語言。 * JavaScript 跟 Java 毫無關係! * JavaScript 只是搭上當時正火紅的 Java 這股熱潮,而命名的。 * 應用範圍: * 網頁瀏覽器 * MongoDB 和 CouchDB 使用 JavaScript 作為腳本語言及查詢語言。 * Node.js:運行於伺服器端的 JavaScript。 ### JavaScript 歷史 * 1995年,JavaScript誕生,由 Netscape (網景公司,當時知名瀏覽器廠商) 著手開發一門在瀏覽器上執行的語言系統。 * 當時為了處理網頁表單的簡單驗證。 * 隨之,Netscape以外的瀏覽器也漸漸採用,亦在1997年建立了統一標準 `ECMAScript standard`。 ### JavaScript 版本 > [JavaScript Versions](https://www.w3schools.com/js/js_versions.asp) > | 版本 | 正式名稱 | 備註 | | --- | --- | --- | | 1 | ECMAScript 1 (1997) | First Edition. | | 3 | ECMAScript 3 (1999) | 廣泛支援 (2000 - 2010) | | 5 | ECMAScript 5 (2009) | JS ES5 | | 6 | ECMAScript 2015 | JS ES6 添加`let`、`const`、可以預設參數的值 | | 8 | ECMAScript 2017 | 添加 Object | | 9 | ECMAScript 2018 | 最新版 | --- ## (P) **值、型別與運算子** > 本節介紹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) | * 不管整數或帶有小數點的數字,還是科學記號表示都是。 ```javascript= 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`:正無限大。 ```javascript= 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` ```javascript= console.log(typeof NaN); //number console.log(0 / 0); // NaN console.log(Infinity - Infinity); // NaN ``` * 運算子 (Operators):`+`、`-`、`*`、`/`、`%` * 運算順序: * 有括號`()`,先運算 * 先乘`*`除`/`或取餘數`%`,後加`+`減`-` ### 2. 字串(String) * JavaScript沒有char(字元)的概念,只有字串。 * 使用`""`(雙引號)、`''`(單引號)或 ``` ` ```,將字串包起來。 * 多行字串,可透過`\`(反斜線)繼續。但`\`後面不能有任何東西,包括空白字元。 ```javascript= 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}`),允許字串嵌入運算式並返回運算結果。 ```javascript= 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,通常用於判斷式,作為控制程式流程的用途。 * 比較運算子:`>`、`<`、`>=`、`<=`、`==`、`!=`、`===`、`!==` ```javascript= 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) ```javascript= console.log(true && false); // → false console.log(false || true); // → true console.log(!false); // → true ``` :::success * Unary operators (一元運算子):只需要單個運算元,即可完成運算。 * 如:`typeof`運算子,用於判斷型別種類。 ```javascript= console.log( -(-10) ); // → 10 console.log(typeof 4.5); // → number console.log(typeof "x"); // → string ``` * Binary operators (二元運算子):需要兩個運算元,才可完成運算。 ```javascript= console.log( 10 - 2); // → 8 ``` * ternary operator (三元運算子):需要三個運算元,才可完成運算。 * 條件運算子 * 語法:`condition ? exprIfTrue : exprIfFalse` * 當條件符合,回傳中間的值(exprIfTrue),反之回傳右邊的值(exprIfFalse)。 ```javascript= 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` ```javascript= //當變數 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` ```javascript= //當變數 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 (強制轉型)並運算。 ```javascript= 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 強制轉換型別 * `===`:嚴格相等 * `!==`:嚴格不相等 ```javascript= console.log(null == undefined); //true console.log(null === undefined); //false (型別不同,直接回傳 fasle) ``` ### Summary :::info * 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. ::: --- ## (P) **程式結構** > 本節將學習JavaScript的語法結構。 ### 運算式與敘述句 > JavaScript是以`;`來判斷一段程式碼的結束。 > 有無縮排,在JavaScript並不影響。建議還是縮排,因為方便閱讀程式碼區塊。 * `Expression` (運算式) * 產生值(value)的`不完整`程式碼片段。 * Ex: `!false`、`"SIRLA"`、`-( 2+3 )` > 相當於一個句子的某部分片段。 * `Statement` (敘述句) * 一段`完整`的程式碼,執行某個動作。 * Ex: `console.log( -( 2+3 ) );` > 相當於一個完整的句子。 ### 變數宣告 * 用以方便抓取或是儲存值(value)。 > 「弱型別」的JavaScript,變數本身不帶有資料型別的資訊,變數只用來作為取得值或物件的`參考`。 > 在執行時期,透過變數來`參考`至物件或值,才會得知此變數有什麼操作方法。 * 用於宣告變數的保留字(keyword) * `let` * 宣告區域變數。 ```javascript= 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`。 ```javascript= let a; console.log(a); // → undefined ``` * `var` (short for “variable”) * ES5 的遺物,應避免使用`var`。 * 宣告全域變數。 * `const` (constant) * 最常使用。 * 宣告常數,不能再作修改。 * 一經宣告,就永遠存在。 ```javascript= var name = "Ayda"; const greeting = "Hello "; console.log(greeting + name); // → Hello Ayda ``` ### 變數名稱的命名 * 可以是任何字母、數字,但開頭不可為數字。 > 駝峰式命名:`FuzzyLittleTurtle`、`fuzzyLittleTurtle`。 > JavaScript 區分大小寫。 * 可以包含`$`、`_`,但不可有其他標點符號、特殊字元或空白(space)。 * Ex:`fuzzy_little_turtle` * 不可為`保留字` (keywords)。 * 當你用`保留字`命名時,會出現syntax error。 ```javascript= 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` ```javascript= let x = 30; console.log("the value of x is", x); // → the value of x is 30 ``` ### 流程控制 1. 直線型的流程: ```javascript= let Num = Number(prompt("輸入數字:")); console.log("平方後的結果:" + Num * Num); ``` 2. 條件型的流程: * `if` ```javascript= let Num = Number(prompt("輸入數字:")); if (!Number.isNaN(Num)) { console.log("平方後的結果:" + Num * Num); } ``` ```javascript= if (1 + 1 == 2) console.log("It's true"); // → It's true ``` * `if ... else ...` ```javascript= 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 ...` ```javascript= let Num = Number(prompt("輸入數字:")); if (Num < 10) { console.log("Small"); } else if (Num < 100) { console.log("Medium"); } else { console.log("Large"); } ``` 3. 迴圈型(loop)的流程: * `while` * 符合條件後才做 ```javascript= let number = 0; while (number < 8) { console.log(number); number = number + 2; } // → 0, 2, 4, 6 ``` * `do ... while` * 先做再說 ```javascript= 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 ++` ```javascript= 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`被執行。 ```javascript= for (let current = 20; ; current ++) { if (current % 7 == 0) { console.log(current); break; } } // → 21 ``` * `continue`:跳出`loop body`,但仍在迴圈內繼續跑。 ```javascript= 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才停止。 ```javascript= 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. ``` --- ## (P) **函式** > 本節將學習JavaScript函式的語法。 * 函式(function) * 是`物件(Object)`的一種。 * 將一或多段程式指令包裝起來,藉由呼叫來重複使用,也方便維護。 * 一個函式會包含三個部分: 1. 函式的名稱 (也可能沒有名稱) 2. `( )`中的部分,稱為「參數」。若有多個參數,則用`,`隔開。 3. `{ }`內的部分,放需要重複執行的運算。 ```javascript= function square(number) { return number * number; } square(2); // 4 ``` ### 定義與呼叫函式 * 定義函式 (3種方式) > 透過`return`回傳結果。若無,則預設會回傳 undefined。 1. 函式宣告 ```javascript= function 名稱([參數]) { ... } ``` ```javascript= function square(x) { return x * x; } ``` 2. 函式運算式 * 匿名函式 ```javascript= 變數名稱 = function ([參數]) { ... } ``` 1. 無參數 ```javascript= const makeNoise = function() { console.log("Pling!"); }; makeNoise(); // → Pling! console.log(typeof makeNoise); //function ``` 2. 有參數 > 若有兩個以上的參數,則用`,`隔開。 ```javascript= const square = function(x) { return x * x; }; ``` 3. 箭頭函式 * 當箭頭函式無參數時,`()`仍保留。 ```javascript= 變數名稱 = ([參數]) => { ... }; ``` ```javascript= const power = (base, exponent) => { let result = 1; for (let count = 0; count < exponent; count++) { result *= base; } return result; }; ``` * 在函式定義中,設定參數預設值 ```javascript= 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在編譯階段,自動將變數和函式定義先放入記憶體, > 再去執行函式呼叫。 ```javascript= 名稱([參數]); ``` ```javascript= const square = function(x) { return x * x; }; square(3); //9 ``` * 若多給參數,則JavaScript會自動忽略多餘的參數,執行時不會出問題。 ```javascript= function square(x) { return x * x; } console.log(square(4, true, "sirla")); // → 16 ``` * 若少給參數,則預設缺少參數的值為undefined,執行時會出問題。 ```javascript= 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):只可在函式`區塊內`使用。 > 區域變數:`let`、`const` ```javascript= 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 ``` ```javascript= 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 ``` * 巢狀結構: * 函式區塊內,再包裹函式。 * 外部函式區塊內的參數,可作用於內部函式。 ```javascript= 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)](https://pjchender.blogspot.com/2017/05/javascript-closure.html) * 不使用閉包(closure)的情況 * 狗跟貓的計數函式,輸出結果混在一起了 ```javascript= // 狗的計數函式 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)的作法,讓函式有自己私有變數 * 讓狗的計數函式只計算狗的,貓的計數函式只計算貓的。 * 讓變數保留在該函式中而不會被外在環境干擾。 ```javascript= // 狗的計數程式 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 ```javascript= // 狗的計數程式 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`時,變數間也都是獨立的執行環境不會干擾。 ```javascript= 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) ``` ### 遞迴 * 函式內不斷呼叫自己本身。 * 缺點: * 以遞迴的方式執行程式,其速度比迴圈慢三倍。 ```javascript= function power(base, exponent) { if (exponent == 0) { return 1; } else { return base * power(base, exponent - 1); } } console.log(power(2, 3)); // → 8 ``` ### 函式進化論 ```javascript= 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 ``` ```javascript= 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 ``` ```javascript= 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 ```javascript= function g(a, b) { return a * b * 3.5; } console.log(g(1, 2)); ``` 2. Define f to hold a function value ```javascript= const f = function(a) { console.log(a + 2); }; f(1); ``` 3. A less verbose function value ```javascript= let h = a => a % 3; console.log(h(2)); ``` * 範圍 * 全域 (global):`var` * 區域 (local):`let`、`const` ### Exercises 1. Minimum ```javascript= function min(a, b) { if (a < b){ return a; }else{ return b; } } console.log(min(0, 10)); // → 0 console.log(min(0, -10)); // → -10 ``` ```javascript= const min = function(a, b) { if (a < b){ return a; }else{ return b; } }; ``` ```javascript= let min = (a, b) => { if (a < b){ return a; }else{ return b; } } ``` 2. Recursion ```javascript= 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 ```javascript= 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 ```