es6 箭頭函數(arrow function) 學習筆記


箭頭函數有兩個好處。☕

1.他們比傳統函數表達式簡潔。

const arr = [1, 2, 3];
const squares = arr.map(x => x * x);

// 傳統函數表達式:
const squares = arr.map(function (x) { return x * x });

2.箭頭函數不會綁定關鍵字this,我們不需要用bind()或者that = this這種方法了

function UiComponent() {
    const button = document.getElementById('myButton');
    button.addEventListener('click', () => {
        console.log('CLICK');
        this.handleClick(); // this不再是button
    });
}

和this同樣沒有被箭頭函數綁定的參數有

  • arguments
  • super
  • this
  • new.target

例如:

function foo() {
    setTimeout( () => {
        console.log("args:", arguments);
    },100);
}

foo( 2, 4, 6, 8 );
// args: [2, 4, 6, 8]

=>箭頭函數並沒有綁定 arguments,所以它會以 foo() 的 arguments 來取而代之,而 super 和 new.target 也是一樣的情況

 

傳統的函數不好的地方在於有一些非方法的函數,劫持了this

在JavaScript中,傳統函數有3種角色:

1.非方法函數

2.方法

3.構造函數

這三種函數在使用中會有角色沖突,角色2、3的函數含有他們自己的this指向,但是角色1會對this進行劫持

你可以看這段傳統JS代碼:

function Prefixer(pp) {
    this.prefix = pp;
}
Prefixer.prototype.prefixArray = function (arr) { // (A)
    return arr.map(function (x) { // (B)
        // 無法正常工作:
        return this.prefix + x; // (C)
    });
};
var pre = new Prefixer('Hi ');
pre.prefixArray(['Joe', 'Alex']);//TypeError: Cannot read property 'prefix' of undefined

在C行,我們希望訪問this.prefix,但是卻無法訪問,因為this在非方法函數里是undefined,所以我們會得到報錯。

在ECMAScript5中,有三種辦法可以繞過這個問題:

方案1 that = this

function Prefixer(prefix) {
    this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
    var that = this; // (A)
    return arr.map(function (x) {
        return that.prefix + x;
    });
};


var pre = new Prefixer('Hi ');
pre.prefixArray(['Joe', 'Alex']); //[ 'Hi Joe', 'Hi Alex' ]

 

方案2 直接指定this的值

一部分數組方法會提供一個額外的參數值來修改this,注意A行

function Prefixer(prefix) {
    this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
    return arr.map(function (x) {
        return this.prefix + x;
    }, this); // (A)
};

方案3 綁定this

你可以使用bind()方法來告訴函數是誰調用的

function Prefixer(prefix) {
    this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
    return arr.map(function (x) {
        return this.prefix + x;
    }.bind(this)); // (A)
};

 

 

ECMAScript6 方案:箭頭函數

箭頭函數的工作方式很像方案3,但箭頭函數壓根不會修改this的作用域,而且你不需要給正常的函數加綁定。

下面是用了箭頭函數后的代碼:

function Prefixer(prefix) {
    this.prefix = prefix;
}
Prefixer.prototype.prefixArray = function (arr) {
    return arr.map((x) => {
        return this.prefix + x;
    });
};

如果完全用ES6寫這段代碼,可以寫成這樣,可以用一個類和一個更節省的方法寫函數:

class Prefixer {
    constructor(prefix) {
        this.prefix = prefix;
    }
    prefixArray(arr) {
        return arr.map(x => this.prefix + x); // (A)
    }
}

在A行,我們通過箭頭函數省略了一些字符

  • 如果函數只有一個參數(x),那么可以省略括號
  • 如果函數體只有一個表達式,那么可以省略return

 

箭頭函數語法

參數:

    () => { ... } // 沒有參數
     x => { ... } // 一個參數
(x, y) => { ... } // 多個參數

 

函數體:

x => { return x * x }  // 語句塊
x => x * x  // 表達式,和上一行作用相同

如果函數體是一個表達式,會隱式返回結果,對比一下:

const squares = [1, 2, 3].map(function (x) { return x * x });
const squares = [1, 2, 3].map(x => x * x);

 

單個參數時括號可以省略

只有一個函數參數的時候,才可以省略小括號:

[1,2,3].map(x => 2 * x) //[ 2, 4, 6 ]

 

