【ES6基礎】箭頭函式(Arrow functions)

前端達人發表於2019-03-03
【ES6基礎】箭頭函式(Arrow functions)

ES6中,除了let和const新特性,箭頭函式是使用頻率最高的新特性了。如果你曾經瞭解如日中天的JavaScript衍生語言CoffeeScript, 就會清楚此特性並非ES6獨創。箭頭函式顧名思義是使用箭頭(=>)定義的函式,屬於匿名函式一類。

今天的文章內容將會從以下幾個方面,介紹箭頭函式:

  • 使用語法
  • this穿透
  • 箭頭函式和傳統函式的區別

本篇文章閱讀時間預計8分鐘

使用語法

箭頭函式有四種使用語法

1、單一引數的單行箭頭函式

如下段程式碼所示,很簡單:

const fn= foo =>`${foo} world`;複製程式碼

這是箭頭函式最簡潔的形式,常用於用作簡單的處理函式,如過濾。如下段程式碼所示:

let array=['a','bc','def','ghij'];
array=array.filter(item=>item.length>=2);複製程式碼

2、多引數的單行箭頭函式 語法也很簡單,如下段程式碼所示:

const fn=(foo,bar) => foo+bar複製程式碼

在實際開發中,函式的引數不會只有一個,在箭頭函式中,多引數的語法跟普通函式一樣,用括號包裹引數項。我們經常處理函式,如排序,示例程式碼如下:

let array=['a','bc','def','ghij'];
array=array.sort((a,b) => a.length < b.length);複製程式碼

3、多行箭頭函式 單一引數,如下段程式碼所示:

foo => {
    return `${foo} world`;
}複製程式碼

多引數,如下段程式碼所示:

(foo,bar) => {
    return foo+bar;
}複製程式碼

4、無引數箭頭函式

如果一個箭頭函式無引數傳入,則需要用一對空的括號來表示空的引數列表。

const greet = () => 'Hello World'複製程式碼

以上都是被支援的箭頭函式的表達方式,其最大的好處就是簡單明瞭,省略了function關鍵字,而使用 => 代替。相對於傳統的function函式,箭頭函式在簡單的函式使用中更為簡潔直觀。

書寫箭頭的函式過程中,我們應該注意以下幾點:

1、使用單行箭頭函式時,應避免換行

錯誤的用法,如下段程式碼所示:

const fn=x
=> x*2 //SyntaxError複製程式碼

正確的寫法,如下:

const fn= x => x*2 //ok複製程式碼

2、引數列別的右括弧、箭頭應在一行

錯誤的用法,如下段程式碼所示:

const fn = (x,y) //SyntaxError
=> {
   return x*y;
}複製程式碼

下段程式碼書寫是正確的:

const fn= (x,y) => { //ok
    return x*y
}

const fn= (x,
           y) => { //ok
    return x*y        
}複製程式碼

3、單行箭頭函式返回只能包含一條語句

錯誤的書寫,如下段程式碼所示:

const fn1= x => x=x*2; return x+2; //SyntaxError複製程式碼

正確的書寫,如下段程式碼所示:

const fn2= x => {
    x=x*2;
    return x+2;
} //ok複製程式碼

4、如果單行箭頭返回一個物件,請用圓括號包裹

錯誤的書寫,如下段程式碼所示,解析引擎會將其解析成一個多行箭頭函式:

const ids=[1,2,3];
const users=ids.map(id=>{id:id});
//wrong:[ undefined, undefined, undefined ]複製程式碼

正確的書寫,如下段程式碼所示:

const ids=[1,2,3];
const users=ids.map(id=>({id:id}));
//Correct:[ { id: 1 }, { id: 2 }, { id: 3 } ]複製程式碼

箭頭函式十分簡潔,特別適合單行回撥函式的定義,比如我們有以下需求:

我們有一個這樣的名字陣列names,['Will','Jack','Peter','Steve','John','Hugo','Mike'],輸出序號為偶數的名字[ 'Will', 'Peter', 'John', 'Mike' ],我們如何使用箭頭函式在一行語句完成呢,如下段程式碼所示:

const names=['Will','Jack','Peter','Steve','John','Hugo','Mike'];
const newSet=names
.map((name,index)=>({
    id:index,
    name:name
}))
.filter(man => man.id %2 ==0)
.map(man => [man.name])
.reduce((a,b) => a.concat(b))複製程式碼

this穿透

事實上,箭頭函式不僅書寫簡潔,還有一個神奇的功能,就是將函式內部的this延伸上一層作用域中,及上一層的上下文會穿透到內層的箭頭函式中,讓我們先看一段實際的例子,如下段所示:

