---
# System prepended metadata

title: All About JavaScript

---

# 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。![GEC](https://hackmd.io/_uploads/B16TmC243.png)


- 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。
![By Value](https://hackmd.io/_uploads/B1hxXLcE3.png)

> 當建立一個 object 變數 c 且指定另一個變數 d = c 時，d 會將其指定到 c 的記憶體位置。故彼此之間會互相影響，此為 by reference。
![By Reference](https://hackmd.io/_uploads/SkFW7IcV2.png)

> 但若用 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 皆為物件 -> 「萬物皆物件」。
> 
![object prototype chain](https://hackmd.io/_uploads/HJmVm0nEn.png)

![function prototype chain](https://hackmd.io/_uploads/HkpSXChEh.png)

![array prototype chain](https://hackmd.io/_uploads/S168XC3Eh.png)

![string prototype chain](https://hackmd.io/_uploads/B1YwQC2Vn.png)

![number prototype chain](https://hackmd.io/_uploads/BkLd703N3.png)

![boolean prototype chain](https://hackmd.io/_uploads/SJkFQAn43.png)


*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)