ES6 系列之箭頭函數

回憶

我們先來回憶下箭頭函數的基礎語法。

ES6 增加了箭頭函數:

let func = value => value;

相當於:

let func = function (value) {
    return value;
};

假如須要給函數傳入多個參數:

let func = (value, num) => value * num;

假如函數的代碼塊須要多條語句:

let func = (value, num) => {
    return value * num
};

假如須要直接返回一個對象:

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")
  })
};

實在就能夠簡化為:

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

比較

本篇我們重點比較一下箭頭函數與平常函數。

重要區分包括:

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,能夠參考 http://es6.ruanyifeng.com/#docs/class#new-target-%E5%B1%9E%E6%80%A7

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 系列目次地點:https://github.com/mqyqingfeng/Blog

ES6 系列估計寫二十篇擺布,旨在加深 ES6 部份知識點的明白,重點解說塊級作用域、標籤模板、箭頭函數、Symbol、Set、Map 以及 Promise 的模仿完成、模塊加載計劃、異步處置懲罰等內容。

假如有毛病或許不嚴謹的處所,請務必賦予斧正,非常謝謝。假如喜好或許有所啟示,迎接 star,對作者也是一種勉勵。

    原文作者:冴羽
    原文地址: https://segmentfault.com/a/1190000015162781
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