ES6 章節:箭頭函式介紹

Vicky
宅宅薇琪 [前端學習筆記]
9 min readFeb 26, 2021

箭頭函式也是 ES6 的新增語法。ES6 語法的新增是為了讓原有 JS 撰寫更為精簡 (也稱為語法糖)。ES6 可以讓 JS 撰寫觀念更為直覺、簡便 (新方法),但運作邏輯和傳統 JS 觀念上會有不同。

箭頭函式如何縮寫

我們先來看一段函式表達式的寫法。

先宣告一個變數 callName,後面接函式的內容。把此函式賦予到變數 callName 上。

const callName = function(someone){  // 會傳入一個參數
return '我是' + someone; // 會 return 一個值
}
console.log(callName('小明')); // 我是小明

縮寫,改成箭頭函式的寫法。

步驟一 : 把 function 此關鍵字移除

步驟二 : 在「參數」的右邊補上「=>」

const callName = (someone) => {
return '我是' + someone;
}
console.log(callName('小明')); // 我是小明

還可以縮寫,縮寫條件為 “如果程式碼內容為表達式時” 就可縮寫。

表達式就是可以回傳一個值。
以上例來說,函式內沒有其他內容,直接回傳一個值,就可做縮寫。

步驟一 : 把程式碼改成「單行」

步驟二 : 把「大括號」去掉

步驟三 : 把「return」拿掉 : 會自動「return」回傳箭頭後面的結果

const callName = (someone) => '我是' + someone;
console.log(callName('小明')); // 我是小明

當只有一個參數時,可以把小括號拿掉。

const callName = someone => '我是' + someone;
console.log(callName('小明')); // 我是小明

沒有參數時,此小括號不可省略。

const callName = () => '小括號不可省略';
console.log(callName()); // 小括號不可省略

兩個參數時,此小括號不可省略。

const callName = (a, b) => '我是' + a + b;
console.log(callName('林', '小花')); // 我是林小花

與傳統函式不同之處 - 箭頭函式沒有 arguments 這個參數

箭頭函式屬於新增的方法,和傳統函式會有不同。

先來看看「傳統函式」裡的 arguments 參數

  • 傳統函式在執行時會自動帶上 arguments 這個參數
  • arguments 參數會將我們傳入的參數一一列出
  • arguments 參數為「類陣列」 ,和一般的陣列不一樣
const nums = function(){
console.log(arguments);
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500);
// 看到 arguments 物件,裡面帶入的值就是我們所傳入的參數

把上例改成「箭頭函式」:
因為「箭頭函式」沒有 arguments 這個參數,所以跳錯。

const nums = () => {
console.log(arguments);
// 跳錯,ReferenceError: arguments is not defined
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500);
// ReferenceError (無法去存取一個不存在的變數)
注意錯誤代碼 ReferenceError : 無法去存取一個不存在的變數

但有些時候必須把未列出的參數也取出,那該怎麼做呢 ?
可以使用「其餘參數」的做法。

「其餘參數」用法 : 前面加上「3個點」,後面接「變數名稱」

const nums = (...arg) => {
console.log(arg);
}
nums(10, 50, 100, 50, 5, 1, 1, 1, 500);

與傳統函式不同之處 — 「箭頭函式」沒有自己的 this

this 的綁定和 “如何呼叫” 有很大的關係

  • this 與函式如何宣告沒有關係,僅與呼叫方法有關
  • 物件的方法調用時,僅需要關注是在哪個物件下呼叫的
var myName = '全域'
var person = {
myName: '小明',
callName: function(){
console.log('1', this.myName); // 1 小明
setTimeout(function(){ // 簡易呼叫, this 指向 window
console.log('2', this.myName); // 2 全域
console.log('3', this); // 3 (this 指向 window)
}, 10);
}
}
person.callName();
/* 在 person 物件下呼叫 callName 函式時,若 callName 函式內有 this,this 就會指向 person */

