# All About JavaScript
## Execution Contexts & Lexical Environment
*Syntax Parser*(語法解析器)
> 讀取程式碼並確認語法是否正確,並將其轉譯成電腦理解的語言。
*Lexical Environment*(詞彙環境)
> 程式碼在程式中的<u>**實際所在位置**</u>,會影響其對應的記憶體位置與其他變數或函式的互動。
*Execution Context*(EC,執行語境)
> JS 引擎模組化直譯程式碼時的區塊環境。
- Global Execution Context(GEC,全域執行語境)
> 在執行任何程式之前,預設建立的 EC,會建立 global object(全域物件,以 web browser 來說就是 `window`)和 `this`,此時 `window` = `this`。所有在 GEC 下 `window` 物件的屬性,都可以直接指稱而不需要使用 dot-notation。
- Function Execution Context(FEC,函式執行語境)
> 在函式執行時候會各別為函式建立的專屬 EC,故可能同時存在多個 FEC。
*Hoisting*(提升)
> 在 Creation Phase(創造階段)時,替變數(設為 `undefined`)和函式<u>**註冊記憶體空間**</u>。
> [我知道你懂 hoisting,可是你了解到多深?](https://blog.huli.tw/2018/11/10/javascript-hoisting-and-tdz/)
*Single Thread*(單線程)
> 一次執行一件事情。
*Asynchronous*(非同步)
> 當執行一件事情時,可以同時執行其他事情。
> [JavaScript 中的同步與非同步(上):先成為 callback 大師吧!](https://blog.huli.tw/2019/10/04/javascript-async-sync-and-callback/)
*Scope*(作用域)
> 作用域是一個<u>**變數的生存範圍**</u>。一旦出了這個範圍,就無法存取到這個變數。
>
> 透過 Outer Environment(外部環境)由 inner EC 往 outer EC 尋找,最後就形成 Scope Chain(作用域鏈)。
## Types and Operators
*Dynamic Typing*(動態型別)
> Runtime(執行環境)才會進行型別檢查、變數能任意更換型別,且程式撰寫時不用明確的型別宣告。
*Primitive Type*(原始型別)v.s. *Object*(物件)
> "Primitive types are manipulated <u>**by value**</u> and object types are manipulated <u>**by reference**</u>."
> [來數數 JavaScript 的所有資料型別](https://blog.huli.tw/2022/02/25/javascript-how-many-types/)
> [使用 JavaScript 的數字時的常見錯誤](https://blog.huli.tw/2022/03/14/javascript-number/)
*Operator*(運算子)
> 一種語法特殊的<u>**函式**</u>,一般來說會傳入兩個參數並回傳一個結果。
>
> 運算子優先順序和相依性表請見:[Operator precedence](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Operator_precedence)。
*Coercion*(強制轉型)
> 將一個值的型別轉換成另一種型別。
>
> 相等比較表請見:[Equality comparisons and sameness](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness)。
>
> Falsy Value(假值)請見:[Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)。
> [從「為什麼不能用這個函式」談執行環境(runtime)](https://blog.huli.tw/2022/02/09/javascript-runtime/)
> [如何不用英文字母與數字寫出 console.log(1)?](https://blog.huli.tw/2020/12/01/write-conosle-log-1-without-alphanumeric/)
## Objects and Functions
*Namespace*(命名空間)
> 一個裝著變數與函式的容器,避免相同名稱的變數或函式互相干擾。
>
> JS 並沒有 namespace,所以可以用物件作為 Faking Namespace(偽命名空間)。
*JSON*(JavaScript Object Notation)
> 一種由 JS Object Literal(物件實字)衍生出的資料交換格式,較 XML(Extensible Markup Language)易懂且資料體積小。通常 API 和 config 皆使用 JSON。
>
> key 需用<u>**雙引號**</u>包住,且型別只能為 `string`。
>
> 常見方法:JS -> `JSON.stringify()` -> JSON -> `JSON.parse()` -> JS
*First-Class Function*(一級函式)
> <u>**可被視作變數**</u>的函式(一種特殊的物件),包含指定為變數、作為參數傳遞、可被回傳等行為。JS 的函式皆為 first-class function。
>
> 當函式傳遞時特性與物件相同(因為本來就是物件),當要執行時,用括號去 invoke 函式中的 code。
*High-Order Function*(HOF,高階函式)
> 當一個函式能夠<u>**接受函式作為參數**</u>或者<u>**回傳函式**</u>時,就被稱為 HOF。HOF 是抽象函式的一種,不包含執行細節,而是在 high-level 的角度描述解決方法。
*Function Statement / Declaration*(函式陳述式)
> 執行後不會回傳任何值,且在執行前會將整個函式都存進記憶體,所以會 hoisting。
```javascript
functionStatement()
function functionStatement() {
console.log(`This is function statement`)
}
```
*Function Expression*(函式表達式)
> 執行後<u>**會回傳一個值**</u>,所以可以存成一個變數或作為參數傳遞。由於只有變數會被存取,所以不會 hoisting。
>
> 函式表達式的函式不需要名稱,此時此函式稱為 Anonymous Function(匿名函式)。
```javascript
const functionExpression = function() {
return `This is function expression`
}
functionExpression()
```
> [覺得 JavaScript function 很有趣的我是不是很奇怪](https://blog.huli.tw/2020/04/18/javascript-function-is-awesome/)
*By Value v.s. By Reference*
> 當建立一個 primitive type 變數 a 且指定另一個變數 b = a 時,b 會建立另一個獨立的記憶體位置並將 a 的值存於此。故彼此之間不會互相影響,此為 by value。

> 當建立一個 object 變數 c 且指定另一個變數 d = c 時,d 會將其指定到 c 的記憶體位置。故彼此之間會互相影響,此為 by reference。

> 但若用 object literal 的方式指定 object 的值,則會產生 by value 的行為,建立一個新的記憶體位置。
> [深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?](https://blog.huli.tw/2018/06/23/javascript-call-by-value-or-reference/)
*`this`*
> `this` 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟<u>**你如何呼叫**</u>有關。
> 當某個函式是放在某一個物件裡面時,那麼該函式裡面的 `this` 指稱的就是該<u>**物件本身**</u>。而當脫離物件,在嚴格模式下的預設值是 `undefined`,非嚴格模式下是全域物件(瀏覽器底下是 `window`,node.js 底下是 `global`)。
> 當位於 Arrow Function(箭頭函式)內時,在<u>**宣告此函式**</u>的地方的 `this` 是什麼,它的 `this` 就是什麼。
> [淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂](https://blog.huli.tw/2019/02/23/javascript-what-is-this/)
*Immediately Invoked Function Expression*(IIFE,立即執行函式運算式)
> 若要建立一個 anonymous function 且不設為變數,要用括號將函式包起來,因若 syntax parser 開頭讀到 `function` (會以為是 function statement)又未讀到函式名稱,便會拋出錯誤。因此,使用 function expression 加上括號並直接 invoke,便可以做出 IIFE。
> 透過 IIFE 可以 executing code on the fly(直接執行程式),且<u>**在 IIFE 內的變數不會影響到函式外部**</u>。
```javascript
(function(concept) {
return `This is ${concept}!`
})('IIFE')
```
*Closure*(閉包)
> <u>**當一個 function 執行完畢且其 EC 已經離開 execution stack,這個 EC 仍會存在於當下的記憶體位置。**</u>因此,JS engine 可以透過 scope chain 去取得 outer environment reference,也就是存在 EC 內的變數。此時,因為現在執行的 EC 跟之前存在的變數 close 在一起,故稱為閉包。
> 由於 `var` 是 function scope,所以要避免污染可以透過 block scope 的 `let` / `const` ,或者是 IIFE 來達成。
```javascript
function buildFunctions(n) {
var arr = []
// --------------------
// let
for (let i = 0; i < n; i++) {
arr.push(
function() {
return i
}
)
}
// --------------------
// IIFE
for (var i = 0; i < n; i++) {
arr.push(
(function(j) {
return function() {
return j
}
})(i)
)
}
// --------------------
return arr
}
var fs = buildFunctions(3)
fs[0]() // 0
fs[1]() // 1
fs[2]() // 2
```
> [所有的函式都是閉包:談 JS 中的作用域與 Closure](https://blog.huli.tw/2018/12/08/javascript-closure/)
*Callback Function*(回呼函式)
> 將一個函式作為參數傳入另一個函式,且在傳入之函式執行結束時執行。
*`bind()` / `call()` / `apply()`*
> 當想要<u>**動態調整函式中 `this` 所指稱的對象**</u>時可用,為 JS 內建在函式中的方法。
> `bind()` 是回傳綁定 `this` 後的原函式(並未執行),當被綁定後其 `this` 便不能再被修改。而 `apply()` 和 `call()` 則是回傳函式執行結果。
```javascript
var person = {
firstname: 'Len',
lastname: 'C.',
getFullName: function() {
return `${this.firstname} ${this.lastname}`
}
}
var logNameWithLocation = function(location) {
return `${this.getFullName()} at ${location}`
}
// bind
logNameWithLocation.bind(person)('Taiwan')
// call
logNameWithLocation.call(person, 'Taiwan')
// apply
logNameWithLocation.apply(person, ['Taiwan'])
```
> function borrowing 是借用特定物件中的方法,而 function carrying 是複製特定函式並賦予預設參數值,常用於算數上。
```javascript
// function borrowing
var tank = {
name: 'King Tiger II',
fire: function() {
return 'boom!'
}
}
var car = {
name: 'Lightning McQueen'
}
tank.fire.bind(car)()
tank.fire.call(car)
tank.fire.apply(car)
// function currying
function multiply(a, b) {
return a * b
}
var multiplyByTwo = multiply.bind(this, 2)
multiplyByTwo(3) // 6
var multiplyByThree = multiply.bind(this, 3)
multiplyByTwo(3) // 9
```
*Functional Programming*(FP,函式語言程式設計)
> [Functional Programming in JS](https://ithelp.ithome.com.tw/users/20106426/ironman/3024)
## Object-Oriented Javascript, Prototypal Inheritance and Building Objects
*Inheritance*(繼承)
> 一個物件可以使用其他物件的 property(屬性)或 method(方法)。繼承分為兩種,一種是 classical inheritance(類別繼承),用於 C# 或 JAVA 等;另一種則是 prototypal inheritance(原型繼承),用於 JS。
*Prototype*(原型)
> 所有 JS 對象皆預設包含 prototype 屬性(名稱為 `__proto__`),而沿著 prototype chain 找到最後的 prototype 皆為物件 -> 「萬物皆物件」。
>






*Prototype Chain*(原型鏈)
> 當尋找指定對象的 property 或 method 但對象本身並未擁有時,會往其 prototype 去找,故以 prototype 構成的鏈便稱為 prototype chain。可以用 `instanceof` 去確認對象是否在 prototype chain 中。
> [該來理解 JavaScript 的原型鍊了](https://blog.huli.tw/2017/08/27/the-javascripts-prototype-chain/)
*Reflection*(反射)
> 一個可以觀測、列出並改變自身 property 或 method 的物件,可以藉由 reflection 來實現 Extend(擴展)。
```javascript
// source code of 'extend' from Underscore.js
var extend = createAssigner(allKeys)
function createAssigner(keysFunc, defaults) {
return function(obj) {
var length = arguments.length
if (defaults) obj = Object(obj)
if (length < 2 || obj == null) return obj
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length
for (var i = 0; i < l; i++) {
var key = keys[i]
if (!defaults || obj[key] === void 0) obj[key] = source[key]
}
}
return obj
}
}
```
*Function Constructor*(函式建構式)
> 能夠藉由 `new` operator 建構出物件(此時稱為 Instance(實例))的函式,其中的 <u>**`this` 會指向 instance**</u>。若此 constructor 並未指定回傳為其他物件,此 instance 會自動被回傳。要注意的是,不管是用 `new String()` 還是 `new Number()` 等方式,建構出來的都是物件!
```javascript
0 === Number(0)
0 !== new Number(0)
'' === String('')
'' !== new String('')
typeof Number(0) // number
typeof String('') // string
typeof new Number(0) // object
typeof new String('') // object
```
>
> <u>**constructor 的 prototype(名稱為 `.prototype`)屬性是所有透過這個 constructor 所建立出來的 instance 的 prototype。**</u>因此可以將 method 放在 prototype 中(注意不要 overwrite 原本 prototype 的 method),這樣每一個 instance 都不需要複製一份 method 的 copy,而是可以藉由 prototype chain 來取得此 method 以節省記憶體空間。
```javascript
function Vehicle(name) {
this.name = name
}
Vehicle.prototype.start = function() {
return `${this.name} start!`
}
var car = new Vehicle('car')
car.start() // 'car start!'
```
*class*(類別)
> 只是一種建立物件的 syntactic sugar(語法糖),其本質還是 prototypal inheritance。
>
> 可以用 `extends` 來建立子類別,使其 prototype 指向父類別;用 `super` 呼叫父類別的 constructor 並傳遞相應的參數。
```javascript
class RacingCar {
constructor(name) {
this.name = name
}
start() {
return `${this.name} start!`
}
}
class GPX extends RacingCar {
start() {
return `push ${this.name} start button!`
}
originalStart() {
return super.start()
}
}
var asurada = new GPX('Asurada')
asurada.start() // 'push Asurada start button!'
asurada.originalStart() // 'Asurada start!'
```
## ES6
*`let` / `const`*
> [從 V8 bytecode 探討 let 與 var 的效能問題](https://blog.huli.tw/2020/02/20/let-vs-var-bytecode/)
*`Promise`*
> `Promise` 會等待某項非同步作業完成(`fulfilled` / `rejected`),並執行此作業完成後要執行的任務。
*`Async` / `Await`*
> `async` / `await` 是 `Promise` 的 syntactic sugar。當 syntax parser 在 `async function` 中讀到 `await` 時,會先將 `async function` 從 execution stack 中「暫停並放到一旁」(其他 execution stack 中的函式不受影響)。當 `await` 等待的 `Promise` `fulfilled` 或 `rejected` 後,`async function` 中剩下的程式會繼續被執行。
## Tricky Knowledge
- 函式的 length 就是 parameter(參數)的數量[(ref)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
- `Array.fill()` 裡面放的值,會引用到同一個 reference[(ref)](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/fill)
- `Object.create(null)` !== `{}`[(ref)](https://stackoverflow.com/questions/15518328/is-creating-js-object-with-object-createnull-the-same-as)
- `for...in` 也會讀到 prototype 上新增的 property,所以建議用 `for...of` 或 `for loop`[(ref)](https://stackoverflow.com/questions/29285897/what-is-the-difference-between-for-in-and-for-of-statements)
## Additional Information
- Course
- [CS50](https://pll.harvard.edu/course/cs50-introduction-computer-science?delta=0)
- [JavaScript: Understanding the Weird Parts](https://www.udemy.com/course/understand-javascript/)
- Practice
- [JS30](https://javascript30.com)
- [30 Days of LC JavaScript Challenge](https://leetcode.com/discuss/study-guide/3458761/Open-to-Registration!-30-Days-of-LC-JavaScript-Challenge)
- Source Code
- [Underscore Source Code](https://underscorejs.org/docs/underscore-esm.html)
- [Read the jQuery source like a book like a boss](http://robflaherty.github.io/jquery-annotated-source/)
- Others
- [ES6 Features](https://github.com/lukehoban/es6features#arrows)