---
# System prepended metadata

title: Eloquent JavaScript 3rd edition (2018) 第三章~Functions
tags: [Javascript]

---

# Eloquent JavaScript 3rd edition (2018) 第三章~Functions

---
tags: Javascript relate
---

###### tags: `Javascript`

> People think that computer science is the art of geniuses but the actual reality is the opposite, just many people doing things that build on each other, like a wall of mini stones.
> 
> Donald Knuth

## Defining a function

> A function definition is a regular binding where the value of the binding is a function. 

function的定義是 一個正常的binding但是他的value被指派為function

下方範例就很明顯: 指定binding "square" 為funciton(x)

```javascript=
const square = function(x) {
  return x * x;
};

console.log(square(12));
// → 144
```

function的body一定要被包裹在{}之間就算只有單行的statement

function可以有很多參數(parameters)也可以一個都沒有

沒有參數的例子:
```javascript=
const makeNoise = function() {
  console.log("Pling!");
};

makeNoise();
// → Pling!
```

兩個參數的例子:
```javascript=
const power = function(base, exponent) {
  let result = 1;
  for (let count = 0; count < exponent; count++) {
    result *= base;
  }
  return result;
};

console.log(power(2, 10));
// → 1024
```

return 這個statement確定了functions回復的結果

如果return後面沒有接上expression的話會回復undefined


## Bindings and scopes(範圍)

每個binding都有範圍，也就是binding可以造訪並且有作用的範圍。

有一種binding是全域的，整段程式都可以使用稱作**global**

但是在function裡面的binding只能給function使用稱作 **local bindings**

function裡面的binding比較被隔離的感覺並不知道外面的global的環境

一般來說binding只會作用在他所在的block裡面，所以當你把binding設立在loop裡面時，前跟後的程式碼是無法使用它的




下方這個例子可以看出就算global的 n=10寫在上方 halve(100)還是只看它裡面的n的值去做處理:


```javascript=
const halve = function(n) {
  return n / 2;
};

let n = 10;
console.log(halve(100));
// → 50
console.log(n);
// → 10
```


## Nested scope

巢狀的範圍簡單來說就是function裡面還有一個function一層一層包裹住呈現巢狀

```javascript=
const hummus = function(factor) {
  const ingredient = function(amount, unit, name) {
    let ingredientAmount = amount * factor;
    if (ingredientAmount > 1) {
      unit += "s";
    }
    console.log(`${ingredientAmount} ${unit} ${name}`);
  };
  ingredient(1, "can", "chickpeas");
  ingredient(0.25, "cup", "tahini");
  ingredient(0.25, "cup", "lemon juice");
  ingredient(1, "clove", "garlic");
  ingredient(2, "tablespoon", "olive oil");
  ingredient(0.5, "teaspoon", "cumin");
};
```

## Functions as values

一個binding宣告了一個function後如果他不是constant，他可以再設一個新的value:

```javascript=
let launchMissiles = function() {
  missileSystem.launch("now");
};
if (safeMode) {
  launchMissiles = function() {/* do nothing */};
}
```

## Declaration notation(宣告符號)

這是一個比較簡短的方式去創造一個function binding

```javascript=
function square(x) {
  return x * x;
}
```
下方的程式碼解釋了這種簡短的寫法(function declaration)，不需要遵循從頭到尾這樣的規則，並且可以被所有的程式碼使用，很多時候這樣的自由是有相當意義的。

```javascript=
console.log("The future says:", future());

function future() {
  return "You'll never have flying cars";
}
```

## Arrow functions

一邊箭頭用在下面這個地方:
一般來說箭頭使用在參數(parameter)之後，必且後方接著function的body，表達這個參數產生這個結果(body)

```javascript=
const power = (base, exponent) => {
  let result = 1;
  for (let count = 0; count < exponent; count++) {
    result *= base;
  }
  return result;
};
```

當只有一個參數得時候可以忽略括號()，body的部分如果只有一個表達式(expression)得時候可以這樣簡寫:

```javascript=
const square1 = (x) => { return x * x; };
const square2 = x => x * x;
```

