### 函數參數的默認值
ES6允許為函數的參數設置默認值,即直接寫在參數定義的后面
```js
function log (x, y = 'World') {
console.log(x, y)
}
```
參數變量是默認聲明的,所以不能用 `let` 或 `const` 再次聲明。
#### 參數默認值的位置
通常情況下,定義了默認值的參數應該是函數的尾參數。
```js
function f (x, y = 1) {
// ...
}
```
#### 函數的 Length 屬性
指定了默認值后,函數的 `length` 屬性將返回沒有指定默認值的參數個數,也就是說指定了默認值后,`length` 屬性將失真
#### 作用域
一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域,等到初始化結束,這個作用域就會消失。
```js
var x = 1
function f (x, y = x) {
console.log(y)
}
f(2) // 2
```
### rest參數
ES6引入了 `rest` 參數(形式為 ...變量名),用于獲取函數的多余參數,這樣就不需要使用 `arguments` 對象了,`rest` 參數搭配的變量是一個數組,該變量將多余的參數放入其中。
```js
function add (...values) {
let sum = 0
for (var val of values) {
sum += val
}
return sum
}
add(2, 5, 3) // 10
```
`rest`參數中的變量代表一個數組,所以數組特有的方法都可以用于這個變量。
`rest`參數之后不能再有其他參數,否則會報錯。函數的 `length` 屬性不包括 `rest` 參數。
### 嚴格模式
從ES5開始,函數內部可以設定為嚴格模式
```js
function doSomething (a, b) {
'use strict'
// code
}
```
ES6規定只要函數參數使用了默認值、解構賦值或擴展運算符,那么函數內部就不能顯式的設定為嚴格模式,否則會報錯。
### name屬性
函數的 `name` 屬性返回該函數的函數名
ES6對這個屬性做出了一些修改。如果將一個匿名哈數賦值給一個變量,ES5的 `name` 屬性會返回空字符串,而ES6的 `name` 屬性會返回實際的函數名。
```js
var f = function () {}
// ES5
f.name // ""
// ES6
f.name // "f"
```
### 箭頭函數
ES6允許使用 "箭頭" ( => ) 定義函數
```js
var f = v => v
```
#### 注意事項
- 函數體內的 `this` 對象就是定義時所在的對象,而不是使用時所在的對象
- 不可以當做構造函數,也就是不可以使用 `new` 命令,否則會拋出一個錯誤。
- 不可以使用 `arguments` 對象,該對象在函數體內不存在。如果要用,可以用 `rest` 參數代替。
- 不可以使用 `yield` 命令,因此箭頭函數不能用作 `Generator` 函數。
#### 嵌套的箭頭函數
箭頭函數內部還可以再使用箭頭函數
### 綁定 this
箭頭函數可以綁定 `this` 對象,大大減少顯式綁定 `this` 對象的寫法 (`call`、`apply` 和 `bind`)。但箭頭函數并非適合所有場合。ES7提出了 "函數綁定" 運算符,用來取代 `call`、`apply` 和 `bind` 調用,Babel已支持。
函數綁定運算符是一個并排的雙冒號 (::),雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象作為上下文環境(即 `this` 對象)綁定到右邊的函數上。
```js
foo::bar
// 等同于
bar.bind(foo)
foo::bar(...arguments)
// 等同于
bar.bind(foo, arguments)
```
### 尾調用優化
#### 什么是尾調用
尾調用是函數式編程的一個重要概念。它指的是某個函數的最后一步是調用另外一個函數。
```js
function f (x) {
return g(x) // 最后一步調用函數g
}
```
#### 尾調用優化
尾調用的特殊在于其調用位置。函數調用會在內存形成一個調用記錄,又稱為 "調用幀",保存著調用位置和內部變量等信息。如果函數A的內部調用函數B,那么函數A的調用幀上方還會形成函數B的調用幀,等B的調用結束后,將結果返回給了A,B的調用幀才會消失,如果B的內部還調用了函數C,那么就還有一個C的調用幀,以此類推,所有的調用幀形成一個調用棧。
尾調用由于是函數的最后一步操作,所以不需要保留外層函數的調用幀,因為調用位置、內部變量等信息用不到了,直接用內層函數的調用幀取代外層函數的即可。
```js
function f () {
let m = 1
let n = 2
return g(m + n)
}
f()
// 等同于
function f () {
return g(3)
}
f()
// 等同于
g(3)
```
尾調用優化即只保留內層函數的調用幀,如果所有的函數都是尾調用,那么每次執行時調用幀只有一項,這將大大節省內存。
#### 尾遞歸
函數調用自身稱為遞歸,如果尾調用自身就稱為尾遞歸。遞歸非常耗內存,很容易發生棧溢出錯誤,對于尾遞歸來說,由于只存在一個調用幀,所以永遠都不會發生 "棧溢出"
```js
function factorial (n) {
if (n === 1) return 1
return n * factorial(n - 1)
}
factorial(5) // 120
// 改寫成尾遞歸
function factorial (n, total) {
if (n === 1) return total
return factorial(n - 1, n * total)
}
factorial(5, 1) // 120
```