ES6 系列之箭頭函數

回顧

咱們先來回顧下箭頭函數的基本語法。html

ES6 增長了箭頭函數:git

let func = value => value;
複製代碼

至關於:es6

let func = function (value) {
    return value;
};
複製代碼

若是須要給函數傳入多個參數:github

let func = (value, num) => value * num;
複製代碼

若是函數的代碼塊須要多條語句:express

let func = (value, num) => {
    return value * num
};
複製代碼

若是須要直接返回一個對象:app

let func = (value, num) => ({total: value * num});
複製代碼

與變量解構結合:異步

let func = ({value, num}) => ({total: value * num})

// 使用
var result = func({
    value: 10,
    num: 10
})

console.log(result); // {total: 100}
複製代碼

不少時候,你可能想不到要這樣用,因此再來舉個例子,好比在 React 與 Immutable 的技術選型中,咱們處理一個事件會這樣作:函數

handleEvent = () => {
  this.setState({
    data: this.state.data.set("key", "value")
  })
};
複製代碼

其實就能夠簡化爲:ui

handleEvent = () => {
  this.setState(({data}) => ({
    data: data.set("key", "value")
  }))
};
複製代碼

比較

本篇咱們重點比較一下箭頭函數與普通函數。this

主要區別包括:

1.沒有 this

箭頭函數沒有 this,因此須要經過查找做用域鏈來肯定 this 的值。

這就意味着若是箭頭函數被非箭頭函數包含,this 綁定的就是最近一層非箭頭函數的 this。

模擬一個實際開發中的例子:

咱們的需求是點擊一個按鈕,改變該按鈕的背景色。

爲了方便開發,咱們抽離一個 Button 組件,當須要使用的時候,直接:

// 傳入元素 id 值便可綁定該元素點擊時改變背景色的事件
new Button("button")
複製代碼

HTML 代碼以下:

<button id="button">點擊變色</button>
複製代碼

JavaScript 代碼以下:

function Button(id) {
    this.element = document.querySelector("#" + id);
    this.bindEvent();
}

Button.prototype.bindEvent = function() {
    this.element.addEventListener("click", this.setBgColor, false);
};

Button.prototype.setBgColor = function() {
    this.element.style.backgroundColor = '#1abc9c'
};

var button = new Button("button");
複製代碼

看着好像沒有問題,結果倒是報錯 Uncaught TypeError: Cannot read property 'style' of undefined

這是由於當使用 addEventListener() 爲一個元素註冊事件的時候,事件函數裏的 this 值是該元素的引用。

因此若是咱們在 setBgColor 中 console.log(this),this 指向的是按鈕元素,那 this.element 就是 undefined,報錯天然就理所固然了。

也許你會問,既然 this 都指向了按鈕元素,那咱們直接修改 setBgColor 函數爲:

Button.prototype.setBgColor = function() {
    this.style.backgroundColor = '#1abc9c'
};
複製代碼

不就能夠解決這個問題了?

確實能夠這樣作,可是在實際的開發中,咱們可能會在 setBgColor 中還調用其餘的函數,好比寫成這種:

Button.prototype.setBgColor = function() {
    this.setElementColor();
    this.setOtherElementColor();
};
複製代碼

因此咱們仍是但願 setBgColor 中的 this 是指向實例對象的,這樣就能夠調用其餘的函數。

利用 ES5,咱們通常會這樣作:

Button.prototype.bindEvent = function() {
    this.element.addEventListener("click", this.setBgColor.bind(this), false);
};
複製代碼

爲避免 addEventListener 的影響,使用 bind 強制綁定 setBgColor() 的 this 爲實例對象

使用 ES6,咱們能夠更好的解決這個問題:

Button.prototype.bindEvent = function() {
    this.element.addEventListener("click", event => this.setBgColor(event), false);
};
複製代碼

因爲箭頭函數沒有 this,因此會向外層查找 this 的值,即 bindEvent 中的 this,此時 this 指向實例對象,因此能夠正確的調用 this.setBgColor 方法, 而 this.setBgColor 中的 this 也會正確指向實例對象。