當arrow function沒有參數的時候可以直接寫一():

```javascript=

const horn = () => {
  console.log("Toot");
};
```

arrow function就是個讓funcition寫起來更簡潔

## The call stack

計算機必須記得那些宣告的內容，像是下方例子中的console.log()以及greet都是那些地方

計算機記得要印出東西的內容存放的地方就叫 call stack，當function return的時候計算機會移除最上方的內容並且使用其內容來繼續執行

存放call stack需要消耗電腦容量，當stack變得太多，電腦會顯示“out of stack space” or “too much recursion”.

```javascript=
function greet(who) {
  console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
```
下方是個無解的蛋先生還是雞先生的問題，電腦一定會滿載

跑出下面這條因為他是個無限的迴圈，同時搞砸這個stack
`RangeError: Maximum call stack size exceeded`


``` javascript=
function chicken() {
  return egg();
}
function egg() {
  return chicken();
}
console.log(chicken() + " came first.");
// → ??
```

## Optional Arguments

從下方程式碼可以看出js對於argument的內容放得很寬，會自動抓取他認定的數值去執行。
甚至有可能打錯了，也因為它會自動抓取的關西而沒有發現錯誤。

```javascript=
function square(x) { return x * x; }
console.log(square(4, true, "hedgehog"));
// → 16
```

好的面相有:
讓function可以被不同的argument called

``` javascript=
function minus(a, b) {
  if (b === undefined) return -a;
  else return a - b;
}

console.log(minus(10));
// → -10
console.log(minus(10, 5));
// → 5
```
下方演示了 (=) 這個operator在參數中出現的功能，當那個有(=)的參數沒有被給予value的時候，(=)後方的值會直接取代進去: 

``` javascript=
function power(base, exponent = 2) {
  let result = 1;
  for (let count = 0; count < exponent; count++) {
    result *= base;
  }
  return result;
}

console.log(power(4));
// → 16
console.log(power(2, 6));
// → 64
```

## Closure(閉包)

閉包是指變數的生命週期只存在於該函式內，一旦離開了函式，該變數就會被回收而不可再利用，且必須在函式內事先宣告。

下面這個情況說明了function裡面local binding`let local = n`就是個閉包請他的內容不會離開function，並且這樣的宣告可以重新創造在每一次的宣告中，然後不一樣的宣告不會踐踏之前的local bindings

> This situation is a good demonstration of the fact that local bindings are created anew for every call, and different calls can’t trample on one another’s local bindings.

``` javascript=
function wrapValue(n) {
  let local = n;
  return () => local;
}

let wrap1 = wrapValue(1);
let wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2
```

下方這段程式碼表示了當宣告產生的時候，function的body應該去看環境創造的地方而不是他宣告的地方:

```javascript=
function multiplier(factor) {
  return number => number * factor; //  創造的環境
}

let twice = multiplier(2); // 因此他必須帶入factor為2
console.log(twice(5));
// → 10
```


## Recursion

當一個funciton宣告自己的時候就是recursion

>  A function that calls itself is called recursive.

用這個方法寫求冪exponentiation就是個例子:

```javascript=
function power(base, exponent) {
  if (exponent == 0) {
    return 1;
  } else {
    return base * power(base, exponent - 1);
  }
}

console.log(power(2, 3));
// → 
```

下面的程式碼是個求解的過程=>如何呈現13:

```javascript=
function findSolution(target) {
  function find(current, history) {
    if (current == target) {
      return history;
    } else if (current > target) {
      return null;
    } else {
      return find(current + 5, `(${history} + 5)`) ||
             find(current * 3, `(${history} * 3)`);
    }
  }
  return find(1, "1");
}

console.log(findSolution(13));
// → ((1 * 3) + 5) + 5
```
這邊是他如何求出13的過程:

``` javascript=
find(1, "1")
  find(6, "(1 + 5)")
    find(11, "((1 + 5) + 5)")
      find(16, "(((1 + 5) + 5) + 5)")
        too big
      find(33, "(((1 + 5) + 5) * 3)")
        too big
    find(18, "((1 + 5) * 3)")
      too big
  find(3, "(1 * 3)")
    find(8, "((1 * 3) + 5)")
      find(13, "(((1 * 3) + 5) + 5)")
        found!
```


