0%

箭頭函式與this(上)

箭頭函式 (Arrow functions)

與一般函式相比,寫法更加簡潔,也避免了某些問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 一般函式寫法
function fn(){
console.log('此為具名函式 - 函式陳述式寫法。')
}
var a = function(){
console.log('此為匿名函式 - 函式表達式寫法。')
}

// 箭頭函式
const b = () => {
console.log('此為箭頭函式寫法。')
}
// 若函式只有單一變數,參數括號可以簡化
const c = str => {
str = '簡化參數括號'
console.log(str)
}

// 若函式內只有單一陳述行,大括號可以簡化,但沒有參數時,括號必須存在
const d = () => console.log('此為箭頭函式簡易寫法。')

箭頭函式與一般函式的差異

箭頭函式除了寫法簡潔之外,它跟一般函式有甚麼差異? 每當搜尋相關介紹時,總會說明它沒有這些 :

  • this
  • arguments
  • call()
  • apply()
  • bind()

而這些到底是甚麼? 為什麼那三個內建函式又是甚麼? 為什麼它們看起來很陌生? 在下集會來介紹,在上集中會先介紹 this

先簡易說明一下,call()apply()bind() 這三種 JS內建函式,可以影響 this 的運作,若使用箭頭函式的話,由於 ES6的規則,連帶的那三個函式也跟著無法使用。
arguments,它為一般函式的內建參數,在箭頭函式中無此內建參數可運用,但是可以改用 ES6的其餘參數來達到同樣效果。

this

看到箭頭函式的介紹時,常會看到一句話,箭頭函式沒有自己的 this,而 this 從何而來,又指向誰,為什麼會用到它?

this是甚麼 ?

  • this 是 JavaScript的內建關鍵字之一
  • 通常會用在函式或物件中,來呼叫自身的擁有的屬性或值等等…

this從何而來 ?

先來看個範例 :

1
2
3
4
var a = 1
console.log(a) // 1
console.log(window.a) // 1
console.log(this.a) // 1

當我們將瀏覽器打開時,就已經建立好全域環境與執行環境,而程式碼就是寫在執行環境中,而 this 與全域環境一起被建立。

  • 全域環境
  • this
  • 執行環境

當我們要呼叫 a這個變數時,由上述範例可以知道,有三種呼叫變數 a的方式 :

  • 直接呼叫
  • 透過全域物件呼叫
  • 透過 JS內建關鍵字 this 呼叫

變數 a是由 var宣告的,而它的執行環境在全域環境中,於是變數 a就被建立在全域環境,接著就能透過全域物件 window找到它。

this指向誰 ?

通常我們寫程式碼時,習慣在全域環境下作業,而 this 在呼叫自身時,必須留意在哪個環境被呼叫,而不看在哪裡被使用,像是以下這些例子 :

  • 簡易呼叫函式
    1
    2
    3
    4
    5
    6
    7
    // 在全域的環境下執行函式,`this`會呼叫全域下的變數
    var name = '全域阿婆'
    function nameFn(){
    var name = '漂亮阿姨'
    console.log(this.name);
    }
    nameFn() // '全域阿婆'
  • 物件中呼叫函式
    1
    2
    3
    4
    5
    6
    7
    8
    // 在全域的環境下執行物件中的函式,`this`則指向物件本身
    const obj ={
    name:'小杰',
    callFn(){ // 物件內函式縮寫
    console.log(this.name);
    }
    }
    obj.callFn(); // '小杰'
  • 物件裡呼叫函式並執行函式中的函式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var name = '全域阿婆';
    const obj = {
    name:'小杰',
    callFn(){ // 物件內函式縮寫
    function callFn2(){
    console.log(this.name);
    };
    callFn2();
    }
    }
    obj.callFn(); // 相當於直接執行 callFn2() -> '全域阿婆'

為什麼會用 this ?

this 一開始有提到,它可以用在函式或物件中,來呼叫自身的擁有的屬性或值,來看以下例子 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let personA = {
age: 18,
sayHello: function (number){
console.log(`我今年${number}歲`)
}
}
personA.sayHello(personA.age)

let personB = {
age: 10,
sayHello: function (){
console.log(`我今年${this.age}歲`)
}
}
personB.sayHello()

使用 this 可以讓程式碼簡化不少。

箭頭函式與 this

上述範例中,物件裡呼叫函式並執行函式中的函式,this 會指向全域,而我們可以使用箭頭函式來解決這樣的問題,因為 :

  • 箭頭函式沒有自己的 this
  • 當箭頭函式中使用 this時,它會往上一層尋找
  • 也就是說,在哪邊被使用,它就會往上一層尋找,而不看環境
1
2
3
4
5
6
7
8
9
10
11
let name = '全域阿婆'
const obj = {
name:'小杰',
callFn () { // 物件內函式縮寫
const callFn2 = () => {
console.log(this.name);
}
callFn2();
}
}
obj.callFn(); // '小杰'

回想

  • 箭頭函式怎麼寫 ?
  • 如何知道箭頭函式沒有自己的 this ?
  • 甚麼時候會用到 this ?
  • this 在不同地方會指向誰 ?
    • 在全域環境下,若創造一個箭頭函式並使用了 this ,它會指向誰 ?
    • 在物件中新增了一個函式並使用了 this ,最後在全域環境下呼叫物件內的函式,它會指向誰 ?

參考來源

  1. Schaos - 前端三十|10. [JS] 一般函式與箭頭函式的差異?
  2. 胡立 - 淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
  3. Mooji - JS 原力覺醒 Day07 - 陳述式 表達式
  4. 卡斯伯 - 鐵人賽:箭頭函式 (Arrow functions)
  5. MDN - 箭頭函式
  6. 六角學院 - JS核心篇