把上例程式碼中的 setTimeout 改成「箭頭函式」。

因為「箭頭函式」沒有自己的 this,若在「箭頭函式」裡看到 this 就先當作不存在,this 會使用外層作用域的 this (指向外層的 person 物件)。

var myName = '全域'
var person = {
myName: '小明',
callName: function(){
console.log('1', this.myName); // 1 小明
setTimeout(() => { // 箭頭函式,this 指向外層作用域的 person 物件
console.log('2', this.myName); // 2 小明
console.log('3', this); // this 指向外層作用域的 person 物件
}, 10);
}
}
person.callName();

接著,再把 callName 函式也變成「箭頭函式」。

var myName = '全域'
var person = {
myName: '小明',
callName: () => { // 箭頭函式,沒有自己的 this,this 指向 window
console.log('1', this.myName); // 1 全域
setTimeout(() => { // 箭頭函式,沒有自己的 this,this 指向 window
console.log('2', this.myName); // 2 全域
console.log('3', this); // this 指向 window
}, 10);
}
}
person.callName();

this 不同,導致 DOM 的 this 也會指向不同位置

來看一下範例 , 若使用「傳統函式」寫法, 監聽器則會綁定 this。

HTML 部分

<p>這裡具有一段話</p>

JS 部份 : 透過 click 方式把 DOM 位置取出

const ele = document.querySelector('p');
ele.addEventListener('click', function(){
// 透過 click 方式取出 DOM 的位置
console.log(this);
// <p>這裡具有一段話</p> (這裡 this 指的是 HTML 裡的 DOM 元素)
this.style.background = 'yellow'; // 點擊 DOM 元素即變色
});

若改成「箭頭函式」, this 則指向全域 window。

const ele = document.querySelector('p');
ele.addEventListener('click', () => {
// 透過 click 方式取出 DOM 的位置
console.log(this); // 指向全域 window (「箭頭函式」沒有自己的 this)
// this.style.background = 'yellow';
});

「箭頭函式」沒有自己的 this,也無法透過 call, apply, bind 重新給予 this

先前章節有提到可以透過 call 的方式把另外一個物件傳入,並作為函式執行的 this 使用。

在「傳統函式」中,this 指向 family

const family = {
myName: '小明家',
}
const fn = function(para1, para2) {
console.log(this, para1, para2);
}
fn.call(family, '小明', '杰倫'); // {myName: "小明家"} "小明" "杰倫"

若改成「箭頭函式」,this 指向全域 window。

const family = {
myName: '小明家',
}
const fn = (para1, para2) => {
console.log(this, para1, para2);
}
fn.call(family, '小明', '杰倫'); // 全域 window "小明" "杰倫"

與傳統函式不同之處 -「箭頭函式」無法作為「建構函式」使用

假設有兩段「建構函式」,一個是用「傳統函式」撰寫、另一個是用「箭頭函式」撰寫。

const Fn = function (a) {
this.name = a; // this 指向傳入的參數 (作為建構函式用)
}
const ArrowFn = (a) => { // 無法作為建構函式使用
this.name = a;
}

來看一下「傳統函式」、「箭頭函式」撰寫的「建構函式」prototype 長怎樣

console.log(Fn.prototype, ArrowFn.prototype);  
// {constructor: ƒ} undefined

結果 :

  • 傳統函式有 prototype,可以做為「建構函式使用」
  • 「箭頭函式」沒有 prototype,不能做為「建構函式使用」

再來使用 new 方式來新增物件實體

物件實體 b 跳錯,ArrowFn 並不是一個建構函式,他沒有 prototype,且 this 的指向也不同。

const a = new Fn('a');  // 可以另外傳入參數作為物件的屬性用
console.log(a); // Fn {name: "a"}

const b = new ArrowFn('b'); // 跳錯,(ArrowFn is not a constructor)
// ArrowFn 並不是一個建構函式,他沒有 prototype

--

--