---
###### tags: `JavaScript` `程式筆記`
---
# JavaScript 筆記整理
<!-- > [time=Sun, Apr 4, 2021 10:00 PM] -->
:::info
> [time=Wed, Aug 3, 2022 9:21 AM]
內容搬移至 [notion](https://www.notion.so/JavaScript-6344f85cb1634f0a98e0bff286ef7167)
:::
[深入淺出前端開發](https://f2e.kalan.dev/javascript-basic/2.html#ecmascript)
* ECMAScript 是規格,JavaScript 是實作。同樣實作了 ECMAScript 的還有 ActionScript
* ECMAScript 每年都會發布一次
* ECMAScript 成為正式規格需要經過五個階段
* 每位開發者都可以提出草案
## 直譯式語言
[【JS課程筆記】-直譯式語言 vs 編譯式語言](https://medium.com/ivycodefive/js%E8%AA%B2%E7%A8%8B%E7%AD%86%E8%A8%98-%E7%9B%B4%E8%AD%AF%E5%BC%8F%E8%AA%9E%E8%A8%80-vs-%E7%B7%A8%E8%AD%AF%E5%BC%8F%E8%AA%9E%E8%A8%80-ab584edaa762)
> 直譯器轉換過程 (以變數為例)
> 1. 將語法基本單元化 :
變數還未被定義、賦予值,只是先將結構中的關鍵字一一取出來。
> 1. 轉為抽象結構樹 :
轉為像是JSON的資料格式,將整個結構樹定義出來,需特別注意,程式碼在此階段還未真正運行。
> 1. 代碼生成 :
會因執行環境不同 (例如使用瀏覽器,或使用Node.js),生成的代碼也會有點不同,在代碼生成之後才會真正運行。
## `var` & `let` & `const`
推薦觀看直播:[直播共筆](https://www.notion.so/JS-var-let-const-6c4d85fc28fc42a9aa2b6c303b276748)
{%youtube FGdKdn_CnWo %}
### 為什麼要宣告變數?
如果不宣告,都會變成全域變數。造成全域污染
```javascript=
// 沒有宣告變數
a = 0;
function fn(){
a = 1;
}
console.log(a); // output: 1
```
```javascript=
// 有宣告變數,這邊 a 的作用域就只在 fn() 內
function fn() {
var a = 0;
}
console.log(a); // output: a is not defined
```
如果不宣告,這個 a 會變成 window 的「屬性」,宣告了就是「變數」,屬性可以被刪除,變數不行
```javascript=
a = 0;
delete window.a;
console.log(window); // 可以看到 a:0 是屬性
console.log(a); // a is not defined
let b = 0;
delete window.b;
console.log(b); // 0
```
### 作用域

<!-- > photo by 六角學院課程 -->
JaveScript 屬於靜態作用域,又稱為語法作用域。
變數的作用域在語法解析時,就已經確定作用域,且不會再改變。

<!-- > photo by 六角學院課程 -->
### `var` 跟 `let` 差異
重複宣告
```javascript=
// var 可以重複宣告
var a = 0;
var a = 1;
console.log(a); // 1
// let 已經宣告就過不能再重複宣告
let a = 0;
let a = 1;
console.log(a); // Identifier 'a' has already been declared
```
block 作用域
```javascript=
// var 沒有 block 作用域,只有分全域(global) 跟 function(local)
{
var a = 1;
}
console.log(a); // 1
// let 有 block 作用域
{
let b = 2;
}
console.log(b); // b is not defined
```
for 也算是一個 block
```javascript=
for (var i = 0; i < 10; i++) {
}
console.log(i); // 10
for (let j = 0; j < 10; j++) {
}
console.log(j); // j is not defined
```
上例延伸
```javascript=
for (var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i); // 10 次(0~9) 10
}, 0);
}
// 輸出 10 因為 setTimeout 屬於非同步行為
// JS 默認在所有事件結束後才會執行非同步行為
// 所以執行 setTimeout 時 i 已經跳出 for 了
for (let j = 0; j < 10; j++) {
setTimeout(() => {
console.log(j); // 0~9
}, 0);
}
// 這邊我們知道在 for 迴圈裡使用 var 時 setTimeout 都是輸出 10
// 但 let 卻可以正常輸出 0~9
// 原因就是 let 屬於 block 作用域 這讓每個 i 都獨立存在於 block 內
// 這也是為何 let 比 var 穩定很多的原因
// 另外這是一個常見考題 下次遇到就知道怎麼做囉~~(用 let 啦哪次不用)
```
[hoisting\*](#hoisting) 和 [暫時性死區(TDZ)\*](#let-跟-const-的-hoisting)
```javascript=
console.log(a); // undefined
var a = 1;
console.log(a); // 1
console.log(b); // Cannot access 'a' before initialization
let b = 1;
```
:::info
結論:用 `let` 取代 `var`
:::
### `const`
不可重新賦值
```javascript=
const a = 1;
a = 3;
// Assignment to constant variable.
```
物件傳參考的特性
```javascript=
// 可以改變 a 的 value
const a = {
name: 'me',
}
a.name = 'etc'
console.log(a); // {name: 'etc'}
// 不能將 a 重新指向
const a = {
name: 'me',
}
a = {
name: 'etc',
}
// Assignment to constant variable.
```
:::info
結論:確定不會重新賦值的,能用 `const` 就不要用 `let`
:::
### 使用時機
`var`, `let`, `const` 都有其使用時機,如果不確定怎麼用,可以使用規範如:[ESLint](https://eslint.org/docs/user-guide/getting-started) 來協助,VSCode 有套件而且預設就有安裝,使用教學:[在 VSCode 啟用程式碼規範工具 (ESLint)](https://www.casper.tw/tool/2017/11/09/coding-style/)

:::info
備註:ESLint 規範並**不鼓勵**使用 `var`
:::
## ES6,7,8,9,10,11中,用一次就上癮的語法
[共筆](https://paper.dropbox.com/doc/ES67891011--BjL8XWN7qwQxxMeFAdWJYgdyAg-oUCvWdcmTSKsiuGzbTjZC)
### 物件迴圈
使用 `for...in` 會印出物件原型
```javascript=
Object.keys(family).forEach((key) => {
console.log(key, family[key]);
})
```
### `||` 與 `??`
* 真值(truthy)、假值(falsy)
在 a 值不確定的情況下,給予 100,空字串、數字0、undefined 和 null 會是 Falsy,只要是 falsy 會給予 100
```javascript=
function fn(a){
const output = a || 100;
console.log(output);
}
fn(0); // 100
```
ES11 之後可以用空位合併 Nullish coalescing operator (??),只有 undefined 和 null 會給予 100
```javascript=
function fn(a){
const output = a ?? 100;
console.log(output);
}
fn(0); // 0
```
### 參數預設值
不過如果是使用在變數上,可以用預設值(ES6),此時只有 undefined 會給予 100
```javascript=
function fn(a = 100){
const output = a;
console.log(output);
}
```
### 具名參數
給常常忘記參數傳入順序的你
```javascript=
let height, weight;
function multiply({height, weight}) {
return height * weight;
}
multiply({height: 40, weight: 50});
multiply({weight: 10, height: 90});
```
### 其餘參數(rest parameters)
我懶得打,[我抄的](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/function_scope.html#%E4%B8%8D%E5%9B%BA%E5%AE%9A%E5%82%B3%E5%85%A5%E5%8F%83%E6%95%B8variadic%E8%88%87%E5%85%B6%E9%A4%98rest%E5%8F%83%E6%95%B8)
```javascript=
function sum(...value) {
let total = 0
for (let i = 0 ; i< value.length; i++){
total += value[i]
}
return total
}
console.log(sum(1, 2, 3)) //6
console.log(sum(1, 2)) //3
console.log(sum(1, 2, 3, 4)) //10
console.log(sum('1', '2', '3')) //123
console.log(sum('1', '2')) //12
console.log(sum('1', '2', '3', '4')) //1234
```
### optional chaining(可選串連)
不知道有沒有這個物件,又不想讓程式報錯(就會停止執行)
```javascript=
const obj = {
name: '小明',
// family: {
// name: '小明家',
// obj: {
// name: '111'
// }
// }
}
console.log(obj?.family?.obj?.name);
```
## 陣列的操作
* reduce 累加
```javascript=
arr.reduce(callback[accumulator, currentValue, currentIndex, array], initialValue)
```
> 當回呼函式第一次被呼叫時,`accumulator` 與 `currentValue` 的值可能為兩種不同的狀況:
> 若在呼叫 `reduce()` 時有提供 `initialValue`,則 `accumulator` 將會等於 `initialValue`,且 `currentValue` 會等於陣列中的第一個元素值
> 若沒有提供 `initialValue`,則 `accumulator` 會等於陣列的第一個元素值,且 `currentValue` 將會等於陣列的第二個元素值。
```javascript=
[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );
```
* map 遍歷陣列會回穿一個新陣列
## hoisting
關於基本概念以及為什麼要有 hoisting 可以參考:[JavaScript: 变量提升和函数提升](https://www.cnblogs.com/liuhe688/p/5891273.html)
> 通常 JS 引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明及函数声明提升至当前作用域的顶端,然后进行接下来的处理。
深扒 hoisting 推薦 Huli 的:[我知道你懂 hoisting,可是你了解到多深?](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
* 只有宣告會 hoisting,賦值不會
* hoisting 順序
* `function` 的宣告
* `function` 的參數
* `var`
### 參數的 hoisting
```javascript=
function fn(v) {
console.log(v);
var v = 3;
}
fn(10); // 10
```
調整一下執行順序,注意這是想像,實際上並不會真的改變程式碼執行順序
```javascript=
function fn(v) {
var v = 10; // 參數
var v;
console.log(v);
v = 3;
}
fn(10);
```
### function 的 hoisting
```javascript=
function hoistFunction() {
foo();
var foo = function() {
console.log(1);
};
foo();
function foo() {
console.log(2);
}
foo();
}
hoistFunction(); // output: 2, 1, 1
```
調整後
```javascript=
// 预编译之后
function hoistFunction() {
// 宣告會 hoisting,順序是 function 先 var 後
function foo() {
console.log(2);
}
var foo;
// 剩下的不會 hoisting 所以按照寫的順序
foo(); // 2
foo = function() {
console.log(1);
};
foo(); // 1
foo(); // 1
}
hoistFunction();
```
:::info
結論:无论是早期的代码,还是ES6中的代码,我们都需要遵循一点,先声明,后使用。
:::
### let 跟 const 的 hoisting
> let 與 const 確實有 hoisting,與 var 的差別在於提升之後,var 宣告的變數會被初始化為 undefined,而 let 與 const 的宣告不會被初始化為 undefined,而且如果你在「賦值之前」就存取它,就會拋出錯誤。
>
> 在「提升之後」以及「賦值之前」這段「期間」,如果你存取它就會拋出錯誤,而這段期間就稱做是 TDZ,它是一個為了解釋 let 與 const 的 hoisting 行為所提出的一個名詞。
<!-- https://github.com/nightn/front-end-plan/blob/master/js/js-scope.md
https://www.cnblogs.com/leoo2sk/archive/2010/12/19/ecmascript-scope.html
https://realdennis.medium.com/%E6%87%B6%E4%BA%BA%E5%8C%85-javascript%E4%B8%AD-%E4%BD%BF%E7%94%A8let%E5%8F%96%E4%BB%A3var%E7%9A%843%E5%80%8B%E7%90%86%E7%94%B1-f11429793fcc
https://ithelp.ithome.com.tw/articles/10209121
https://www.geeksforgeeks.org/difference-between-var-and-let-in-javascript/ -->
## async/sync
基本概念釐清:[非同步(Asynchronous)與同步(Synchronous)的差異](https://medium.com/@hyWang/%E9%9D%9E%E5%90%8C%E6%AD%A5-asynchronous-%E8%88%87%E5%90%8C%E6%AD%A5-synchronous-%E7%9A%84%E5%B7%AE%E7%95%B0-c7f99b9a298a),直接複製:
> :x: ~~Node.js的處理方式是Sync~~
> :heavy_check_mark: Node.js的處理方式是Async
>
> :x: ~~Async是一次處理很多需求~~
> :heavy_check_mark: Async是接收到需求,不用一直等到需求完成再執行其他需求
### Event queue (事件佇列)
[一次只能做一件事的 Javascript](https://www.casper.tw/javascript/2017/12/07/javascript-event-queue/)
* 事件堆疊會逐一執行
* 等到執行到不會立即執行的行為,就會放到 event queue,event queue 的事件在**所有事件完成前**不會被執行。
* 等到所有事件堆疊完成後 event queue 內的事件才能被觸發。
測試
```javascript=
function doWork() {
var auntie = "漂亮阿姨";
(function () {
console.log('打給阿姨')
setTimeout(function () {
console.log(auntie + '回電')
}, 0) // 修改成 0 秒
})();
// ...
console.log('洗碗');
return '完成事件堆疊';
}
doWork(); // 執行
```
結果

在所有非同步事件如 `click`、`setTimeout`、`ajax`...,都不會立即執行這些行為,而是將這些行為放到 event queue 中,等待事件觸發後再回來執行。
### 從 callback 到 promise 到 async/await
{%youtube CTChl_DYTz0 %}
[輕鬆理解 Ajax 與跨來源請求](https://blog.huli.tw/2017/08/27/ajax-and-cors/)也有提到,我自己是在看過文軒的影片之後才得到這篇,好理解
## 物件傳值與傳參考
### 前置觀念:除了原始型別以外,其他都是物件型別
* 原始型別
* boolean
* null
* undefined
* string
* number
* symblo
* 物件型別
* object
* array
* [typeof 的一些誤區](https://www.casper.tw/javascript/2017/12/08/javascript-typeof/#:~:text=%E5%9E%8B%E5%88%A5%E3%80%82-,Undefined%20%E8%88%87%20Null,-%E9%80%99%E5%85%A9%E5%80%8B%E5%B0%B1)
### 原始型別傳值(value),物件型別傳參考(reference)
[物件連連看,兩個物件的值竟然一樣?!](https://www.casper.tw/javascript/2017/12/10/javascript-reference/)
```javascript=
let a = { id: 1 };
let b = a;
b.id = 2;
console.log(a.id); // 2
```
### 複製物件
* 淺度複製 (Shallow Copy)
* 手動賦值
* for...in
* jQuery `$.extend`
* 原生方法 `assign()`
* 深度複製 (Deep Copy)
* 轉字串
* jQuery `$.extend`
差異在於第一層傳值,還是所有層級皆傳值,以下例 `pet` 做範例
```javascript=
/* 原始資料 */
let auntie = {
name: "陳小美",
age: 36,
isSingle: true,
pet: {
cats: 3,
dogs: 2,
},
};
console.log(auntie.pet.cats); // 3
```
shallow copy
```javascript=
/* 手動賦值 */
let auntie_copy = {
name: auntie.name,
age: auntie.age,
isSingle: auntie.isSingle,
pet: auntie.pet,
};
// 改一下複製的值
auntie_copy.pet.cats = 9;
// 會發現原始資料也被改了
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 9
```
```javascript=
/* for...in */
let auntie_copy = {};
for (const prop in auntie) {
auntie_copy[prop] = auntie[prop];
}
auntie_copy.pet.cats = 9;
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 9
```
```javascript=
/* jQuery $.expend */
let auntie_copy = $.extend({}, auntie);
auntie_copy.pet.cats = 9;
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 9
```
```javascript=
/* assign() */
let auntie_copy = Object.assign({}, auntie);
auntie_copy.pet.cats = 9;
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 9
```
deep copy
```javascript=
/* 轉字串 */
let str = JSON.stringify(auntie);
let auntie_copy = JSON.parse(str);
auntie_copy.pet.cats = 9;
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 3
```
```javascript=
/* jQuery $.expend */
let auntie_copy = $.extend(true, {}, auntie);
auntie_copy.pet.cats = 9;
console.log(auntie_copy.pet.cats); // 9
console.log(auntie.pet.cats); // 3
```
## this
:::danger
這部分還沒整理完,短期內可能也不會整理
:::
[淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂](https://blog.techbridge.cc/2019/02/23/javascript-this/)
* 嚴格模式底下就都是 `undefined`
* 非嚴格模式
* 瀏覽器底下是 `window`
* node.js 底下是 `global`
[what is THIS in javascript?](https://kuro.tw/posts/2017/10/12/What-is-THIS-in-JavaScript-%E4%B8%8A/)
* `this` 是 JavaScript 的一個關鍵字。
* `this` 是 function 執行時,自動生成的一個內部物件。
* 隨著 function 執行場合的不同,`this` 所指向的值,也會有所不同。
* 在大多數的情況下, `this` 代表的就是**呼叫 function 的物件 (Owner Object of the function)**。
[javascript 的 this 到底是誰?](https://www.casper.tw/javascript/2017/12/12/javascript-this/)
如果直接調用函式,此函式的 this 會指向 window,即使 function 內在包覆著 function,但只要是直接呼叫,this 都是屬於全域。
```javascript=
window.auntie = '漂亮阿姨';
function callAuntie () {
console.log('call:', this.auntie); // 漂亮阿姨
// function 內的 function
function callAgainAuntie () {
console.log('call again:', this.auntie); //漂亮阿姨
}
callAgainAuntie();
}
callAuntie();
```
如果 function 是在物件下調用,那麼 this 則會指向此物件,無論 function 是在哪裡宣告。
```javascript=
function callName() {
console.log(this.name);
}
var name = "全域阿婆";
var auntie = {
name: "漂亮阿姨",
callName: callName,
// 這裡的 function 指向全域的 function,但不重要
};
callName(); // '全域阿婆'
auntie.callName(); // '漂亮阿姨',呼叫是在物件下調用,那麼 this 則是該物件
```
還有很多範例解釋參考 [what is THIS in javascript?](https://kuro.tw/posts/2017/10/12/What-is-THIS-in-JavaScript-%E4%B8%8A/),我懶得打
```javascript=
var obj = {
func1: function () {
console.log(this); // 這裡的 this 是 func1 的一個物件
var func2 = function () {
console.log(this); // 這裡的 this 是全域
};
func2();
},
};
obj.func1();
```
小結
* this 不是 function 本身
* this 也不是 function 的 scope
* this 與 function 在何處被宣告完全無關,而是取決於 function 被呼叫的方式
* this 指的是,當 function 執行時,這個 scope 的 owner
* 當 function 是某個物件的 method,this 指的就是上層物件
* 全域變數的上層物件就是 window
## 閉包
循序漸進的好解釋 [[JS] 深入淺出 JavaScript 閉包(closure)](https://pjchender.dev/javascript/js-closure/)@pjchender
> 一般來說function執行結束時,裡面資源(記憶體)會被釋放。
> 閉包就是function會return 一個function,常應用於當有複雜的計算的function時,一旦輸入值是一樣的,重複呼叫時不會重新計算
> 來源:[所有的函式都是閉包:談 JS 中的作用域與 Closure](https://blog.huli.tw/2018/12/08/javascript-closure/) @huli
```javascript=
// 非閉包
function complex(num){
return num*num*num
}
complex(20) //計算一次
complex(20) //計算兩次
complex(20) //計算三次
```
```javascript=
//運用閉包
function complex(num){
return num*num*num
}
function cache(func){
var ans = {} // 閉包應用的關鍵,在外面呼叫function但是ans還是會記住答案
return function() {
if(ans[num]) {
return ans[num]
}
ans[num] = func[num] // 依此例ans[20]=complex(20)
return ans[num]
}
}
const cachedComplex = cache(complex)
cachedComplex(20) //計算一次,並紀錄參數=20時的答案
cachedComplex(20) //回傳答案
cachedComplex(20) //回傳答案
```
:::danger
-----------------未整理分隔線----------------以下是約莫一年前的推積灰塵區
:::
## function
* 函式只是物件的一種
* 可以將 function 儲存成變數
* 可以將 function 當成參數代入另一個 function 中
* 可以在一個 function 中回傳另一個 function
* function 跟物件一樣有屬性(property)
* 可以用`.`看到屬性
### ref
https://pjchender.blogspot.com/2016/03/javascriptfunctionobjects.html
## Function Expressions vs Function Declarations(function statement)
### 先看看[函式要用那種定義方式比較好?](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/function_scope.html#%E5%87%BD%E5%BC%8F%E8%A6%81%E7%94%A8%E9%82%A3%E7%A8%AE%E5%AE%9A%E7%BE%A9%E6%96%B9%E5%BC%8F%E6%AF%94%E8%BC%83%E5%A5%BD%EF%BC%9F)
- 多數情況下,兩者執行結果一樣
- Expressions有回傳值
- statement沒有回傳值,譬如if不能被指定成一個變數
- 不能這樣 let a = if(){};
```javascript=
// 存在很久了,可以callback
function funcDeclaration() {
return 'A function declaration';
}
// 沒宣告不能用,var有hoisting但內容是undefined,執行會出錯
var funcExpression = function () {
return 'A function expression';
}
```
### 好處
https://www.sitepoint.com/function-expressions-vs-declarations/
- As closures
- As arguments to other functions
- As Immediately Invoked Function Expressions (IIFE) https://pjchender.blogspot.com/2016/05/javascriptiifesimmediately-invoked.html
- 在我們建立函式的同時,這段函式就會立即被執行了!
- 在這種情況下,這個 function 會被建立,但是不會被存在任何變數當中,也不會被執行。
- 透過這樣的方式,我們可以「直接執行某個函式(executing code on the fly)」,還有很重要的一點是,在 IIFEs 內所定義的變數並不會跑出去這個函式之外而干擾到程式其他的部分----->被建立在function的這個execution context 中,而不是被建立在Global Execution Context中
也許可以理解為跟*let的出現*一樣是為了程式碼更乾淨、易懂,避免污染全域環境???
---
教科書
* [從ES6開始的JavaScript學習生活](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/)
* [IT幫鐵人賽-JavaScript之一定要了解的 Array 與方法](https://ithelp.ithome.com.tw/articles/10219475)
### Array
* pop()從後面刪
* push()從後面加
* shift()從前面刪
* unshift()從前面加
* slice()
* splice()
* const array: only the variable name and structure type are constant.不能reassign為空array,用pop或shift慢慢刪(用function跑)
[assign array](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/array.html#%E6%8B%B7%E8%B2%9Dcopy%E9%99%A3%E5%88%97)
判斷是否為array
```javascript=
Object.prototype.toString.call(variable) === '[object Array]'
```
### loop
Three Loop questions:
1. What do I want to repeat?
-> The text function with the message!
2. What do I want to change each time?
-> The y position, increasing by 20 each time.
3. How long should we repeat?
-> As long as y is less than 400, all the way down.
for 適用明確循環
for (start; how long; change)
while 判斷為true就執行
do...while loops run code at least once— only checking the stopping condition after the first execution 先執行再判斷
---
## Higher-order functions
> When you pass a function as an argument, remember not to use parenthesis.
Right: myCalculator(5, 5, myDisplayer);
Wrong: myCalculator(5, 5, myDisplayer());
* 追求重複使用,撇開邏輯
* 抽象化:寫得越死就越不能重複使用,命名也不宜太過針對
{%youtube BMUiFMZr7vk%}
https://medium.com/functional-javascript/higher-order-functions-78084829fff4
```javascript=
function add(x) {
return function(y) {
return x + y
}
}
var add2 = add(2)
var add3 = add(3)
add2(4) //==>> 6
add3(6) //==>> 9
```
> The function **add** takes a single variable x and returns a function, which in turn takes a single variable y and returns the sum of x and y.
**add** is a **Function Factory**. Given a value, it creates a function, that adds a given value to a value stored in its closure.
So why not just write add as **add(x, y)**? This is surely simpler. Writing functions as function factories allow you to compose other functions. We will see more of this below. And in the chapter on composition.
其實就是把複雜變簡單,只是多數範例為了好懂都用簡單的範例,讓人覺得把簡單變簡單多此一舉
:::warning
### 閉包[上移*](#閉包)
:::
[非常簡單的說明](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/function_scope.html#%E5%85%A7%E9%83%A8%E5%B7%A2%E7%8B%80%E5%87%BD%E5%BC%8F)
```javascript=
function addOuter(a, b) {
function addInner() {
return a + b
}
return addInner()
}
addOuter(1, 2) //3
```
> 內部函式可以獲取到外部函式所包含的環境值,例如外部函式的傳入參數、宣告變數等等。而內部函式又可以成為外部函式的回傳值,所以當內部函式接收到外部函式的環境值,又被回傳出去,內部函式間接變成一種可以讓函式對外曝露包含在函式內部環境值的溝通管道,這種結構稱之為"閉包(closure)"。
[Higher Order Functions - Closures:](https://medium.com/functional-javascript/higher-order-functions-78084829fff4) Creating closures should come naturally to a JavaScript programmer. If not, keep practising closures. We will be making a lot of closures in this book. You should recognise them even if it is not always explicitly mentioned.
### callback
[w3cschool](https://www.w3schools.com/js/js_callback.asp)
```javascript=
function myDisplayer(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalculator(num1, num2) {
let sum = num1 + num2;
myDisplayer(sum);
}
myCalculator(5, 5);
```
有可能在`sum = num1 + num2`執行完之前就執行`myDisplayer(sum)`,所以需要callback確保`myDisplayer(sum)`最後執行,
> Where callbacks really shine are in asynchronous functions, where one function has to wait for another function (like waiting for a file to load).
>
> Using a callback, you could call the calculator function (myCalculator) with a callback, and let the calculator function run the callback **after** the calculation is finished

> 回調(callback)提供了使用此函式的開發者一種彈性的機制,讓程式開發者可以自行定義在此函式的最後完成時,要如何進行下一步,這通常是在具有執行流程的數個函式的組合情況。實際上,回調函式實現了JavaScript中非同步(asynchronously)的執行流程,這使得原本只能從頭到底(top-to-bottom)的程式碼,可以在同時間執行多次與多種不同的程式碼。[出自:從ES6開始學習JavaScript](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/function_scope.html#%E5%9B%9E%E8%AA%BFcallback)
#### Codecademy - The .forEach() Method

```javascript=
groceries.forEach(groceryItem => console.log(groceryItem));
```
這個 callback 函式(.forEach()括號內的函式)將會把 Array 中的每一個元素作為參數,帶進本 callback 函式裡,每個元素各執行一次
1. groceries `.forEach()` calls the forEach method on the groceries array.
1. `.forEach()` takes an argument of callback function. **Remember, a callback function is a function passed as an argument into another function.**
1. `.forEach()` loops through the array and executes the callback function for each element. During each execution, the current element is passed as an argument to the callback function.
1. The return value for `.forEach()` will always be undefined.
[你懂 JavaScript 嗎?#23 Callback](https://cythilya.github.io/2018/10/30/callback/)
### 常見使用Map、Reduce
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/array.html#%E8%BF%AD%E4%BB%A3
https://fred-zone.blogspot.com/2017/01/javascript-mapreduce.html
https://ithelp.ithome.com.tw/articles/10235812
## Object
[ref:Codecademy](https://www.codecademy.com/courses/introduction-to-javascript/lessons/objects/exercises/pass-by-reference)
> when we pass a variable assigned to an object into a function as an argument, the computer interprets the parameter name as pointing to the space in memory holding that object. As a result, functions which change object properties
```javascript=
let spaceship = {
homePlanet : 'Earth',
color : 'red'
};
let tryReassignment = obj => {
obj = {
identified : false,
'transport type' : 'flying'
}
console.log(obj) // Prints {'identified': false, 'transport type': 'flying'}
};
tryReassignment(spaceship) // The attempt at reassignment does not work.
spaceship // Still returns {homePlanet : 'Earth', color : 'red'};
spaceship = {
identified : false,
'transport type': 'flying'
}; // Regular reassignment still works.
```
```javascript=
let spaceship = {
'Fuel Type' : 'Turbo Fuel',
homePlanet : 'Earth'
};
// Write your code below
let greenEnergy = (obj) => {
obj = {
'Fuel Type':'avocado oil' //不會改變
}
}
let remotelyDisable = (obj) => {
obj.disabled = true //會改變
}
```
- scope作用域=>變數的生存範圍
- 作用域會往上尋找變數(形成「作用域鏈scope chain」:注意這個function在哪個scope是以被宣告的位置為主,而非被呼叫的位置)
- 注意:如沒有宣告變數,JS會自動宣告為全域變數
- ES5:以function為單位,內部宣告的變數外部無法讀取