Advanced Javascript
Table of Contents
Javascript Foundation
一、Call Stack && Memory Heap(內存堆)
1. Call Stack 與 Stack Overflow
圖像化觀察 Call Stack 與 Web Api 交互的網站
http://latentflip.com/
2. Memory Heap 與 garbege collection
3. Memory Heap 與 Memory leak
- 用越來越多的數據去填充內存堆,垃圾搜集機制不會起作用,因為 for 迴圈一直執行它。
4. 常見的記憶體洩漏
-
Global variable
使用越多全域變數,會佔用越多記憶體內存
-
event listener
如果你一直增加監聽器而不刪除他,特別是在 SPA 網頁用戶來回移動時,會一直創造新的監聽器。內存就會增加越來越多的事件監聽器
-
setInterval
setInterval 除非我們清除他,不然他都不會被垃圾回收
二、Execution Context
- 每一個 function 會產生一個 Execution Content,並以fist in last out 的方式一個一個加入 stack 中做執行(如下圖),等執行完後,stack 會清空,瀏覽器中的 Execution Content 又只剩下 Global Execution Content

- 瀏覽器一開始就會建立一個 Global Execution Content,裡面會有 Global Object 和 this

三、Lexical Environment
- Lexical Environment 指的是你在哪裡寫下程式碼
- 可以把 Lexical Environment 想成 Execution Context 創建時生成的星球裡面的東西

四、Hoisting

- Hoisting 是什麼呢?
- Hoisting 並不是時記得把程式碼移到最前面,而是 Javascript Engine 在 execution context 分配內存的動作。
- 如果 Excution Context 裡有 var 或 function 開頭的宣告,Javascript Engine 就會去 Memory Heap 內建立相對應的內存,
- 如果是 var Name,會在內存裡建立一個 Name 的位置,但因為尚未賦予值,因此 Name 的變數會先預設為 undefined
- 如果是 function 開頭的宣告,則會把所有 function 內容存到內存內
就像下面的例子,即使我在尚未宣告變數 name 和 function sayHi 前,我就可以讀取這個變數及函式
- 如何避免 Hoisting?
- 變數使用 let 和 const 宣告
- 函式使用 function expresion 且使用 let 或 const 宣告
- 使用 IIEF,當使用立即函式時,JS 分配記憶體的動作看到 (function(){..})() 的開頭不是 function,就不會將 IIEF 提升
- 每一個 Execution context 都會有 Hoisting 出現,像是一開始的 Global Execution Context 就包含了 Hoisting
- 下面的範例中,foodThoughts 這個 function 被呼叫時,他的 Execution context 伴隨著 Hoisting,他看到 var fovorFood,因此先將他放入內存中,暫時給予 undefined 的值
- 等到下一個 favorFood = 'sushi' 後,下一段 console.log favorFood 的值就會是 sushi
五、Function Invocation
六、arguments Keyword
- 每一個函式創造出的 exection contect 裡面都有一個 argument

- argument 並不是一個 array,他並不能使用使用 array 的方法遍歷

- 可以使用 Array.from(arguments) 或解構參數的方式將 argument 變成 array
七、Variable Environment
- execution context 會自成一個自己的變數環境,不受其他 execution context 影響

八、Scope Chain
function 透過 scope chain 去拿取父層的變數,一直會往外找到 global exection context

九、Function Scope vs Block Scope
scope 意味著我們可以訪問哪些變數
- function scope 指的是在 function { } 內的範圍,在 function 內的變數就無法讓外部訪問
- Block scope 指的是除了 function 外,其他的 { } 範圍,但這僅限於 ES6 後使用 let, const 來說。因為如果是使用 var,在 { } 外仍然可以訪問到 { } 內的變數 ⇢ 建議大部分都用 let 和 const 來築成 block scope
十、Global Variables
避免使用過多的全域變數,使用全域變數可能造成
- 過多的全域變數會造成 memory leaks
- 變數命名衝突
十一、IIFE
使用立即函式,可以避免污染全域環境
十二、this Keyword
1. object.someFunc(this)
物件內的函式,在函式內可以透過 this 訪問物件內的其他屬性或方法
- this 參照的對象主要是看哪一個物件呼叫他

