This 與 Arrow Function
在前一篇章節,筆者介紹了函式的概念以及多種函式的宣告方法、立即函式,本章我們就來進一步探討什麼是箭頭函式、函式的呼叫方法有哪些以及 this 的概念吧!

What is "This"?

若讀者不是第一次寫程式,並且有接觸過物件導向程式語言,對於 this 肯定不陌生,像是下方的 PHP 程式碼:
1
//...
2
class linkedList implements linkedListInterface{
3
private $head = 0;
4
private $length = 0;
5
private $memory = array();
6
function printList(){
7
if($this->length ==0){
8
return 0;
9
}
10
else{
11
$currentNode = $this->head;
12
while(true){
13
print_r($this->memory[$currentNode]);
14
$currentNode = $this->memory[$currentNode]->getNext();
15
if($currentNode === "nullPtr"){
16
break;
17
}
18
}
19
print "
20
";
21
}
22
}
23
//...
24
}
25
//...
Copied!
在大多數的程式語言中, this 都指向其實體本身。不過,身為怪奇語言的 JavaScript 肯定是不會放過廣大開發者的XD
在 JavaScript 中,this 會隨著函式、 class 實體化時被自動生成,它通常代表呼叫函式的那個物件,像是:
1
const returnYourName = function(){
2
return this.name;
3
}
4
const Ian = {
5
name: 'Ian',
6
whatYourName: returnYourName
7
}
8
const Peter = {
9
name: 'Peter',
10
whatYourName: returnYourName
11
}
12
console.log(Ian.whatYourName()) //Ian
13
console.log(Peter.whatYourName()) //Peter
Copied!
我們可以透過結果發現:
當執行 Ian.whatYourName() 時, 在 Ian 物件中的 whatYourName() 的函式實體的 this 是指向 Ian 的。
這也驗證了這句話:
在 JavaScript 中,this 會隨著函式、 class 實體化時被自動生成,它通常代表呼叫函式的那個物件。
如果讀者不相信,我們可以進一步透過程式碼驗證:
1
const returnYourName = function(){
2
return this.name;
3
}
4
const returnThis = function(){
5
return this.name;
6
}
7
const Ian = {
8
name: 'Ian',
9
whatYourName: returnYourName,
10
returnThis: returnThis
11
}
12
console.log(Ian.returnThis()) //Ian
Copied!
結果也與其他說法如出一轍。

call(), apply(), bind()

在上述的範例中,我們已經知道:
在 JavaScript 中,this 會隨著函式、 class 實體化時被自動生成,它通常代表呼叫函式的那個物件。
不過, JavaScript 也有提供三種方法讓我們自己決定 this 該指向誰。
在正式開始之前,我們先宣告一個 sum 函式:
1
let sum = function(para1, para2){
2
return para1 + para2;
3
}
Copied!
在一般情況下,若我們要呼叫定義好的函式會這麼做:
1
sum(1,2); //3
Copied!

bind()

我們可以利用 bind() 讓函式的 this 綁定我們指定的值,如下:
1
function sum(para1:number,para2:number) {
2
console.log(para1 + para2,this)
3
}
4
5
const sumX = sum.bind('Hello')
6
sumX(1,2)
Copied!
根據範例程式碼,筆者宣告了 sumX 存放綁定 this 後的 sum 函式,所以我們在呼叫 sumX() 時,便會透過命令列看到:
1
3, Hello
Copied!

apply() and call()

bind() 不同, apply()call() 只需要在呼叫函式時加在後方:
1
sum.call('Ian', 1, 2) // Ian 1 2
2
sum.apply('Peter', [1, 2]) // Peter 1 2
Copied!
至於 call()apply 的差別,就只是帶入參數的方法不同而已。

箭頭函式

在先前的篇章中,筆者僅介紹了普通函式。在 JavaScript ES6 開始新增了一個特別的函式: 箭頭函式 (Arrow function)。
使用起來非常簡潔:
1
()=>{
2
console.log("Hello, world!")
3
}
4
let sum = (para1: number, para2: number)=>{
5
return para1 + para2;
6
}
7
let result = sum()
Copied!
需要注意的是,箭頭函式的 this 與一般函式不同,箭頭函式的 this 會綁定到其定義時所在的物件。
因為筆者在準備範例時在 Deno 上遇到了一些問題,所以會先將這部分放到問題解決再補上。
如果想進一步學習也可以參考今天的延伸閱讀。

目前遇到的問題

上面提到筆者遇到了一些問題,為了證明筆者不是在找藉口不寫稿,筆者就把目前遇到的問題丟上來:
  • 全域下, this 應該要是 window 而不是 undefined
  • 一直噴以下錯誤:
1
TS2683 [ERROR]: 'this' implicitly has type 'any' because it does not have a type annotation.
2
return this.name;
Copied!
在 Deno 的官方文件中有提到,在全域中的 thiswindow (node.js 為 global),然而不論筆者怎麼嘗試,結果都是 undefined ,我也有考慮到是否為 TypeScript 預設會開嚴格模式,不過筆者也嘗試了官方文件中提到的 -c tsconfig.json--no-check。 當加上 --no-check 執行程式碼時,確實不會在噴出 'this' implicitly has type 'any' because it does not have a type annotation. 的錯誤,不過筆者認為這項作法只是在逃避問題,並不是好作法。 至於在全域中的 thisundefined 的問題,我在 tsconfig.json 將嚴格模式中把它關掉以後也不見改善,我在想一定是我沒有將官方文件閱讀清楚,還請各位讀者諒解,我會盡快找出問題的答案,如有必要,也會在 Deno 的 Repo 發看看 issue (但我認為這問題不是 Deno 本身造成的,所以筆者還是希望能自己解決問題)。

延伸閱讀

同樣的事情在不同人眼中可能會有不同的見解、看法。
在讀完本篇以後,筆者也強烈建議大家去看看以下文章,或許會對型別、變數宣告...等觀念有更深層的看法唷!
Last modified 6mo ago