# 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)
* 不會改變原始輸入參數,或是外部的環境,所以沒有副作用
* 不依頼其他外部的狀態(變數之類的)

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

## 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
```