[JS奇怪的世界]No.27 函數陳述句與函數表示式
前言
上一章節我們已經明白函數就是物件,接下來課程將利用這個觀念讓我們做一些有趣又強大的事情,但是開始前我們必須瞭解函數陳述句與函數表示式的差異。
表示式 (Expressions)
表示式 (Expressions)**的意思是說,輸入之後能夠直接回傳值的一串程式,簡單來講就是只要我們輸入那一大串程式碼之後能夠直接回傳值,那它就是一個表示式**。
所以我們來看一下範例程式碼。
1 | var a |
就這樣,接下來我們就可以開啟瀏覽器在 F12 開發者工具中輸入 console.log(a)
,可以看到 a
目前儲存在記憶體中。
那表示式到底是什麼?表示式會回傳一個值,所以當我們這樣打
1 | a = 3; |
記得中間這是一個等號運算子,它會將 3
賦予至 a
,然後再回傳一個值,所以我們就會出現一個值,那就是 3,而這行為就是表示式,所以其實前面 var a
是有回傳值的,那個值就是 undefined
。
陳述句 (Statements)
那陳述句呢?讓我們看看這個範例程式 ↓
1 | if (a === 3) { |
a === 3
其實也是一個表示式,因為它也可以直接回傳值(當條件為 true 或 false),但 if 這個指令就不一樣了,它是一個陳述句,它不會直接回傳一個值,所以我們也不能把它設置為一個變數。
1 | var b = if (a === 3) { |
這邊我們可以瞭解到一件觀念
- 函數表達式 會直接回傳值。
- 函數陳述句 不會回傳值,而是做其他事情。
函數陳述句
在 JavaScript 中我們都知道函數是一個物件,而函數裡面也有函數陳述句與函數表示式,這兩個東西非常的強大,首先我們先來做一個函數陳述句 ↓
1 | function greet() { |
當它被執行時並不會回傳任何值(此處執行並不是 greet()
,而是在講全域環境建立),函數陳述句的特色就是通過 hoisting 特性,先儲存進記憶體中,直到你執行這個函數函數。
在前面章節有講過變數及函數有提升特性,所以可以在函數陳述句之前呼叫 greet
。
1 | greet(); |
函數表示式
接下來就是函數表示式。
1 | var anonymousGreet = function() { |
看到這邊要注意一個觀念「函數就是物件」(附帶一提這也是人家講的匿名函數。)
(匿名函數就是沒有名稱的函數。)
那該如何觸發這個函數表示式?
1 | anonymousGreet(); |
這就是函數表達式,感覺與函數陳述句很像吧。
讓我們整合一下兩個程式碼 ↓
1 | greet(); |
相信你一定會發現這兩者差異並不大,但我們存在 anonymousGreet
中的匿名函數,只要加上括號就可以觸發函數。
匿名函數就是一個表示式
而函數陳述句與函數表示式最大差異在於,函數陳述句僅僅只是將函數存入記憶體,它只是知道[oh!這裡有一個函數],然後透過 hoisting 提升到最前面儲存至記憶體內就沒了。
但函數陳述句則是不一樣,它並不會因為 hoisting 提升到最前面,只有變數會被提升,陳述句則在原本地方
所以如果將 anonymousGreet()
移至 var anonymousGreet
之前會發生什麼事情?
1 | anonymousGreet() |
這時候我們要去想一下,當執行環境被創造時哪兩個東西會被提升,變數與函數然後提升,那結果會是什麼?
我們會得到 TypeError: anonymousGreet is not a function
,而不是 hi
,
所以實際將程式碼解開來看就會像這樣 ↓
1 | var anonymousGreet; |
這就是為什麼會顯示 TypeError: anonymousGreet is not a function
,因為變數 anonymousGreet
被提升到最前面且賦予 undefined
,那 undefined
本身是不能夠執行,所以才會出現錯誤。
除非直到 anonymousGreet =
,anonymousGreet
才會被正確的被重新設定,所以我們又得知一個結論。
函數表示式並不會被提升
這邊我們在一次要強調函數就是物件,所以可以這樣玩
1 | function log(a){ |
那該如何執行輸出 hi
?只要這樣就可以了。
1 | function log(a){ |