在這裏再額外提一點,就是注意 bindEvent 和 setBgColor 在這裏使用的是普通函數的形式,而非箭頭函數,若是咱們改爲箭頭函數,會致使函數裏的 this 指向 window 對象 (非嚴格模式下)。

最後,由於箭頭函數沒有 this,因此也不能用 call()、apply()、bind() 這些方法改變 this 的指向,能夠看一個例子:

var value = 1;
var result = (() => this.value).bind({value: 2})();
console.log(result); // 1
複製代碼

2. 沒有 arguments

箭頭函數沒有本身的 arguments 對象,這不必定是件壞事,由於箭頭函數能夠訪問外圍函數的 arguments 對象:

function constant() {
    return () => arguments[0]
}

var result = constant(1);
console.log(result()); // 1
複製代碼

那若是咱們就是要訪問箭頭函數的參數呢?

你能夠經過命名參數或者 rest 參數的形式訪問參數:

let nums = (...nums) => nums;
複製代碼

3. 不能經過 new 關鍵字調用

JavaScript 函數有兩個內部方法:[[Call]] 和 [[Construct]]。

當經過 new 調用函數時,執行 [[Construct]] 方法,建立一個實例對象,而後再執行函數體,將 this 綁定到實例上。

當直接調用的時候,執行 [[Call]] 方法,直接執行函數體。

箭頭函數並無 [[Construct]] 方法,不能被用做構造函數,若是經過 new 的方式調用,會報錯。

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
複製代碼

4. 沒有 new.target

由於不能使用 new 調用,因此也沒有 new.target 值。

關於 new.target,能夠參考 es6.ruanyifeng.com/#docs/class…

5. 沒有原型

因爲不能使用 new 調用箭頭函數,因此也沒有構建原型的需求,因而箭頭函數也不存在 prototype 這個屬性。

var Foo = () => {};
console.log(Foo.prototype); // undefined
複製代碼

6. 沒有 super

連原型都沒有,天然也不能經過 super 來訪問原型的屬性,因此箭頭函數也是沒有 super 的,不過跟 this、arguments、new.target 同樣,這些值由外圍最近一層非箭頭函數決定。

總結

最後,關於箭頭函數,引用 MDN 的介紹就是:

An arrow function expression has a shorter syntax than a function expression and does not have its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors.

翻譯過來就是:

箭頭函數表達式的語法比函數表達式更短,而且不綁定本身的this,arguments,super或 new.target。這些函數表達式最適合用於非方法函數(non-method functions),而且它們不能用做構造函數。

那麼什麼是 non-method functions 呢?

咱們先來看看 method 的定義:

A method is a function which is a property of an object.

對象屬性中的函數就被稱之爲 method,那麼 non-mehtod 就是指不被用做對象屬性中的函數了,但是爲何說箭頭函數更適合 non-method 呢?

讓咱們來看一個例子就明白了:

var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b();
// undefined Window
obj.c();
// 10, Object {...}
複製代碼

自執行函數

自執行函數的形式爲:

(function(){
    console.log(1)
})()
複製代碼

或者

(function(){
    console.log(1)
}())
複製代碼

利用箭頭簡化自執行函數的寫法:

(() => {
    console.log(1)
})()
複製代碼

可是注意:使用如下這種寫法卻會報錯:

(() => {
    console.log(1)
}())
複製代碼

爲何會報錯呢?嘿嘿,若是你知道,能夠告訴我~

ES6 系列

ES6 系列目錄地址:github.com/mqyqingfeng…

ES6 系列預計寫二十篇左右,旨在加深 ES6 部分知識點的理解,重點講解塊級做用域、標籤模板、箭頭函數、Symbol、Set、Map 以及 Promise 的模擬實現、模塊加載方案、異步處理等內容。

若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。若是喜歡或者有所啓發,歡迎 star,對做者也是一種鼓勵。

相關文章
相關標籤/搜索