--- type: slide --- # 函式 & ES6語法介紹 ###### tags: `HyUI4.0` --- # 函式 與 作用域 --- 一個函式會包含三個部分 * 函式的名稱 (也可能沒有名稱,稍後會提到) * 在括號 中的部分,稱為 參數,參數與參數之間會 用逗號 , 隔開 * 在大括號 { } 內的部分,內含需要重複執行的內容,是函式功能的主要區塊 --- ``` javascript= function square(number) { return number * number; } square(2); // 4 square(3); // 9 square(4); // 16 ``` --- 定義函式的方式 * 函式宣告(Function Declaration) * 函式運算式(Function Expressions) * 透過 new Function 關鍵字建立函式 --- 函式宣告/ 函式陳述式 (Function Declaration) ``` javascript= function 名稱([參數]) { // 做某事 } ``` note: 函式宣告 是屬最常見的用法 --- 函式運算式/函式表達式 (Function Expressions) --- ```javascript= var square = function (number) { return number * number; }; ``` 像這類沒有名字的函式在 JavaScript , 通常稱它為「匿名函式」 --- 透過 new Function 關鍵字建立函式 ```javascript= // 透過 new 來建立 Function "物件" var square = new Function('number', 'return number * number'); ``` note: 透過 new Function 所建立的函式物件,每次執行時都會進行解析「字串」(如 'return number * number' ) 的動作,運作效能較差,所以通常實務上也較少會這樣做。 --- 參考影片 3.1_function 的寫法 3.2_function 帶參數 --- 練習一下 [動手寫函式](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-javascript/write-reusable-javascript-with-functions) --- ### 變數的有效範圍 (Scope) --- 在 ES6 之前,JavaScript <br>變數有效範圍的最小單位 是以 function 做分界的 <!-- <small> ES6 之後有 let 與 const。<br> 與 var 不同的是,它們的 scope <br>是透過大括號 { } 來切分的。<br> ex:區域變數: if、else、for、while </small> --> --- 下列印出來會是?? ```javascript= var x = 1; var doSomeThing = function(y) { var x = 100; return x + y; }; console.log( doSomeThing(50) ); // ? console.log( x ); // ? ``` <span> 150<!-- .element: class="fragment" data-fragment-index="1" --> </span> <br/> <span> 1<!-- .element: class="fragment" data-fragment-index="2" --> </span> --- 如果 function 內部沒有 var x 呢? --- 自己的 function 內如果找不到, 就會一層層往外找,直到全域變數為止 ```javascript= var x = 1; var doSomeThing = function(y) { // 內部找不到 x 就會到外面找,直到全域變數為止。 // 都沒有就會報錯:ReferenceError: x is not defined return x + y; }; console.log( doSomeThing(50) ); ``` <span> 51<!-- .element: class="fragment" data-fragment-index="1" --> </span> <br/> --- ### 沒有 var 宣告的變數很危險! --- 下列印出來會是?? ```javascript= var x = 1; var doSomeThing = function(y) { x = 100; return x + y; }; console.log( doSomeThing(50) ); // ? console.log( x ); // ? ``` <span> 150<!-- .element: class="fragment" data-fragment-index="1" --> </span><br/> <span> 100<!-- .element: class="fragment" data-fragment-index="2" --> </span> note: 因為 function 內沒有重新宣告 x 變數,使得 x = 100 跑去變更了外層的同名變數 x --- 參考影片 3.3_全域與區域變數 --- ### 提升 (Hoisting) --- 變數和函數的宣告時 會在編譯階段中先被放入記憶體 --- 下列印出來會是?? ```javascript= var x = 1; var doSomeThing = function(y) { console.log(x); // 會出現什麼? var x = 100; return x + y; }; ``` <span> undefined<!-- .element: class="fragment" data-fragment-index="1" --> </span><br/> note: 雖然透過 var 對變數 x 來重新做宣告,但是呢,要是不小心在宣告前就使用了這個變數,這時候 JavaScript 就會開始尋找變數 x 了,在自己的 scope 找 雖然是在下面,但可以確認的是自己的 scope 裡面有宣告,於是就 很貼心地 「只會把宣告的語法」拉到這個 scope 的「最上面」 --- 瀏覽器 (或者編譯器) 的眼中 是長這樣 ```javascript [2|3|4] var doSomeThing = function(y) { var x; console.log(x); // undefined x = 100; return x + y; }; ``` --- JavaScript 的這種特性,稱作「變數提升」 強烈建議所有可能用到的變數 都盡量在 scope 的最上面先宣告 完成後再使用。 --- 除了變數以外,函式也有提升 --- 回憶一下 函式的定義有分 <span> 「函式運算式」 <!-- .element: class="fragment" data-fragment-index="1" --> ```javascript= var xxx = function() {...} ``` </span> <span> <!-- .element: class="fragment" data-fragment-index="2" --> 「函式宣告」 ```javascript= function xxx() {...} ``` </span> Note: 差別在於 函式宣告 提升 整個函式 而透過「函式運算式」只有提升宣告的變數 --- 函式宣告 ```javascript square(2); // 4 function square(number) { return number * number; } ``` --- 函式運算式 ```javascript square(2); // TypeError: square is not a function var square = function (number) { return number * number; }; ``` note: 函式宣告與函式運算式除了呼叫的時機不同外 執行上沒有明顯差異 為了避免錯誤 最好還是養成習慣 在不 --- 參考影片 3.4_hoisting 、var 觀念 --- # 重點回顧 --- * var宣告的變數有效範圍 (scope) 的最小切分單位是 function * 即使是寫在函式內,沒有 var 宣告的變數會變成「全域變數」 * 變數都盡量在scope的最上方做宣告 * 「函式宣告」會提升 整個函式 而透過「函式運算式」只有提升宣告的變數 --- # ES6中的新特性 --- 主要介紹有以下幾點 * let & const * 箭頭函式 * 樣板字面值 --- # let & const --- ## 常數與變數 在ES6 之前 只有定義變數 沒有定義常數 * 常數 const * 變數 let --- const 一旦被賦值後,就不能被更動 let 則可以 ```javascript= //這個a是不可變的(常數) const a = 10 //這行程式碼會發生錯誤: "a" is read-only(只能讀不能寫) a = 11 //這個b是可變的(變數) let b = 5 //b可以再改變其中的值 b = 6 ``` --- const在宣告時,就要給它值,否則會報錯 ```javascript= const v10; //Uncaught SyntaxError: Missing initializer //in const declaration ``` --- ## 變量提升(Hoisting) --- var在宣告前使用是可以的,不會報錯 ```javascript= console.log(v4); // undefined var v4 = 5 ; ``` --- 在 ES6 let 及 const 移除這個不直覺的現象,在宣告前不能使用 ```javascript= console.log(v4); //Uncaught ReferenceError: v4 is not defined at ES6.js:19 let v4 = 5 ; console.log(v5); //Uncaught ReferenceError: v5 is not defined at ES6.js:23 const v5 = 5 ; ``` --- ## 不允許重複宣告 --- 使用 var 即使重複宣告也不會報錯, 只會被後面宣告者覆蓋 ```javascript= var v6 = 5; var v6 = 7; console.log(v6); //7 ``` --- 使用 ES6移除這個不嚴謹的邏輯, 每個變數/常數在特定區塊下應該爲獨立的 ```javascript= let v7 = 5; let v7 = 7; console.log(v7); //Uncaught SyntaxError: Identifier 'v7' has already been declared const v8 = 5; const v8 = 7; console.log(v8); //Uncaught SyntaxError: Identifier 'v8' has already been declared ``` --- ## let 與 var 作用域 --- var 的變數範圍在 function, 而 let 的作用域在 block note: block 意指 {} 大括號),除了 function 以外 if、for 的 {} 都屬於 let 的作用域。 --- 用 var 宣告 ```javascript= function varMing () { var ming = '小明'; if (true) { var ming = '杰哥'; // 這裡的 ming 依然是外層的小明,所以小明即將被取代 } console.log(ming); // '杰哥' } varMing(); ``` --- 用 let 宣告 ```javascript= function letMing () { let ming = '小明'; if (true) { let ming = '杰哥'; // 這裡的 ming 是不同的,只有在這個 if block 才有作用 } console.log(ming); // '小明' } letMing(); ``` --- 參考影片 3.5_let - if、function 用法 3.6_let、const 注意事項與使用時機 --- ## 樣板字面值 --- 把過去的 “字串與變數” 做串接時, 都必須使用「加號」做連結。 當文字比較多或是需要換行時就難以閱讀 「樣板字面值」 就是用來解決這樣的問題 --- 傳統字串串接 ```javascript= const cash = 10; const string = '氣氣氣氣'; const sentence = '我的 ' + cash + ' 元掉到水溝裡了,真是' + string; console.log(sentence); // 我的 10 元掉到水溝裡了,真是氣氣氣氣 ``` --- 使用「樣板字面值」 1. 文字內容外圍使用「反引號」(ㄅ左邊那個按鍵)把字串頭尾包起來 3. 使用 $ { 「變數」 或 「表達式內容」 } 在 $ { } 內插入「變數」 --- ```javascript= const cash = 10; const string = '氣氣氣氣'; const sentence = `我的 ${ cash } 元掉到水溝裡了,真是${ string }`; console.log(sentence); // 我的 10 元掉到水溝裡了,真是氣氣氣氣 ``` --- 多行字串 --- 在普通的字串中, 我們需要使用如下的語法以達到換行的效果 (需要加上“\n” 來換行) ```javascript= console.log('string text line 1\n' + 'string text line 2'); // "string text line 1 // string text line 2" ``` --- 但使用樣板字面值,只需要撰寫 如下所示的程式碼,就能達到同樣的效果 (直接斷行) ```javascript= console.log(`string text line 1 string text line 2`); // "string text line 1 // string text line 2" ``` --- $ { } 內插入「表達式」 --- 使用 || 來設定「預設值」 當 “前面的值” 為 “假值 falsy” 時, 則使用「預設值」 --- ```javascript= const cash = 10; const string = ''; const sentence = `我的 ${cash} 元掉到水溝裡了,真是${ string ||'好生氣啊'}`; console.log(sentence); //我的 10 元掉到水溝裡了,真是好生氣啊 ``` --- 課程參考 3.7_樣板字面值(Template literals)基本介紹 --- # 箭頭函式 <small> arrow function)</small> --- ### 箭頭函式基本寫法 --- 首先 看一下原本寫函式的方法 ```javascript= function greeting() { console.log('Hello, JavaScript!!'); } greeting(); ``` --- ES6 之後,我們可以把它改成箭頭函式的寫法 ```javaascript= const greeting = () => { console.log('Hello, JavaScript!!'); }; greeting(); ``` --- 如果只有一行 可以省略 { } ```javascript= const greeting = () => console.log('Hello, JavaScript!!'); greeting(); ``` --- 如果我們的函式本身只是要回傳某個值的話, 可以把 return 這個字省略掉 ```javascript= const greeting = () => 'Hello, JavaScript!!'; console.log(greeting()); // 'Hello, JavaScript!!' // 等同於這樣寫 const greeting = function () { return 'Hello, JavaScript!!'; }; ``` --- 在大括號內的 { } 是需要自行加入 return, 如果沒有傳入值則會出現 undefined ```javascript= var callSomeone = (someone) => { someone + '吃飯了' } console.log(callSomeone('小明')) // undefined ``` --- ### 箭頭函式帶入參數值 --- 如果有兩個以上參數,需要使用括號 ```javascript= const add = (a, b) => a + b; console.log(add(3, 5)); // 等同於這樣寫 const add = function (a, b) { return a + b; }; ``` --- 當函式只有一個參數時,不需要使用括號 ```javascript= const greeting = person => `Hello, ${person}`; greeting('Aaron'); // Hello, Aaron // 等同於這樣寫 const greeting = function (person) { return `Hello, ${person}`; }; ``` --- 當函式沒有參數時,則一定要有括號 ```javascript= var callSomeone = () => '小明' + '吃飯了' console.log(callSomeone()) ``` --- 參考影片 3.8_箭頭函式簡介 --- # 重點回顧 --- ### let / var / const 1. const 一旦被賦值後,就不能被更動 let 則可以。 1. const 在宣告時,就要給它值,否則會報錯。 1. var 在宣告前使用是可以的,不會報錯,但是 let 和 const 則不行。 1. var 即使重複宣告也不會報錯,但是 let 和 const 則不行。 1. var 的變數範圍在 function,而 let 的作用域在 block。 --- | 宣告方式 | var | let | const | | ---------- | ------ | ---- | ----- | | 類型 | 沒有分 | 變數 | 常數 | | 宣告給值 | 不用 | 不用 | 要給值 | | 宣告後變更值 | 可以 | 可以 | 不行 | | 重複宣告 | 可以 | 不行 | 不行 | | 作用域範圍 | function | block | block | | 宣告前使用 | 可以 | 不行 | 不行 | --- ### 樣板字面值 1. 樣板字面值,文字內容外圍使用「反引號」,在 $ { } 內插入「變數」。 1. 樣板字面值支援多行字串,可以直接斷行。 1. 樣板字面值可以在 $ { } 內插入「表達式」,使用 || 來設定「預設值」。 --- ### 箭頭函式 1. 當箭頭函式如果只有一行 可以省略 { } 1. 當箭頭函式如果只有一行 可以把 return 這個字省略掉 1. 但如果在大括號內的 { } 是需要自行加入 return,如果沒有傳入值則會出現 undefined 3. 如果只有帶一個參數 可以省略() 4. 沒有參數時則一定要有括號 --- ## 參考資料 [函式 Functions 的基本概念](https://ithelp.ithome.com.tw/articles/10191549) [從ES6開始的JavaScript學習生活](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/var_const_naming.html) [MDN樣板字面值](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Template_literals) [var 與 ES6 let const 差異](https://ithelp.ithome.com.tw/articles/10209121) [ES6 章節:Template Literial](https://medium.com/vicky-notes/es6-%E7%AB%A0%E7%AF%80-template-literial-7309b9e3bebb) [箭頭函式(arrow function)和它對 this 的影響](https://pjchender.dev/javascript/js-arrow-function/) --- [控制判斷](https://hackmd.io/EwAG4WaqRy-t1Me5ySmv-g#/) --- <style> .reveal h1{ font-size:2em; } .reveal h1,.reveal h2 { color:#c9f2ff; } .reveal{ font-size:26px; } </style>