--- title: Astro課程 0805 - JavaScript (Day1) tags: astro, javascript --- # Part 1: 程式基礎,以JavaSript為例 看自己本地環境的`node js`版本 `node --version` ```  master  node --version v14.6.0 ``` 不同的javascript引擎,語法跑出來的結果不太相同 不同的js版本,LTS (長期支援 Long-term support)可能不一樣 [Production applications should only use Active LTS or Maintenance LTS releases](https://nodejs.org/en/about/releases/) ```javascript y = `test` // string interpolation ``` ```javascript let x = 10; // 第一次出現時宣告 // 三種字串宣告方式:單引號、雙引號、反引號 let y = '100'; let a = ""; // grave, backtick 這個在js和其他程式語言的功能不一樣 let b = `test`; let z = x + y; console.log(z); let userName = "John"; let greeting = "Hello, " + userName + ", how are you?"; // string interpolation let greeting2 = `Hello, ${userName}, Nice to meet you. how are you?`; console.log(greeting) console.log(greeting2) ``` # 全域變數 / 區域變數 ## scope 變數作用域 以前版本的`javascript`只有: - global scope 全域變數 - function scope 函式內區域變數 ruby 還有 block scope ## 以Ruby舉例 ```ruby def foo x = 100 end puts x # 會出錯 # hello-ruby.rb:5:in `<main>': undefined local variable or method `x' for main:Object (NameError) ``` 拿到x的方法 ```ruby def foo x = 100 puts x end foo() # 100 ``` scope的概念 ```ruby def foo x = 100 puts x end x = 1000 puts x # 1000 ``` ## 以js舉例 let 放在function內(會出錯) ```javascript function foo() { let x1 = 100; // ReferenceError: x1 is not defined at Object.<anonymous> (/Users/tingtinghsu/Documents/projects/astro_js/Day1/hello-foo.js:6:13) } foo(); console.log(x1); ``` ```javascript let x1 = 100; function foo() { //let x1 = 100; } foo(); console.log(x1); //=> 100 ``` 如果放在function內但不宣告 => 變成全域變數 ```javascript function foo() { x1 = 100; } foo(); console.log(x1); //=> 100 ``` 行為良好的程式語言是否該取得`i`? ```javascript for (var i = 0; i < 10; i++) { } console.log(i); // => 會印出10 ``` 為了不要讓`i`被取得,使用`let` (超過可以使用的範圍再取用時就會噴錯) ```javascript for (let i = 0; i < 10; i++) { } console.log(i); ReferenceError: i is not defined at Object.<anonymous> (/Users/tingtinghsu/Documents/projects/astro_js/Day1/for.js:4:13) ``` let是新的語法 (rails webpacker: 拿到js的新語法,翻譯成大部分瀏覽器可以使用的舊語法) 比起`var`,用`let`的好處 ```javascript function bar(x) { for (var j = 0; j < x; j++) { // 1. 拿得到 j } // 2. 拿得到 j } // 3. 拿不到 j ``` ```javascript function bar(x) { for (let j = 0; j < x; j++) { // 1. 拿得到 j } // 2. 拿不到 j } // 3. 拿不到 j ``` # 變數提升:把變數的宣告提升到檔案的最上層 ```javascript var userName1 = "Johnny Doe" console.log(userName1); // Johnny Doe ``` 兩句交換,後為什麼會印出`undefined`? ```javascript console.log(userName); var userName = "John Doe" // undefined ``` ```javascript console.log(userName); var userName = "John Doe" // //javascript直譯器跑完檔案時,會把上面那句話拆成兩層, //然後`var userName`會被提升到檔案最上層 var userName userName = "John Doe" ``` # Function 函式 回傳值`return`和`console.log`的區別 ```javascript function greeting(name) { // say a friendly hi console.log("Hello world! This is" + name) // 印出Hello world! This istai return `name: ${name}` //回傳 name: tai } let ret = greeting('tai') console.log(ret); ``` 跟`ruby`的語法不同,如果沒有`return`,就不會有回傳值 ```javascript function greeting1(name) { console.log("Hello world! This is" + name)` } let result = greeting1("Mary") console.log(result) // undefined ``` ruby ```ruby def bar(x) if(x < 100) { return 100 } else { 300 # return 300 } end ``` 呼叫函式`call`, `invoke`時一定要加括弧 `foo()` ```javascript function greeting2(name) { // say a friendly hi console.log("Hello world! This is" + name) console.log("function ends") // 偵測到無法執行的程式碼 return // 自以為有分號,結果變成沒有回傳值 `name: ${name}` } let result2 = greeting2("Mary") console.log(result2) // undefined ``` ## method 和 function 有什麼不同? Ruby: 如果一個function隸屬於一個物件,就是一個method 依照ruby的method規則,以下會出錯 ```ruby p [1, 2, 3, 4, 5, 6].map def i i + 1 end ``` 但是在JS裡,函式是自由的 以下是沒有名字的函式 ```javascript let res8 = [1, 2, 3, 4, 5].map(function(i){ return i + 1}) console.log(res8) #=> [ 2, 3, 4, 5, 6 ] ``` 把函式提出來 (JS函式的good part) ```javascript function add1(i){ return i + 1} let res8 = [1, 2, 3, 4, 5].map(add1) console.log(res8) ``` # `==` 與 `===` `==` 會自動幫忙轉型 比較時,盡量使用嚴格比較`===` ```javascript let numberOne = 1 let stringOne = "1" console.log(numberOne == stringOne) // true console.log(numberOne === stringOne) // false ``` (桃紅色的區域會發生很多奇怪的事!) ![](https://i.imgur.com/pRdbxjI.png) # if-else 計算車資 ```javascript let cartTotal = 1000; let ratio; function getRatio(total) { if(total > 1000) { return 0.8; } else if (total > 500) { return 0.9; } else { return 1; } } console.log(cartTotal * getRatio(cartTotal)) ``` # For迴圈 ```javascript let array = [1, 2, 3, 4, 5, 6]; let res50 = [0, 0, ...array, 7, 8, 9] res50[0] = 100; // 彈性,瑣碎:開頭可以調整,跳的走也可以 eg i +=2 for(var i = 0; i < res50.length; i ++) { let result = res50[i] + 1; console.log('I got a number: ', result) // => I got a number: 101 } console.log(i) // => 印出11, 最後的檢查會再做一次,但是發現條件不會符合 ``` console ``` I got a number: 101 I got a number: 1 I got a number: 2 I got a number: 3 I got a number: 4 I got a number: 5 I got a number: 6 I got a number: 7 I got a number: 8 I got a number: 9 I got a number: 10 11 ``` 像ruby版本寫法的迴圈 ```javascript for(let x of res50){ console.log(x) // 走的過程不會去亂動陣列本身 } ``` # case 條件判斷 ```javascript let gender = 'F', title; switch(gender) { case 'M': title = 'Mr.'; break; case 'F': title = 'Ms.'; break; default: title = ''; } console.log(title); ``` # 集合 Collection 1. `[]` ```javascript let array = [1, 2, 3, 4, 5, 6]; let res2 = array; console.log(res2); // => [ 1, 2, 3, 4, 5, 6 ] let res5 = array.pop(); console.log(res5); // => 6 let res8 = array.slice(1,4); console.log(res8); // => [2, 3, 4] // immutible: 不可被修改的 // push pop slice 這些函式會動到陣列本身 // functional派:不要動到array本身 let res12 = [...array, 7 ,8 ,9] console.log(res12); // => [[ 1, 2, 3, 4, 5, 7, 8, 9] ``` 2. `{}`:鍵值對:像是字典的東西 值可以重複,鍵不行 在JavaScript叫做Object Object的兩種用法: 1. 查表 2. 表達物件的狀態 ```javascript let areaCode = { // 查表 'us': '01', 'tw': '886', 'hk': '86', } let student = { // 表達物件狀態 'name': 'John', 'age': 18, 'gender': 'M', 'favorite': ['music', 'eat'], } ``` ```javascript let areaCode = { 'us': '01', 'tw': '886', 'hk': '86' } let country = 'hk' console.log(areaCode[country]) // => 86 ``` 取值與設值的規則: 如果是為了拿到特定的結果,使用`[]`,其他時候用`.` ```javascript let areaCode = { 'us': '01', 'tw': '886', 'hk': '86' } let student = { 'name': 'John', 'age': 18, 'gender': 'M', 'favorite': ['music', 'eat'] } let country = 'hk' console.log(areaCode[country]) //=> 為了取出`86`這個值 console.log(student.age) ``` # 手動表現出變數提升 ```javascript function baz(x){ let amount, total, qty; //先寫出來放著 //do something amount = 100; } ``` # 讓整個函數提升 用`function baz(x)`這種方式定義`function` 把不重要的小函式藏在底下 ```javascript baz(1000) function baz(x){ var amount, total, qty; //do something amount = 100; console.log(amount + x) } ``` # 另外兩種定義函式的方式 1. function指定給變數 ```javascript putInPot('beef') // beef in pot function putInPot(x) { console.log(`${x} in pot`) } // function指定給變數 let lalala = function (x) { console.log(`${x} in pot`) } ``` 2. 箭頭函式 (ES6) ```javascript let addOne = function(x) {return x + 1} let addOne2 = x => x + 1 // 前面是參數,後面是回傳值 // 第一步:把function拿掉 let addOne = (x) => { return x + 1 } // 第二步:把{}和return拿掉 let addOne = (x) => x + 1 // 第三步:如果參數只有一個,()可以拿掉 let addOne = x => x + 1 ``` 如果參數有兩個,不能走第三步(不能把括弧拿掉) ```javascript let addOne = (x, y) => x + y + 1 ``` # 高階函式 處理函式的函式 原始的寫法長這樣: ```javascript putInPot('beef'); putInPot('water'); console.log('I make dinner with beef') boomboom('chicken') boomboom('coconut'); console.log('I make dinner with chicken') function putInPot(x) { console.log(`${x} in pot`) } function boomboom(x) { console.log(`cooking ${x}`) } ``` ```javascript beef in pot water in pot I make dinner with beef cooking chicken cooking coconut I make dinner with chicken ``` 把pattern找出來 ```javascript // cook('第一個食材'、'第二個食材','callback function': 這邊人家會透過參數丟一個函式進來,自己決定什麼時候要呼叫 cook('beef', 'water', putInPot); cook('chicken', 'coconut', boomboom); // 高階函式:處理函式的函式 function cook(i1, i2, f) { f(i1) f(i2) console.log(`I make dinner with ${i1}`) } ``` ```javascript cook('beef', 'water', x => console.log(`${x} in pot`)) cook('beef', 'water', x => console.log(`cooking ${x}`)) // 函數是自由的,console log也是一個function,所以可以提到外面來 let pp = console.log cook('beef', 'water', x => pp(`${x} in pot`)) cook('beef', 'water', x => pp(`cooking ${x}`)) ``` 下堂課前情提要: 用函式產生函式:呼叫高階函式的時候,直接把函式定義在後面 ``` [1, 2, 3, 4, 5].map(x => x + 1) [1, 2, 3, 4, 5].select(x => x > 3) [1, 2, 3, 4, 5].reduce(x, acc => x + acc) let curriedAdd = x => y => z => x + y + z ``` eg. 雖然不知道什麼時候會發生,但某個事件觸發的時候,會呼叫後面的函式 ```htmlmixed <body> <button id="btn">Don't Click</button> <script> document .querySelector('#btn') .addEventListener('click', () => { for (let i = 0; i < 5; i++) { alert('Hey!!!! WTF') } alert('OK..') }) </script> </body> ```