如果有多個參數的時候,就必須加上小括號:

[[1,2], [3,4]].map(([a,b]) => a + b) //[ 3, 7 ]

 

當你使用ES6函數參數默認值的時候也必須加括號:

[1, undefined, 3].map((x='yes') => x) //[1, yes', 3 ]

 

箭頭函數的優先級比較低

如果你把箭頭函數當成一個運算符的話,它會有比較低的優先級。

所以當箭頭函數和其他運算符相遇時,其他運算符通常會贏。

例如下面示例,箭頭函數和條件表達式挨在一起了:

const f = x => (x % 2) === 0 ? x : 0;

換句話說等同於下面的語句:

const f = x => ((x % 2) === 0 ? x : 0);

如果想提高箭頭函數的優先級,可以使用括號括住函數,讓函數返回結果作為條件:

const f = (x => ((x % 2) === 0)) ? x : 0;

另一方面,當你使用使用typeof這樣的表達式作為函數體時,不需要添加括號

const f = x => typeof x;

 

關於箭頭函數換行

es6禁止參數定義和箭頭之間的換行符

const func1 = (x, y) // SyntaxError
=> {
    return x + y;
};
const func2 = (x, y) => // OK
{
    return x + y;
};
const func3 = (x, y) => { // OK
    return x + y;
};

const func4 = (x, y) // SyntaxError
=> x + y;
const func5 = (x, y) => // OK
x + y;

 

在參數中換行是可以的:

const func6 = ( // OK
    x,
    y
) => {
    return x + y;
};

 

關於箭頭函數的函數體

函數體內只有一個表達式是可以省略大括號的:

asyncFunc.then(x => console.log(x));

但是聲明必須放在大括號中:

asyncFunc.catch(x => { throw x });

 

返回對象變量

如果想讓函數返回一個花括號對象,就要加小括號:

const f1 = x => ({ bar: 123 });
f1();//{ bar: 123 }

不然的話,程序會認為這是一個塊級作用域

const f2 = x => { bar: 123 };
> f2() //undefined

 

立即調用的箭頭函數

還記得立即調用函數表達式(IIFE)嗎? 它們如下所示,用於模擬ECMAScript 5中的塊范圍和值返回塊:

(function () { // open IIFE
    // inside IIFE
})(); // close IIFE

你也可以用箭頭函數模擬:

(() => {
    return 123
})()

 

提取方法

<a id="myButton">按鈕</a>
<script type="text/javascript" src="js/babel.min.js"></script>
<script type="text/babel">
    class KK {
        constructor() {
            var myButton = document.getElementById('myButton');
            myButton.addEventListener('click', event => this.handleEvent(event));
            //es5 myButton.addEventListener('click', this.handleEvent.bind(this));
        }

        handleEvent(event) {
            console.log(event);
        }
    }

    var kk = new KK();
</script>

 

箭頭函數還可以用於參數指定技巧

比如使用array.filter需要傳入函數,而且需要指定this對象為bs,只能通過額外參數來指定

const as = new Set([1, 2, 3]);
const bs = new Set([3, 2, 4]);
const intersection = [...as].filter(bs.has, bs);// [2, 3]

可以用箭頭函數優化寫法:

const as = new Set([1, 2, 3]);
const bs = new Set([3, 2, 4]);
const intersection = [...as].filter(a => bs.has(a)); // [2, 3]

 

部分求值

以前可以利用bind來產生一個新的函數來求值,但是尷尬的是第一個參數必須要傳入undefined,這變得難以理解:

function add(x, y) {
    return x + y;
}
const plus1 = add.bind(undefined, 1);
console.log(plus1(2));//3

如果用箭頭函數,就變得容易理解:

const plus1 = y => add(1, y);

 

箭頭函數和普通函數對比

1.箭頭函數中不會干涉下列關鍵字:super, this, new.target

2.箭頭函數沒有構造函數:常規函數有Construct和函數屬性,而箭頭函數沒有,所以 new (() => {}) 會報錯

 

除了以上兩點,箭頭函數和普通函數沒有什么區別,typeof和instanceof輸出的結果相同:

typeof (() => {}) //'function'
() => {} instanceof Function //true


typeof function () {} //'function'
function () {} instanceof Function //true

 

 

參考資料:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM