# What is closure?
###### tags: `Javascript` `note`
Closure 在 Javascript 之中無處不在,但卻難以用簡單白話的方式說明這什麼,多數都是以範例的方式呈現,而大多詳細解說都會涵蓋較艱澀難懂的名詞(例如 Lexical Environment),就連 MDN 也不例外,直到我在 [Eloquent JavaScript](https://eloquentjavascript.net/) 這本書中找到了較為直覺易懂的解釋,再搭配其他教材能讓你更好的理解 closure 怎麼運作。
[Eloquent JavaScript Chapter 3 Functions Closure](https://eloquentjavascript.net/03_functions.html#h_hOd+yVxaku)
>This feature—being able to reference a specific instance of a local binding in an enclosing scope—is called ***closure***. A function that references bindings from local scopes around it is called a ***closure***.
「 一個引用其周圍局部作用域的綁定的函數被稱之為 ***closure*** 」。
那何謂**綁定**? **變數**、**函數**、**參數**都可以視為**綁定**。
```javascript
function wrapFn(a){ // argument
const b = 10; // variable
// function
function fn(){
console.log("hello function");
}
// arrow function
const fn2 = () => {
console.log("hello arrow function")
};
return () => {
const c = b * a;
fn();
fn2();
}
}
const wrap = wrapFn(10);
wrap();
```
這個 wrap function 就可稱為一個 closure,因為它引用了自己以外的作用域的**綁定**。
而另一個特性是,每當你執行 `wrapFn` 都會產生新的作用域,所以返回的 function 引用的綁定都會是新的。
### 各自獨立的作用域
我們以最常見的計數器當範例:
```javascript
function createCounter(){
let count = 0;
return {
increment:() => { counter++; },
getCount:() => count
}
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment();
counter1.increment();
counter2.incrament();
counter1.getCount() // 2
counter2.getCount() // 1
```
那這有什麼作用? 想想在其他語言有什麼是不能被外部讀取,並且只能由公有方法來操作的東西?
答案就是「 **私有變數/方法** 」,在 **ES11** 之前,JS **class** 並沒有辦法做到真正的私有變數功能,只能依賴開發者的自律性,直到在 [**ES11**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields#syntax) 新增了 `#` 符號表示私有變數或方法。
一般常見的作法是加上標記以利於識別,但其實還是可以直接引用。
```javascript
class Human{
_hands = 2;
constructor(){
}
}
const human = new Human();
console.log(human._hands) // 2
```
ES11 加上 `#` 前綴,當你直接引用時會發生錯誤
```javascript
class Human{
#hands = 2;
constructor(){
}
}
const human = new Human();
console.log(human.#hands) // syntax error
```
即便 `class` 有了私有變數功能,在 JS 使用 closure 還是相當常見。
### Lazy calculation
惰性計算,在 functional programming 中是相當重要且常見的技巧,而因為 JS 有了 closure 特性,使其在 FP 的表現力更上一層樓,讓 function 的**組合**更靈活。
利用 **Lazy calculation** 再產出更多不同的 function:
```javascript
function add(a){
return function(b){
return a + b;
}
}
const add10 = add(10);
const add20 = add(20);
console.log(add10(4)) // 14
console.log(add20(6)) // 26
```
利用 **Lazy calculation** 來描述一個流程的每個動作,以 Ramda 為例:
```javascript
const process = R.pipe(R.map(n => n * n), R.filter((n) => n % 2 === 0), R.sum);
console.log(process([1, 2, 3, 4])) // [1, 2, 3, 4] -> [1, 4, 9, 16] -> [4, 16] -> 20
```
### Rust closure
在 Rust,closure 又是什麼規則呢? 其分成**參考**跟**移動所有權**,如果為將一個變數放到 closure function 裡面,都會默認為參考,而在前面加上 `move` 關鍵字則會將**所有權**移入 closure function 內。
因為一般的數字都會做 copy,所以有沒有加 `move` 沒有區別,這裡我將使用 String 作為範例。
這裡因為 print_a 只有對 **a** 做引用,所以第二個 print 並不會出錯,不過要注意的是,如果將 a 作為 print_a 的回傳值,還是會把所有權拿走。
```rust
fn main(){
let a = "hello world".to_string();
let print_a = || {
println!("{}", a);
};
print_a();
println!("{}", a);
}
```
加上 `move`
```rust
fn main(){
let a = "hello world".to_string();
let print_a = move || {
println!("{}", a);
};
print_a();
println!("{}", a);
}
```
加上 `move` 之後代表我要求將所有變數所有權移入,所以在第二個 print 的 a 的值早已被 print_a 移走,所以會發生錯誤。
這只是一個簡單的範例,實際上 rust closure 的狀況還是相當複雜,所以還是多留意。
### 參考
讀懂了 Eloquent JavaScript 之後,可以在更進階的去讀其他深入的文章。
- [MDN Closure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)
- [Javascript.info Lexical Environment](https://javascript.info/closure#lexical-environment)