## Growing functions

以下兩種方法用來幫助農家定位家中的牲畜數字，農家希望家中的動物數字都要是三位數，所以當數字是3的時候它們希望可以呈現003，故有兩種呈現在下方:


第一種寫法比較不推薦，`printZeroPaddedWithLabel` 名稱太奇怪難以閱讀理解，結合太多想法太貪心了

```javascript=
function printZeroPaddedWithLabel(number, label) {
  let numberString = String(number);
  while (numberString.length < 3) {
    numberString = "0" + numberString;
  }
  console.log(`${numberString} ${label}`);
}

function printFarmInventory(cows, chickens, pigs) {
  printZeroPaddedWithLabel(cows, "Cows");
  printZeroPaddedWithLabel(chickens, "Chickens");
  printZeroPaddedWithLabel(pigs, "Pigs");
}

printFarmInventory(7, 11, 3);
```

第二種寫法就很簡潔的只有zeropad來表示加上0這個想法，比較好閱讀之外更有可能把這個function做別的用途泛用性比較廣。

```javascript=
function zeroPad(number, width) {
  let string = String(number);
  while (string.length < width) {
    string = "0" + string;
  }
  return string;
}

function printFarmInventory(cows, chickens, pigs) {
  console.log(`${zeroPad(cows, 3)} Cows`);
  console.log(`${zeroPad(chickens, 3)} Chickens`);
  console.log(`${zeroPad(pigs, 3)} Pigs`);
}

printFarmInventory(7, 16, 3);
```

這邊它推薦一種原則，不要讓function名稱太靈活，讓他的概念單一，除了好讀之外也更增加泛用性。




## Functions and side effects



### 純粹函式(pure function):

* 只要每次給定相同的輸入值(例如1與2)，就一定會得到相同的輸出值(例如3)
* 不會改變原始輸入參數，或是外部的環境，所以沒有副作用
* 不依頼其他外部的狀態(變數之類的)

![](https://i.imgur.com/OGCDA9H.png)


### 不純粹的函式(impure function):它需要依賴外部的狀態值(變數值):

會產生副作用

![](https://i.imgur.com/exV0GDQ.png)


## Summary


三種寫function的方式:

```javascript=
// Define f to hold a function value
const f = function(a) {
  console.log(a + 2);
};

// Declare g to be a function
function g(a, b) {
  return a * b * 3.5;
}

// A less verbose function value
let h = a => a % 3;
```


## Exercises

### Minimum

比出兩個數字哪個比較小使用 寫出一個min的function:

```javascript=
function min(a, b) {
  if (a < b) return a;
  else return b;
}

console.log(min(0, 10));
// → 0
console.log(min(0, -10));
// → -10
```

### Recursion

看無第三點之後再來!

判斷奇數偶數 藉由三個條件:

* 0是偶數

* 1是奇數

* 任意數字-2依舊會是一樣的狀態(奇數還是奇數偶數還是偶數)

```javascript=

function isEven(n) {
  if (n == 0) return true;
  else if (n == 1) return false;
  else if (n < 0) return isEven(-n);
  else return isEven(n - 2);
}

console.log(isEven(50));
// → true
console.log(isEven(75));
// → false
console.log(isEven(-1));
// → false
```

### Bean counting

找出單字間有幾個 字母 比方說B 有幾個

問題一 讓參數只有一個

問題二 參數變兩個 第一個參數是要找的單字 第二個是要找的字母


看得懂但是寫不出來我哭

``` javascript=
function countChar(string, ch) {
  let counted = 0;
  for (let i = 0; i < string.length; i++) {
    if (string[i] == ch) {
      counted += 1;
    }
  }
  return counted;
}

function countBs(string) {
  return countChar(string, "B");
}

console.log(countBs("BBC"));
// → 2
console.log(countChar("kakkerlak", "k"));
// → 4
```