0%

JS核心-函式

函式

函式是一個物件,所以,它也可以擁有自己的屬性。
它可以被存放在變數、陣列中。

函式運算式-有括號和沒括號的差別

1
2
3
4
5
6
7
function inner() {
return 'Hello';
}
let a = inner; // 沒有加括號
let b = inner(); // 有加括號
console.log(a, b);
console.log(a());

從console.log的結果a是直接印出整個inner函式內容,而b是印出inner回傳的內容’hello’。
那最後那一行,a()就可以執行inner函式,故會回傳內容’hello’。
要小心它們之間的不同喔~~~~

ES6箭頭函式

這種函式有以下的特性:
this變數的強制綁定
.簡短的撰寫方式

接下來,筆記一下箭頭函式可以簡寫的一些特性:

可以忽略return的特性

原本的函式

1
2
3
const plus = function(numA, numB) {
return numA + numB;
}

像上面這個範例,只有需要return計算的結果的話,除了箭頭函式的內容以外,我們還可以把return忽略。
所以,就會變成下面這樣:

1
const plus = (numA, numB) => numA + numB;

若箭頭函式中的括號中沒有參數是不行忽略的括號的

1
2
const sayhello = () => console.log('hello');
sayhello();

若箭頭函式中的括號只有一個參數的話,可以忽略括號

1
2
const sayhello(msg) => console.log(msg);
sayhello('hello');

arguments 物件

在函式被呼叫的時候,會產生arguments物件,而這個argument物件就會包含了我們所傳入的參數內容。
這個arguments物件是類似於物件的東西,既然是類似,所以,它就不會有陣列才擁有的一些方法,但是,利用迴圈來遍歷這個類陣列的arguments物件,
還是可以的喔。
所以,當我們不確定該函式會傳入多少個參數的時候,不管是少或多,我們都能透過arguments來得到這些傳入的參數。

1
2
3
4
5
6
7
8
const plus = function(){
let num = 0;
for(let i=0; i!=arguments; i++) {
num += arguments[i];
}
return num;
}
plus(1, 2); // 3

以上這個範例我們可以看到,就算在定義plus函數的時候,並沒有規定要傳入參數,但是,我們刻意傳入參數,最終,還是可以透過arguments來取得這些傳入的內容喔。

執行遞迴 - callee

我們可以利用arguments的一個屬性叫callee,它會回傳目前正在執行的函式。
如此,我們就可以透過它在該函式執行時,在其內部再利用callee再次呼叫正在執行的函式,形成遞迴的效果。
但這邊要特別注意,前面介紹過的ES6箭頭函式是不能呼叫arguments物件的喔。

箭頭函式使用其餘參數

雖然我們沒辦法在箭頭函式的情境下使用arguments物件,但是,我們可以使用其餘參數來達到一樣的效果。
那這邊其餘參數跟arguments物件比較不同的是,其餘參數是一個真正的陣列,所以,我們可以對它使用陣列才有的方法。

1
2
3
4
5
6
7
8
const plus = (...argu) => {
let num = 0;
for(let i=0; i!=argu.length; i++) {
num += argu[i];
}
return num;
}
plus(1, 2);

將以上例子改寫成直接使用陣列的方法

1
2
3
const plus = (...argu) => argu.reduce((a, b) => a + b);

plus(1, 2, 3, 4); // 10

CallBack Function

這種形式的函式的特性就是,當滿足某種條件時,此函式才會被被動的執行,我們就可以稱該函式為callback function。
那我們很常會在addEventListener的狀態下使用到CallBack Function。

1
2
let el = document.querySelector('.btn');
el.addEventListener('click', function() { ***做某些事*** });

像以上這個範例中在點擊了btn就會觸發addEventListener裡面的第二個參數,也就是那個匿名函式,啽這個匿名函式就是一個CallBack Function。

利用CallBack Function 控制函式執行的順序

我們可以透過將某個函式傳入另一個函式中,來達到先處理a函式後,才處理b函式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let funA = function(callback) {
let i = Math.random() + 1;

window.setTimeout(function(){
console.log('function A');

if(typeof(callback) === 'function') {
callback();
}
}, i*1000);
}

let funB = function() {
let i = Math.random() + 1;
window.setTimeout(funtion(){
console.log('function B');
}, i*1000);
}

funA(funB);

以上這個範例,我們把funB的呼叫包在funA裡面,呼叫,所以,不管何時執行A函式,都一定是在執行console.log('function A'),之後,
才會去執行B函式。

CallBack Hell

如果我們依照上面這個方式來安排函式之間的執行順序,但是,當函式的量一多,呼叫來呼叫去的,你的程式碼會直接變成CallBack 修羅場。
也就是書中有提到的CallBack Hell,那這種現象可以利用Promise來解決這種問題囉。

立即函式

立即函式顧名思義就是當下就會被執行的函式。

1
2
3
4
5
6
7
for(var i=0; i!=3; i++) {
(function(x) {
window.setTimeout(function(){
console.log('現在是第'+x+'次');
}, x*1000)
})(i);
}

以上這個經典範例,我們會利用立即函式直接將當下的i變數,直接傳入該立即函式,並執行裡面的setTimeout函式。

同步 v.s 非同步

書中這邊講得蠻清楚的,所以,很值得紀錄一下~

非同步的概念

假設今天要做一道花枝炒飯。
今天這是一間大飯店,它有多位廚師,其中一位廚師處理花枝,另外一位廚師處理炒飯的部分,
所以,在這個過程中可能花枝先煮好,或者,炒飯先炒好,但沒關係,等到兩道菜都做好後,再一起裝盤出菜。

所以,非同步處理事件的流程,並不會其中一個步驟而被卡住,這個就是非同步的概念。

同步的概念

一樣要做花枝炒飯,
但這是一間小店,它只有一位廚師,所以,他得先處理完花枝後,再接著處理炒飯的部分。
所以,只能等到一項項目處理完之後,才能再接著處理下一個項目。

所以,同步的處理事件的概念是,先完成1之後,才能再依序處理2, 3, 4…。
所以,同步這個名詞乍看真的很像全部一起處理,但其實它是一步一步,按部就班的處理
非同步則是可以同時處理多件事情,不用互等

不使用Promise就控制函式的操作順序

那如果我們希望在非同步的狀態下,想要先不用Promise的方法來達成控制函式的執行順序的話,書中有提供以下的寫法,我順便練習一下,

以上這個範例就可以看到,等到A, B, C這三個函式都執行完後,才執行D這個函式。
但是需要控制的函式一多的話,就會陷入Callback Hell的窘境,所以,我們就需要借助Promise的力量囉~