<commit_before>## ES6篇 - 箭頭函式

![intro](https://raw.githubusercontent.com/eyesofkids/ironman2017/master/day06_es6_arrow_func/asset/intro.png)

本章的目標是對箭頭函式提供一些較為全面性的介紹，除了基本的語法之外，也補充了很多React搭配使用時的實例，此外也提供撰寫的風格建議。當然，箭頭函式並不光只是語法簡短而已，它有一些與原來JavaScript中函式不同的設計，你可以把它當成是原本(傳統)的函式的改進版本。

> 註: 本文章同步放置於[Github庫的這裡](https://github.com/eyesofkids/ironman2017/tree/master/day06_es6_arrow_func)。

根據網路上統計的統計資料，箭頭函式(Arrow Functions)是ES6標準中，是最受歡迎的其中一種ES6新特性。它會受歡迎的原因是好處多多，只要注意在某些情況下不要用過頭就行了。有什麼好處呢？大致上有以下幾點:

- 語法簡單，少打很多字元
- 可以讓程式碼的可閱讀性提高
- 可以綁定詞法上的`this`值

> 註: 上述的統計可以參考[2ality](http://www.2ality.com/2015/07/favorite-es6-features.html)與[ponyfoo](https://ponyfoo.com/articles/javascript-developer-survey-results)這兩個網站的相關統計資料。

> 註: 本文大部份的基本語法內容是來自我電子書中的[箭頭函式](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/arrow_function.html)章節。

## 語法介紹

箭頭函式的語法如下，出自[箭頭函數(MDN)](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions)：

```js
([param] [, param]) => {
   statements
}

param => expression
```

簡單的說明如下:

- 符號是肥箭頭符號(=>) (註: "->"是瘦箭頭)
- 基本上是"函式表達式(FE)的簡短寫法"

一個簡單的範例是：

```js
const func = (x) => x + 1
```

相當於

```javascript
const func = function (x) { return x + 1 }
```

所以你可以少打很多英文字元與一些標點符號之類的，函式是個匿名的函式。基本上的使用如下說明：

- 花括號({})是有意義的，如果函式有多行語句(表達式)時就要使用花括號，花括號中的`return`回傳值語句要自己寫。例如 `() => {}`
- 只有單一個傳入參數時，可以不需要左邊(前面)作為傳入參數使用的圓括號(())符號，例如 `x => x*x`

因此，初學者最容易搞混的是下面這個例子，因為有花括號({})與沒有是兩碼子事:

```js
const funcA = x => x + 1
const funcB = x => { x + 1 }

funcA(1) //2
funcB(1) //undefined
```

當沒使用花括號({})時，代表要會使用自動有`return`的作用，所以它也只能用在單一行的表達式的時候使用。使用花括號({})則是可以加入多行的語句，不過`return`不會自動加，有需要你要自己加上，沒加這個函式最後等於`return undefined`

> 註: 表達式與語句仍然有一些差異，像`throw "Error2"`是一個只有單一行的語句，但它並不能用於箭頭函式中無花括號的情況。

> 註: JS語言中函式的設計，必有回傳值，沒寫相當於回傳undefined

第二個會容易造成混亂與誤解的是，在肥箭頭符號(=>)的後面可以直接換行，下面指的是單行回傳表達式的情況，雖然不會造成錯誤但很難閱讀，所以不建議這樣寫。像下面這幾個的例子都是合法語法:

```js
// !! 不建議這樣寫 !!
const funcA = x =>
x + 1

// !! 不建議這樣寫 !!
const funcB =
x =>
x + 1
```

在這裡容易造成誤解，理由是肥箭頭符號(=>)的後面可以用換行，但"前面"不能直接接到換行，這個會造成編譯器無法編譯，瀏覽器也無法執行。我想主要原因是，畢竟這個符號是等號(=)與大於符號(>)組合而成的一個新符號，等號還有其他的用途。放在語句的前面應該就是等號的作用，再加上一個大於符號會造成語法錯誤。不管如何，別亂寫語法就對了，按照一般的你所看到的正常語法來寫就對了。

通常要換行或是寫成多行的語句時，則是要配合使用花括號或圓括號，但它們兩者在實際上的意義不同。在使用某些函式庫例如React時，你會看到在箭頭後面使用圓括號(())而非花括號({})，會使用圓括號(())會有兩個情況，第一個是仍然是有自動加retrun回傳的作用，但這個只有單一行的表達式在撰寫時區分為多行來寫，第二個是回傳值是個物件的類型值，因為物件的字面文字定義同樣也是用花括號({})，為了明顯區分出是物件的字面文字，所以用圓括號框住，這一點要相當注意。

React中因為使用了JSX語法，所以有可能你會看到一大串的JSX語法最外圍都會加上圓括號(())，例如下面的範例，我這裡只有寫一小串不過通常是一大串:

```js
const HelloWorld = (props) => (
  <div>
    <h1>{props.text}</h1>
  </div>
)
```

圓括號在JavaScript中的用途你應該有看過了一些，用於函式呼叫定義，if之類的語句中的表達式，以及它是一個群組運算符(Grouping operator)，可以明確地控制運算求值的優先次序，例如`((a + b) * c)`。在這裡的用法，則是形成一種未完結的語句，也就是撰寫時多行實際是單一整行的語句，這是由於按照JS中的ASI(Automatic Semicolon Insertion, 自動插入分號)機制，當讀到圓括號的開頭符號(`(`)會被認為這個語句尚未完成，並不會自動插入分號(;)來作語句的結束，一直到看到圓括號的結尾符號(`)`)，才會整個算作一個語句。這與使用花括號({})作為區塊語句的語法，或是函式的傳入參數寫成多行(下面的範例中就有這樣寫)有點類似，但意義不同。但用在這裡的情況仍然是箭頭函式有自動回傳的情況，也就是仍然屬於單一行的表達式。

不過，JSX語法畢竟只是一種簡寫語法，上面的範例透過babel編譯器後，它的程式碼相於下面這樣:

```js
"use strict";

var HelloWorld = function HelloWorld(props) {
  return React.createElement(
    "div",
    null,
    React.createElement(
      "h1",
      null,
      props.text
    )
  );
};
```

要補充一點的是，雖然與箭頭函式無關，但在React中也很常見到的一種JSX的寫法，就是使用圓括號開頭符號(`(`)接在`return`關鍵字的後面，尤其是在`render`函式中，例如像下面這樣的例子:

```js
class HelloWorld extends React.Component {
    render() {
        return (
          <div>
            <h1>{props.text}</h1>
          </div>
        )
    }
}
```

它的功能也是類似上面所說的，形成一個未完結的語句，讓JS認為這行語句尚未結束，而不會作ASI的來結束語句。如果return後面沒有加上圓括號的開頭符號(`(`)，ASI會起作用然後會幫你自動加上分號(;)，這將會造成語法錯誤或是不預期的結果。這一點要特別的小心注意。

> 註: 以上關於ASI(Automatic Semicolon Insertion, 自動插入分號)的機制，可以參考這篇我寫的[部落格文章](http://eddychang.me/blog/javascript/97-js-semicolon.html)。

最後，談到箭頭函式用到一種語法簡化的"**極致**"境界時，你會看到之前很少見的簡短語法，例如這一個之前在[討論區被問到的問題](https://segmentfault.com/q/1010000007320321/a-1020000007321437)，其中的內容是有關於Redux函式庫中的原始碼 - [compose.js](https://github.com/reactjs/redux/blob/89b0c254ad806c9707fe8f2d3cd78b191b30d2c6/src/compose.js)這個檔案，原始碼像下面這樣，因為沒有很長我整個列出來:

```js
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  funcs = funcs.filter(func => typeof func === 'function')

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
```

最後面一行用了兩個箭頭函式，一個是在`reduceRight`方法之中作為傳入參數，摘要出來是長下面這樣:

```js
(composed, f) => f(composed), last(...args)
```

這用了一種少看到的[逗號運算符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator)(,)，這個運算符的作用是可以組合多個表達式，變成單一個表達式，運算求值的順序是從左至右，這可以用兩個以上的表達式。一般在使用這個運算符會用圓括號框住外面，但這只是方便閱讀而已並非必要。

也就是說上面的`f(composed), last(...args)`，實際上是相當於先執行`f(composed)`，然後執行`last(...args)`，最後的箭頭函式的回傳是後面這個表達式的求值結果。

逗號運算符在簡單的`if...else`語句會看到有人(或是編譯工具)用來作簡寫語法，例如下面的例子:

```js
function f(){
  if(x){
   foo()
   return bar()
  }else{
   return 1
  }
}
```

會簡寫為下面的樣子(這是經過[Closure Compiler](https://closure-compiler.appspot.com/home)編譯後的結果):

```js
function f(){return x?(foo(),bar()):1}
```

你其實一定會看過逗號運算符，只是不知道它原來可以這樣用而已，最常見的就是在for語句中的那三個表達式的圓括號中。在其他情況下，這個並不算會常見到的一種語法，但就如上面說的，因為箭頭函式如果要有自動回傳的作用，肥箭頭的後面必定只能用單一個表達式，所以你有可能會看到有人用了逗號運算符，在某些為了讓程式碼寫得更簡潔的情況下，相信以後會愈來愈多人這樣用吧。

## `this`值的綁定

箭頭函式可以取代某些原有使用`self = this`或`.bind(this)`的情況，它可以在詞法上綁定`this`變數。但要視情況而定，而不是每種情況都一定可以用箭頭函式來取代。下面來簡單說明一下。

### 箭頭函式為什麼可以綁定this

一開頭要說的是，箭頭函式並不是完全只是原本JS中函式的縮寫語法而已，它與原本的函式內部設計有些不同，稱得上是改進過的函式。

箭頭函式與原本(傳統)的函式的不同之處，以下這兩點簡單說明:

- 沒有定義本地端綁定(local bindings)的以下幾個變數，也就是說這些變數將由lexical(詞法的)綁定: arguments, super, this, new.target
- 不能作為建構函式(constructor)使用，不能使用`new`

函式在JS中的功用很多，除了像一般的其他程式語言中的函式那樣使用外，函式也可以作為建構函式使用，這是JS中設計出來的四不像的以類別為基礎的物件導向語法，不得不說它又是一個容易造成混亂的設計，這邊我就不再多描述。

函式區塊中還有另一個隱藏的自動生成物件 - arguments。它是一個討論區中的常客，這個物件因為是隱藏的機制，除了是個"偽"陣列的物件，而且它與傳入參數有互相參照的作用，初學者如果用了它就像踏到坑裡面去，不是很容易能掌握它而且會常常出現問題。如果是以我的看法，"不要使用它"就是最佳的回答。連JavaScript的創造者[<NAME>](https://brendaneich.com/author/brendanmozilla-org/page/4/)都認為在ES6之後，這個物件應該逐漸要退出舞台，不需要再使用它。

在ES6之後，JS引進了一種新式的設計，稱為`Lexical this`(詞法的this)，下面簡單的說明一下。

一般而言，在JS中的預設this值，就是呼叫這個函式的物件，例如是`o.m()`，除非`m`是一個綁定方法(一般指bind這個方法)，不然this預設就是`o`。在ES5嚴格模式的作用下，一般的在詞法中函式呼叫(`f()`)的this預設都會是`undefined`，而不是window或全域物件。但如果預設的this值是`undefined`時，它也沒什麼太大的功用。<commit_msg>delete wrong section<commit_after>## ES6篇 - 箭頭函式

![intro](https://raw.githubusercontent.com/eyesofkids/ironman2017/master/day06_es6_arrow_func/asset/intro.png)

本章的目標是對箭頭函式提供一些較為全面性的介紹，除了基本的語法之外，也補充了很多React搭配使用時的實例，此外也提供撰寫的風格建議。當然，箭頭函式並不光只是語法簡短而已，它有一些與原來JavaScript中函式不同的設計，你可以把它當成是原本(傳統)的函式的改進版本。

> 註: 本文章同步放置於[Github庫的這裡](https://github.com/eyesofkids/ironman2017/tree/master/day06_es6_arrow_func)。

根據網路上統計的統計資料，箭頭函式(Arrow Functions)是ES6標準中，是最受歡迎的其中一種ES6新特性。它會受歡迎的原因是好處多多，只要注意在某些情況下不要用過頭就行了。有什麼好處呢？大致上有以下幾點:

- 語法簡單，少打很多字元
- 可以讓程式碼的可閱讀性提高
- 可以綁定詞法上的`this`值

> 註: 上述的統計可以參考[2ality](http://www.2ality.com/2015/07/favorite-es6-features.html)與[ponyfoo](https://ponyfoo.com/articles/javascript-developer-survey-results)這兩個網站的相關統計資料。

> 註: 本文大部份的基本語法內容是來自我電子書中的[箭頭函式](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/arrow_function.html)章節。

## 語法介紹

箭頭函式的語法如下，出自[箭頭函數(MDN)](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions)：

```js
([param] [, param]) => {
   statements
}

param => expression
```

簡單的說明如下:

- 符號是肥箭頭符號(=>) (註: "->"是瘦箭頭)
- 基本上是"函式表達式(FE)的簡短寫法"

一個簡單的範例是：

```js
const func = (x) => x + 1
```

相當於

```javascript
const func = function (x) { return x + 1 }
```

所以你可以少打很多英文字元與一些標點符號之類的，函式是個匿名的函式。基本上的使用如下說明：

- 花括號({})是有意義的，如果函式有多行語句(表達式)時就要使用花括號，花括號中的`return`回傳值語句要自己寫。例如 `() => {}`
- 只有單一個傳入參數時，可以不需要左邊(前面)作為傳入參數使用的圓括號(())符號，例如 `x => x*x`

因此，初學者最容易搞混的是下面這個例子，因為有花括號({})與沒有是兩碼子事:

```js
const funcA = x => x + 1
const funcB = x => { x + 1 }

funcA(1) //2
funcB(1) //undefined
```

當沒使用花括號({})時，代表要會使用自動有`return`的作用，所以它也只能用在單一行的表達式的時候使用。使用花括號({})則是可以加入多行的語句，不過`return`不會自動加，有需要你要自己加上，沒加這個函式最後等於`return undefined`

> 註: 表達式與語句仍然有一些差異，像`throw "Error2"`是一個只有單一行的語句，但它並不能用於箭頭函式中無花括號的情況。

> 註: JS語言中函式的設計，必有回傳值，沒寫相當於回傳undefined

第二個會容易造成混亂與誤解的是，在肥箭頭符號(=>)的後面可以直接換行，下面指的是單行回傳表達式的情況，雖然不會造成錯誤但很難閱讀，所以不建議這樣寫。像下面這幾個的例子都是合法語法:

```js
// !! 不建議這樣寫 !!
const funcA = x =>
x + 1

// !! 不建議這樣寫 !!
const funcB =
x =>
x + 1
```

在這裡容易造成誤解，理由是肥箭頭符號(=>)的後面可以用換行，但"前面"不能直接接到換行，這個會造成編譯器無法編譯，瀏覽器也無法執行。我想主要原因是，畢竟這個符號是等號(=)與大於符號(>)組合而成的一個新符號，等號還有其他的用途。放在語句的前面應該就是等號的作用，再加上一個大於符號會造成語法錯誤。不管如何，別亂寫語法就對了，按照一般的你所看到的正常語法來寫就對了。

通常要換行或是寫成多行的語句時，則是要配合使用花括號或圓括號，但它們兩者在實際上的意義不同。在使用某些函式庫例如React時，你會看到在箭頭後面使用圓括號(())而非花括號({})，會使用圓括號(())會有兩個情況，第一個是仍然是有自動加retrun回傳的作用，但這個只有單一行的表達式在撰寫時區分為多行來寫，第二個是回傳值是個物件的類型值，因為物件的字面文字定義同樣也是用花括號({})，為了明顯區分出是物件的字面文字，所以用圓括號框住，這一點要相當注意。

React中因為使用了JSX語法，所以有可能你會看到一大串的JSX語法最外圍都會加上圓括號(())，例如下面的範例，我這裡只有寫一小串不過通常是一大串:

```js
const HelloWorld = (props) => (
  <div>
    <h1>{props.text}</h1>
  </div>
)
```

圓括號在JavaScript中的用途你應該有看過了一些，用於函式呼叫定義，if之類的語句中的表達式，以及它是一個群組運算符(Grouping operator)，可以明確地控制運算求值的優先次序，例如`((a + b) * c)`。在這裡的用法，則是形成一種未完結的語句，也就是撰寫時多行實際是單一整行的語句，這是由於按照JS中的ASI(Automatic Semicolon Insertion, 自動插入分號)機制，當讀到圓括號的開頭符號(`(`)會被認為這個語句尚未完成，並不會自動插入分號(;)來作語句的結束，一直到看到圓括號的結尾符號(`)`)，才會整個算作一個語句。這與使用花括號({})作為區塊語句的語法，或是函式的傳入參數寫成多行(下面的範例中就有這樣寫)有點類似，但意義不同。但用在這裡的情況仍然是箭頭函式有自動回傳的情況，也就是仍然屬於單一行的表達式。

不過，JSX語法畢竟只是一種簡寫語法，上面的範例透過babel編譯器後，它的程式碼相於下面這樣:

```js
"use strict";

var HelloWorld = function HelloWorld(props) {
  return React.createElement(
    "div",
    null,
    React.createElement(
      "h1",
      null,
      props.text
    )
  );
};
```

要補充一點的是，雖然與箭頭函式無關，但在React中也很常見到的一種JSX的寫法，就是使用圓括號開頭符號(`(`)接在`return`關鍵字的後面，尤其是在`render`函式中，例如像下面這樣的例子:

```js
class HelloWorld extends React.Component {
    render() {
        return (
          <div>
            <h1>{props.text}</h1>
          </div>
        )
    }
}
```

它的功能也是類似上面所說的，形成一個未完結的語句，讓JS認為這行語句尚未結束，而不會作ASI的來結束語句。如果return後面沒有加上圓括號的開頭符號(`(`)，ASI會起作用然後會幫你自動加上分號(;)，這將會造成語法錯誤或是不預期的結果。這一點要特別的小心注意。

> 註: 以上關於ASI(Automatic Semicolon Insertion, 自動插入分號)的機制，可以參考這篇我寫的[部落格文章](http://eddychang.me/blog/javascript/97-js-semicolon.html)。

## `this`值的綁定

箭頭函式可以取代某些原有使用`self = this`或`.bind(this)`的情況，它可以在詞法上綁定`this`變數。但要視情況而定，而不是每種情況都一定可以用箭頭函式來取代。下面來簡單說明一下。

### 箭頭函式為什麼可以綁定this

一開頭要說的是，箭頭函式並不是完全只是原本JS中函式的縮寫語法而已，它與原本的函式內部設計有些不同，稱得上是改進過的函式。

箭頭函式與原本(傳統)的函式的不同之處，以下這兩點簡單說明:

- 沒有定義本地端綁定(local bindings)的以下幾個變數，也就是說這些變數將由lexical(詞法的)綁定: arguments, super, this, new.target
- 不能作為建構函式(constructor)使用，不能使用`new`

函式在JS中的功用很多，除了像一般的其他程式語言中的函式那樣使用外，函式也可以作為建構函式使用，這是JS中設計出來的四不像的以類別為基礎的物件導向語法，不得不說它又是一個容易造成混亂的設計，這邊我就不再多描述。

函式區塊中還有另一個隱藏的自動生成物件 - arguments。它是一個討論區中的常客，這個物件因為是隱藏的機制，除了是個"偽"陣列的物件，而且它與傳入參數有互相參照的作用，初學者如果用了它就像踏到坑裡面去，不是很容易能掌握它而且會常常出現問題。如果是以我的看法，"不要使用它"就是最佳的回答。連JavaScript的創造者[<NAME>](https://brendaneich.com/author/brendanmozilla-org/page/4/)都認為在ES6之後，這個物件應該逐漸要退出舞台，不需要再使用它。

在ES6之後，JS引進了一種新式的設計，稱為`Lexical this`(詞法的this)，下面簡單的說明一下。

一般而言，在JS中的預設this值，就是呼叫這個函式的物件，例如是`o.m()`，除非`m`是一個綁定方法(一般指bind這個方法)，不然this預設就是`o`。在ES5嚴格模式的作用下，一般的在詞法中函式呼叫(`f()`)的this預設都會是`undefined`，而不是window或全域物件。但如果預設的this值是`undefined`時，它也沒什麼太大的功用。
{'fraction_non_alphanumeric': 0.1546916159505859, 'fraction_numerical': 0.014806067762739577, 'mean_word_length': 8.031993437243642, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 68, 'https://': 16, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3842308', 'n_tokens_mistral': 8667, 'n_tokens_neox': 9628, 'n_words': 377}