var Widget={
  // A
    init:function () {
    // B
        document.addEventListener("click", function (event){
    //C
            this.doSomething(event.type);
        }, false);
    },
    doSomething:function (type) {
        console.log("Handling"+ type+"event");
    }
};
Widget.init();複製程式碼

這段程式碼會如何輸出呢,想必大家都猜到了吧,輸出undefined,為什麼呢?我們在B位置內宣告瞭函式(C區域),this關鍵詞的指向B區域的函式,由於B區域內沒有doSomething函式宣告,因此輸出undefined,ES6之前我們如何修正此問題呢?

我們可以使用bind方法改變this指向A區域Widget物件,示例程式碼如下:

var Widget={
  // A
    init:function () {
        // B
        document.addEventListener("click", (function (event) {
            //C
            this.doSomething(event.type);
        }).bind(this), false);
    },
    doSomething:function (type) {
        console.log("Handling"+ type+"event");
    }
};
Widget.init();複製程式碼

下面這種方法是我們最常用的方法,我們在B區域宣告瞭that變數,並將其this賦值,確保c區域this的指向至Widget物件:

var Widget={
 // A
    init:function () {
        // B
        var that=this;
        document.addEventListener("click", function (event) {
                //C
            that.doSomething(event.type);
            console.log(that);
        }, false);
    },
    doSomething:function (type) {
        console.log("Handling"+ type+"event");
    }
};
Widget.init();複製程式碼

有了箭頭函式,我們可以使用箭頭函式的this穿透功能,將this的作用域延伸至上一層B區域函式,如下段程式碼所示:

var Widget={
//A
    init:function () {
    //B
        document.addEventListener("click", (event) => {
        //C
            this.doSomething(event.type);
        }, false);
    },
    doSomething:function (type) {
        console.log("Handling"+ type+"event");
    }
};
Widget.init();複製程式碼

箭頭函式是不是更簡單,程式碼更清晰呢。

還有一個情況需要注意,箭頭函式對上下文的繫結是強制的,無法通過call或aplly進行改變,如下段程式碼所示:

function widget() {
    this.id=123;
    this.log=()=>{
      console.log(this)
        console.log('widget log',this.id);
    }
}

var pseudoWidget={
    id:456
};

new widget().log.call(pseudoWidget);//123複製程式碼

上述程式碼會如何輸出呢,由於箭頭函式對上下文的繫結是強制的,因此this指向不會指向pseudoWidget物件,因此輸出123。

箭頭函式和傳統函式的區別

1、箭頭函式作為匿名函式,是不能作為建構函式的,不能使用new

如下段程式碼所示,我們使用new方法,會提示如下資訊:

const B =()=>({wechat:"前端達人"});
let b = new B(); //TypeError: B is not a constructor複製程式碼

2、箭頭函式不繫結arguments,可以使用剩餘引數(rest)解決

筆者在《【ES6基礎】展開語法(Spread syntax)》文章裡介紹過剩餘引數,這裡就不過多介紹,不清楚的可以點選文章連結進行檢視。

function A(a){
    console.log(arguments); //[object Arguments] {0: 1}
}

var B = (b)=>{
    console.log(arguments); //ReferenceError: arguments is not defined
}

var C = (...c)=>{ //...c即為rest引數
    console.log(c); //[3]
}
A(1);
B(2);
C(3);複製程式碼

3、箭頭函式this指向具備穿透特性,會捕獲其所在上下文的this值

4、箭頭函式沒有原型屬性

var a = ()=>{
    return '前端達人';
}

function b(){
    return '前端達人';
}
console.log(a.prototype);//undefined
console.log(b.prototype);//object{...}複製程式碼

5、箭頭函式不能當做Generator函式,不能使用yield關鍵字

6、箭頭函式對上下文的繫結是強制的,無法通過call或aplly進行改變

小節

今天的內容就介紹到這裡,我們可以看出使用箭頭函能減少程式碼量,更加簡介易讀。在使用箭頭函式時,我們一定要理解箭頭函式和傳統函式的區別,如果函式功能簡單,只是簡單的邏輯處理,儘量使用箭頭函式。

ES6相關文章

【ES6基礎】let和作用域

【ES6基礎】const介紹

【ES6基礎】預設引數值

【ES6基礎】展開語法(Spread syntax)

【ES6基礎】解構賦值(destructuring assignment)

更多精彩內容,請微信關注”前端達人”公眾號!

【ES6基礎】箭頭函式(Arrow functions)


相關文章