- Javascript 中,Lexical Scope 決定我們可以取用的變數,不會因為變數是從哪裡調用它而有所不同,但是,this 不管 Lexical Scope Environment,他只看是誰呼叫他 ⇢ this屬於 dynamic scope
2. 如何解決 this 的動態 dynamic scope
- 使用 arrow function,arrow function 是 lexically 綁定的。他會綁定在該 lexically enviroment 的物件上
- 在該詞彙環境下,另宣告一個變數指向 this,因為在宣告的當下,this 還是在 obj 裡面
3. this 練習
箭頭函式只看詞彙環境 ⇢ 尖頭個性比較直,只會看當下的環境做事
一般函式看誰呼叫它 ⇢ 一般人,位高的人叫他做事他就做
十三、call(), apply(), bind()
- call(), apply() 用來呼叫函式
- bind() 則回返回一個函式
a() 現在習慣的函式呼叫,算是一種簡寫
- 使用 call 將 this 成其他物件, ex: 魔法師治療自己,魔法師也治療弓箭手
- call() 和 apply() 的用法主要差別在參數,call() 直接把參數代在對象物件後面,apply() 則需要在對象物件後面寫成陣列
十四、bind() and currying
使用 currying 重用一段程式碼,給他部分參數,並創建可擴展的函數
十五、Context vs Scope
Types In Javascript
一、Array.isArray()
如果用 typeof 去檢查 array,會顯示 Object
那有沒有什麼方法可以判斷型別為陣列呢?
可以使用 Array.isArray() 來判斷
二、Pass By Value vs Pass By Reference
1. 陣列拷貝 concat
2. 物件拷貝
- deep copy 深拷貝
但要注意,使用 JSON 深拷貝的方法有效能上的問題
- 如何比較來自不同位址的物件,但物件property是相同的
- function 內的參數是 passed by value
Colsures and Prototype
一、Functions are Objects
-
在 Javascript 中,Function 也是 Object 的一種,一種可以呼叫的 Object
-
callable Object 有特別的功能,如:bind, apply, call…
-
因為 Function 是 Object,所以他可以做很多有趣的應用,例如:跟物件一樣當成參數傳出去、可以把它當成資料存起來
二、First Class Citizens
1. function is first class citizens in JS
在 function 的參數裡加上預設值,以免參數出現 undefined 的狀況
四、Higher Order Functions
1. 什麼事 HOF
- 把函式當作參數傳入另一個函式
- function 回傳另一個 function
=> 抽象函示:把內容抽象化放到函式裡
2. Keep code DRY(Don't repeat yourself)
- 下面程式兩個函式幾乎執行的動作都是一樣的,只有跑 for 迴圈時的 loop 不同,該怎麼遵守 DRY 原則?
- 使用 arrow function 讓程式碼看起來更簡潔
五、Closures

closure 是函式與聲明他的詞法環境的結合
閉包允許函式從封閉的範圍或環境訪問變數,即使他離開了宣告他的範圍
六、Closures and Memory

closure 是在暫存堆內做執行,而不是在 call stack 內
七、Closures and Encapsulation
- Encapsulation 封裝
透過 closure,外界不必看到或可能被操縱的訊息則封裝在函式內
需要使用的屬性和方法則可以取用
在 c function 裡面有用到的變數,js就會認為你有用到它,並把它放進 closure
js 會保存任何有在子函式用到的變數 ⇢ closure
八、Solution: Closures
- 範例:settimeout for 迴圈
因為使用 var 宣告 i,i 是存在 global 環境中,沒有 function 把它包起來,再經過 3 秒過後,for 迴圈已經跑完了,這時 settimeout 裡面拿到的 i 就是跑完的值 4
使用 let 形成 block scope: 改用 let 宣告 i,因為 let 會形成 block scope,為每一個 i 創造一個範圍,把 i 保存在 block scope 內
使用 function 閉包形成 function scope:
形成 function scope 後就能把 i 保存在 scope 中
單純只用 var 沒有形成 scope,每次執行拿到的 i 都是 for 迴圈執行完後的值。
有使用 function scope 或
九、Closures Review
十、Prototypal Inheritance

Array 和 function 的原型繼承都是 object
1. 建構函數
透過__proto__可以沿著原型鍊往上找原型繼承

Array 的建構函數用來創造 Array 的變數
從 Array 的建構函數在往上找,可以找到 object 的建構函數
2. 繼承是一個對象可訪問另一個對象的屬性與方法
array 變數透過原型鍊可以使用 object 的方法 toLocalString()
3. 使用 prototype 有什麼好處?
object 可以共享 prototype,可以讓 object 指向內存的同一個位置,進而提高效率
4. function 的 prototype
-
一個 function 變數通常有這些屬性,但事實上 call(),apply(),bind() 不是存在 proterties 裡面

-
multiplyBy5 得 call(),apply(),bind() 是存在他的建構函式 Function 的 Prototype 裡面

- 按照上圖,multiplyBy5 的 proto 會連結到 Function 建構函數的 prototype,這兩個物件會是相同的
- Function 的 proto 會連接到 Object 的 prototype
- 如何使用 prototype
- 不要使用 proto 來訪問 prototype,在 JS 中會有效能上的問題
- 使用 Object.create() 來使用 prototype
- Excercise 1
下面的 lastYear 方法不存在 Date 的 prototype 裡面,要如何把它加進去呢
- Excercise 2
Mofify .map() to print '🗺' at the end of each item.
- Exercise 3
How would you be able to create your own .bind() method using call or apply.
十一、Solution: Prototypal Inheritance
十二、Imposter Syndrome
去教別人你所知道的知識,解決你的冒牌者症候群
Object Oriented Programming
一、OOP and FP
使用 OOP 和 FP 對程式碼的好處

二、OOP Introduction
三、OOP1: Factory Functions
試想,如果要創造出多個屬性及方法相似的物件要怎麼做
使用工廠函式
雖然工廠模式可以方便地創造出物件,但每一個物件都會多創造一個 attack 函式,但每個attack 函式都是一模一樣的,這樣會多占了記憶體位置,有沒有節省記憶體的方法呢?
四、OOP2: Object.create()
使用物件繼承的特性,繼承 attact 函式,這樣 attck 函式就不會重複佔用記憶體。需要使用 attck 時,只需透過原型鍊調用
五、OOP3: Constructor Functions
- Constructor Functions 命名需要使用大寫
- 對建構函式使用 new 會回傳 object
- new 會改變 this 的指向給回傳的 object
- 因為建構函式是函式,所以可以使用 prototype 來新增方法
六、More Constructor Functions
七、Funny Thing About JS…
技術上來說,除了 null 和 undefined,每個東西都是 object
當我們使用 var b = 5,在記憶體內分配變數時,他會建構數字,Javascipt 看到你想要使用對象方法,因此他會自動假定你的意思是 object 而不是 primitive
八、OOP4: ES6 Classes
- 透過 ES6 的 class 語法糖,可以把建構函式的屬性和方法都包在一起
- 方法不寫在 constructor 裡面,因為方法的程式碼是固定的,如果寫進去 constructor,在每次用 new 實體化一個 class 時,都會多創造一個方法,多佔了記憶體的空間
九、Object.create() vs Class
十、this - 4 Ways
- new binding this
使用在建構函式上
- implicit binding 隱性綁定
不做任何事情,this 自動綁定
- explicit binding 顯性綁定
用 bind, apply, call 指定 this 要綁定到哪裡
- arrow function
箭頭函式的 this 指向詞彙環境,與一般函式是看誰呼叫它的動態 scope 不同
十一、Inheritance
- 使用 extends 來繼承 prototype
- 在 child class 要使用 this,得先呼叫 super()
十二、ES2020: Private Class Variables
十三、Public vs Private
使用 # 來宣告私有變數
十四、OOP in React.js
十五、4 Pillars of OOP
十六、OOP and Polymorphism
Functional Programming
一、 Functional Programming Introduction
二、Exercise: Amazon
三、Pure Functions
容易測試
容易組合
No Side Effect
- 以下的程式碼會產生 side effects
下面的 function 都會更改 array 裡面的值
- 以下的程式碼修改為不會產生 side effects
同樣的 input slice 不管呼叫幾次,結果都是一樣的 ⇢ Pure Function
但 splice 儘管 input 一樣,但每次呼叫的值都不同 ⇢ Impure Function
四、Can Everything Be Pure?
Pure Function 的目標不是要讓所有程式碼變得沒有 side effects。
而是以某種方式組織程式碼,以便隔離 side effects
當發生錯誤時,我們能迅速排除 pure function(因為他們是 pure),找到可能會造成 side effect 的程式碼

- 只做一項任務
- 每個函式都要有 return
- Pure
- 沒有和其他程式碼分享的 state
- 不可修改 global state
- 可以組合的
- 可預測的
五、Idempotent
Idempotent 使程式碼是可預測的
- 儘管初始值被改了(impure),但不論呼叫多少次這個 function,回應的值都是一樣的
六、Imperative vs Declarative
七、Immutability
不改變數據,也不改變狀態
需要數據時,拷貝一份回傳新狀態
八、Higher Order Functions and Closures
九、Currying
十、Partial Application
使用 bind 來傳遞參數
十一、MCI: Memoization 1
使用物件的 key-value 屬性去做 cache,避免 function 重複執行
十二、MCI: Memoization 2: use curry
避免污染到全域變數,將 cache data 做成 curry
十三、Compose and Pipe
compose 像是工廠的傳輸帶的組合設計原則
data ⇢ fn ⇢ data ⇢ fn ⇢ data
- compose fn 可以組合函式,同時執行多個函式的功能,執行方向由右而左
- pipe fn 的功能相同,但執行方向由左而右
十四、Arity
fn 帶的參數最好不要超過兩個,超過兩個參數的 fn 很難做組合
十五、Is FP The Answer To Everything?
十六、Solution: Amazon
OOP vs FP
一、Composition vs Inheritance
使用 Composition 模擬出 class 方法
二、OOP vs FP
Asynchronous JavaScript
一、How JavaScript Works
二、Promises
Promise 是一個 Object,他在未來產生的三個狀態,resolve、reject、pending
- Promise 的出現是為解決 callback hall
1. Promise 起手式
三、ES8 - Async Await
四、ES9 (ES2018)
五、ES9 (ES2018) - Async
六、Job Queue
七、Parallel, Sequence and Race
八、ES2020: allSettled()
九、ES2021: any()
十、Threads, Concurrency and Parallelism
Modules In JavaScript
一、 What Is A Module?
二、Module Pattern
- module pattern 回傳出 public api,大家都可以使用
- module pattern 可以取用 global 變數,但是不更改變數
三、Module Pattern Pros and Cons
Module Pattern 仍然有兩個缺點
- 仍然污染到全域變數,雖然污染的變數變少了
- 命名的名稱可能會衝突
四、CommonJS, AMD, UMD
- CommonJS 是同步的模組,通常使用在 server 端,像是:node.js
- 因為他是同步的,我們會使用 webpack 或 browserify 將所有的模組捆成一包,他們會知道模組的先後順序,誰要引用誰。
- AMD 是非同步的模組,使用在瀏覽端
- UMD 同時使用 CommonJS 和 AMD
五、ES6 Modules
- 使用 import 來輸入 module,link
- 使用 export 來輸出 module,link
- script 需要加入 module 的 type
Error Handling
一、Errors In JavaScript
- 當使用 throw 時。目前運行的腳本就會停止
- Error 有三個物件可以使用,name, message, stack
- 透過 stack 我們可以知道,程式碼哪裡出錯
二、Try Catch
- try catch 只能用在同步,非同步的程式碼要另外處理
三、 Async Error Handling
非同步的錯誤捕捉
- Promise catch(),確保每個 Promise 務必要有 catch
- async await + try catch,Promise 沒使用 catch() 捕捉錯誤時,可以加上 try catch 捕捉錯誤
四、 Extending Errors
Data Structures In JavaScript
一、What Is A Data Structure?
二、How Computers Store Data
三、Data Structures In Different Languages
四、Operations On Data Structures
五、Array Introduction
六、Static vs Dynamic Arrays
七、Implementing An Array
八、Strings and Arrays
九、Solution Reverse A String
十、Solution: Merge Sorted
十一、Arrays Review
十二、Hash Tables Introduction
十三、Hash Function
十四、Hash Collisions
十五、Hash Tables In Different Languages
十六、Solution: Implement A Hash Table
十六、keys()
十七、Hash Tables vs Arrays
十八、Solution: First Recurring Character
十九、Hash Tables Review
歷代 ES fn Introduce
一、ES7
二、ES8
- padStart && padEnd 固定數目在字串前留空 & 在字串後留空
- Object.keys & Object.values & Object.entries
三、ES9
四、ES10
- trimStart & trimEnd 去除前後空白
五、Advanced Loops
- for in 使用在可枚舉的"物件"
類似於 Object.keys
六、ES2020
- BigInt
在超過最大安全數字(九千兆)時,在數字後面加 n,代表他是 bigint,這樣做算出才不會有問題
- Option Chaining Operator ?.
用來確認是否物件內的 key 值是否存在,存在才拿取,不存在則回應 undefined
- Nulllish Coalescing Operator ??
用來取代 || (or),因為我們常常會判斷 0, '', false 在物件中的值為不存在
- globalThis
為了統一在不同系統 node 和瀏覽器中使用的變數,
但 globalThis 在 node 中就等於 global
globalThis 在瀏覽器中就等於 this
七、ES2021
- replaceAll
原本的 replace 功能只會替換第一個找到的字串,但 replaceAll 會替換全部
八、 debugger
使用 debugger 來終止程式碼
九、
十、
十一、
十二、
十三、
十四、
十五、
十六、
一、
二、
三、
四、
五、
六、
七、
八、
九、
十、
十一、
十二、
十三、
十四、
十五、
十六、
一、
二、
三、
四、
五、
六、
七、
八、
九、
十、
十一、
十二、
十三、
十四、
十五、
十六、
一、
二、
三、
四、
五、
六、
七、
八、
九、
十、
十一、
十二、
十三、
十四、
十五、
十六、
一、
二、
三、
四、
五、
六、
七、
八、
九、
十、
十一、
十二、
十三、
十四、
十五、
十六、