函式類型
有句話說「在 JS 裡面所有東西都是物件」,
意思是所有型別都由原型鏈的方式製作出來的,而原型鏈是基於基本物件產生的。
function 在底層也是一個物件,只是用 typeof
顯示出來的是 'function'
,
但使用 instanceof
就可以知道,function 實際上也是一種物件。
箭頭函式
可以取代一般的函式宣告,在寫 callback 的時候會明顯比較簡潔,
但語法上有幾點要注意:
- 參數只有 1 個時可以不寫括號,沒參數或 2 個以上的參數就一定要寫
- 箭頭後面可以直接丟要 return 的東西
- 要 return 物件不能直接寫大括號,會被當成函式作用域,要用小括號包好
const sayHi = () => {
console.log("Hi");
};
sayHi(); // 箭頭函式和傳統函式一樣可以賦值給變數再呼叫,變數名稱就是函式名稱
const list = [1, 2, 3, 4];
const newList = list.map((item) => ++item);
// 只有一個參數時可以不寫小括號
// newList 為 [2, 3, 4, 5]
const newObjList = list.map((item) => {
value: ++item;
});
// newObjList 為 [undefined, undefined, undefined, undefined]
// 如果要回傳物件,這樣寫是不行的,大括號被當成函式作用域而不是物件
// 必須改成這樣 list.map(item => ({ value: ++item }))
上面範例可以看到箭頭函式的語法概念,
包含常用的 陣列方法裡面的 callback 都可用箭頭函式改寫。
範例裡面的 ++ 都是寫在變數前面是因為前綴後綴的問題,
return 是直接讀取值後拿走的,所以要用 ++ 運算的話要用前綴,
否則只會帶到原本的值,而不會進行 ++ 運算。
匿名函式
沒有名字的函式也被稱作匿名函式,
如陣列方法都需要在帶參數的地方定義一個 callback function,
這些 function 不用寫函式名稱,因為在沒有用外部的變數存下來的情況下,
匿名函式是不會被再次呼叫的:
const list = [5, 6, 7, 8];
// 這裡的 callback 用關鍵字 function 或用箭頭函式都合法
list.forEach(function (item, index) {
console.log(`index: ${index}, value: ${item}`);
});
IIFE
不管是傳統函式、匿名函式、箭頭函式,它們都可以在被宣告後立即執行,
稱作 IIFE (Immediately Invoked Function Expression)。
語法就是將函式定義的部分整個用小括號包起來,再接一個小括號:
(function sayHi() {
console.log("Hi");
})();
// 宣告完就會直接印出 Hi
(() => console.log("Hi"))();
函式流浪指南
程式碼會在執行之前進行提升(Hoisting)。
其中函式宣告的提升優先度是最高的,
所以有時會看到程式碼的寫法是先呼叫了某個函式,往下找才會看到函式的宣告。
但是用 const 宣告的函式就不能這樣做,因為變數宣告與函式宣告的優先級別不同:
console.log(add(1, 2)); // 函式可以在宣告前就取用
function add(a, b) {
return a + b;
}
console.log(twoSum(1, 2)); // 會報錯
const twoSum = (a, b) => {
return a + b;
};
第一種傳統函式的宣告也稱為函式陳述式,
第二種將匿名函式賦值給變數則稱為函式表達式。
要注意的是提升只會提升宣告行為而不會提升賦值行為,
因此像函式表達式只會當成變數的提升。
變數被提升時,其內容是 undefined,它的值會在程式開始執行時,
讀取到 =
這個賦值運算符號後才會有內容,
因此 const 宣告的函式的問題,是因為我們在賦值之前就執行它,
它實際上是在向 undefined 要求執行函式,所以就報錯了。