---
# System prepended metadata

title: '0416 Stack/Queue, 物件導向, this'
tags: [JavaScript]

---

# 0416 Stack/Queue, 物件導向, this
###### tags: `JavaScript`


## 大綱
- 作業檢討
- [stack vs. queue](#Stack_vs._Queue)
- [物件導向](#物件導向)
- [this（完整版）](#this（完整版）)
---
## 作業檢討
### JavaScript 的變數範圍
```javascript=
var a = 1

function hello() {
  a = 2
  var a = 3
  console.log(a);
}

console.log(a);    // 會印出什麼？
hello()            // 會印出什麼？
console.log(a);    // 會印出什麼？

//答案
// 1
// 3
// 1
```
### 費波那契數列

```javascript=
function fibonacci(n) {
  // 實作內容
}

fibonacci(100) // 可在畫面上印出小於 100 的數列
               // 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
```
- 龍哥作法：
```javascript=
function fib(n) {
  let [prev, current] = [0,1]

  let result = []
  while(current <= n) {
   result.push(current);
   [prev, current] = [current, prev + current]
  }
  
  return result
}
console.log(fib(100))
```

---

## Stack_vs._Queue
- [瘦瘦但很厲害的男生解釋](https://www.youtube.com/watch?v=8aGhZQkoFbQ)
- [那個男生做的視覺化說明網站](http://latentflip.com/loupe/)
- ==JS 是一個單執行緒（single thread）的語言==
- 等一個程序執行完再繼續下一個
- 一個一個往下執行
- 只要有程序執行就會拿一個 Stack，直到程序結束才會拿掉 Stack

```javascript=
function a() {
  console.log('a')
}

function b({
  console.log('b1')
  while(true){        // 無窮迴圈
  }
  console.log('b2')
})

b()
a()

// 僅會印出 b1
// 然後進入無窮迴圈無法自拔
```
- 通常 Stack 都會有個極限
![](https://i.imgur.com/zY5agCU.png =300x)
![](https://i.imgur.com/Yu8df0n.png =500x)

```javascript=
setTimeout(() => {
  console.log('hi')
  }, 1000
)    // 1000 ms(毫秒)-> 1秒, 之後執行 console.log('hi') 
```
```javascript=
setTimeout( function, 時間/毫秒 )    // 經過時間後才執行function
```
- 非同步執行的 function (Queue 的使用)
```javascript=
function b () {
  console.log('b')
}

b()

setTimeout(() => {
  console.log('hi')
  }, 0
)    // 1000 ms(毫秒)-> 1秒, 之後執行 console.log('hi')

b()

// 'b'
// 'b'
// 'hi'
```
- 如果 Stack 中有 ==非同步執行== 的 function => 直接被丟到 WEB API 裡面處理時間問題
- WEB API 時間條件滿足 => 被丟到 Callback Queue 裡排隊
- Queue 內 function 執行條件：Stack 被清空
- 會被丟進 WEB API 的 function: .addEventListener, .setTimeout
* Stack：Last In First Out (LIFO) 後進先出
* Queue：First In First Out (FIFO) 先進先出



### ＊面試題：
```javascript=
setTimeout(
  () => {console.log('hi')},
  3000
)
```
解釋：「最快三秒之後出現 hi 」


---
## 物件導向

- 原生 JS 無 class 寫法
- 但是 JS 是一個真正 ==物件導向== 的程式語言
- [JavaScript 考古題](https://github.com/lydiahallie/javascript-questions)
### JavaScript 中 Object 指向記憶體位置的特性：
```javascript=
let a = {
  name: 'kk',
  age: 18
}
console.log(a) // { name: 'kk', age: 18 }
let b = a
//此時的 b 跟 a 指向同一個東西

b.name = 'cc'
// b 做了修改，但因為 a 跟 b 指向同一個東西，所以 a 跟 b 就會印出同樣的結果

console.log(b) //{ name: 'cc', age: 18 }
console.log(a) //{ name: 'cc', age: 18 }
```
---
### 在 function 裡面改變 object 裡面的值
- 產生 side effect
- const 常數定義：不能 re-assign
  - 但是只要殼 (記憶體位置) 沒換，就可以在裡面做修改
* 宣告 function 後再宣告 function 對應到的 Object
```javascript=
function a () {
  var x = 1
}

a.hello = 123

console.log(a)  // [Function: a] { hello: 123 }
console.log(a.hello)  // 123
```
```javascript=
const user = {
 name: 'kk'
 age: 18
}

Object.freeze(user) // 把 user 凍~凍～起來 🥶
user.age = 30

console.log(user)

// 18
```
```javascript=
const user = {
 name: 'kk'
 age: 18
}

Object.freeze(user)
const c = user

c.age = 30

console.log(user)
console.log(c)

// 18
// 18
```
- 使用 `Object.create()` 創造新的 Object，再指定 Key-Value 給它
```javascript=
const u1 = {}
const u2 = Object.create(null)

u2.age = 18

console.log(u1)  // {}
console.log(u2)  // [Object: null prototype] { age: 18 }
```
---
### 宣告時用 function 回傳的 Object 
- JavaScript 中如果變數名稱帶入時 Value 跟 Key 相同 => 可以只寫 Key
```javascript=
function heroCreator(name, power) {
  const hero = {
    name,     // 如果key & value 一樣，只要輸入一個就好 等於 name: name
    power,   // 等於 power: power
    attack: function() {
     console.log('Attack!')
    }
  }

  return hero
}

const goku = heroCreator('悟空', 500 )

console.log(goku) //{ name: '悟空', power: 500 }
goku.attack() // Attack!

```
- 創造類似於 Ruby 類別模組的概念，讓利用 function 宣告的 Object 都可以使用一樣的 function
```javascript=
function heroCreator(name, power) {
  const hero = {
    attack: function() {
     console.log('Attack!')
    }
  }
  return hero
}

const goku = heroCreator('悟空', 500) 
const gohan = heroCreator('悟飯', 50)

goku.attack()  // 'ATTACK'
gohan.attack()  // 'ATTACK'
```
- 在 Ruby 中常使用的類別方法
```ruby=
class Cat
  def aaa
  end
end

kitty = Cat.new
minny = Cat.new

kitty.aaa
minny.aaa
```
---
### Prototype Chain 原型鏈
- `Object.create(a)` => a 只能為一個原型 Object 或是 null
  - 幫忙串接原型鏈
- 借某個物件當原型創造一個新物件
- 目的：共享特定功能
* Overview
```javascript=
const heroActions = {
  attack: function() {
    console.log('ATTACK')
  }
}

const goku = Object.create(heroActions)
const gohan = Object.create(heroActions)

console.log(heroActions)  // {attack: function}
console.log(goku) // {}
// 透過現有物件打造一個新的物件
// goku 本身沒有任何 function，只是串起原型鏈所以可以使用原型上的 function

goku.name = '悟空'
console.log(goku)  // {name: '悟空'}
goku.attack()  // 'ATTACK'

gohan.name = '悟飯' 
console.log(gohan)  // {name: '悟飯'}
gohan.attack()  // 'ATTACK'

//目的：共享attack功能
goku.attack = 3  // 把 goku 上的 attack 改掉 (呼叫時會先找該物件上的東西)
goku.attack()  // error: goku is not a function
gohan.attack()  // 'ATTACK' 原型的功能還是會在，不會被更動
```
- 用原型創造出來的 Object 上附帶的功能還是會對應到原本的位置
- 從創造出的 Object 上呼叫功能時 => 會順著原型鏈找功能
- 如果原型上的功能被改掉 => 從創造物呼叫時一樣會對到被改掉的功能
```javascript=
const heroActions = {
  attack: function() {
    console.log('ATTACK')
  }
}

const goku = Object.create(heroActions)
heroActions.attack = 1  // 改動原型上的功能

goku.attack()  // 出錯 'attack is not a function'
```
- 理解完之後還是會用 class (實務上也都是這麼用)
```javascript=
class Hero {
  constructor (name, power) {
    this.name = name
    this.power = power
  }
  
  attack () {
  console.log('ATTACK: ' + this.power)
  }
}
```
- 串接原型鍊
```javascript=
const heroActions = {
  attack: function() {
    console.log('ATTACK');
  }
}

const goku = Object.create(heroActions) // 串起原型鏈的動作

console.log(goku)  // {}  因為僅只是串起原型鏈，尚未宣告任何其他值
goku.attack()  // 'ATTACK'  執行功能時會先找 goku(找不到) =連結到heroActions找=> 執行
```
![是這樣嗎？](https://i.imgur.com/eM0q6DY.png)

```javascript=
const heroActions = {
  attack:
}

function heroCreator (name, power) {
  const hero = Object.create(heroActions),
  name,
  power
}


```



#### 有沒有 new 差很多
- 沒有 new ：
```javascript=
function heroCreator(name, power) {
 this.name = name    // this -> 全域變數
 this.power = power
 // 沒有回傳值
}

 const h1 = heroCreator('悟空', 500)
 console.log(h1)
  
// underfined
```

- 有 new： 
- 在新的物件裡面加上 this 空物件，再把 this 回傳
- ==會影響 this 的指向範圍==

```javascript=
function Hero(name, power) {

 // this = {}    甭自己寫
 
 this.name = name    
 this.power = power
 
 // return this  甭自己寫
 
}

 const h1 = new Hero('悟空', 500) // 透過function產生物件要用 new
 console.log(h1)
  
// Hero { name: '悟空', power: 500 }
// 前面的 Hero 只是一個標記，不用理他。新的 h1 就是一個物件
```


---

```javascript=
const obj = []
console.log(obj.length)
```

每個物件都有特別的屬性(`.__proto__`)
屬性會一個接一個連過去到找不到 ( `null` ) 為止

```javascript=
obj.__proto__   //底線底線
```
每個 function 都有 `.prototype`
因為 function 也是物件所以 function 有 `.__proto__` 也有 `.prototype`

```javascript=
function a() {
  var x = 1
}

a.hello = 123

console.log(a)
//
```

所有 function 的 prototype 都是一個空物件 ({ })



```javascript=
a.__proto__ 指向 生他的 function.prototype
```
- JavaScript 的 boxing / unboxing
```javascript=
a = 1
a = Number(1).__proto__ // 不確定對不對
```
![?????](https://i.imgur.com/l7pfxt3.png)

箭頭函式不會有引數(argument

全域變數的回傳值是 undefined
全域變數上的任意屬性是 undefined





## this（完整版）

[new & proto](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)
```javascript=
const obj = {
  name: 'kk',
  age: 18,
  a: function() {
    console.log
  }
}

```
### .bind()
```javascript=
function a() {
  console.log(this.age)
}

const user = {name: 'kk', age: 18}

const user_a = a.bind(user)
// a.bind(user) => .bind 會回傳一個新的 a function 然後以 user 為 this
user_a()

// 18
```
### .call() / .apply()
```javascript=
function a() {
  console.log(this.age)
}

const user = {name: 'kk', age: 18}

a.call(user)
a.apply(user)

// 18
```
- .call()跟.apply()都會呼叫A，然後把第一個參數當成 this(改變 this 的指向)
- 第二個之後的參數才會變成真正的參數帶入原始 function 裡
```javascript=
function a(x) {
  console.log(x)
}

const user = {name: 'kk', age: 18}

a.call(user)
a.apply(user)
a.

// 18
```
this 跟所放的位置無關，看他在哪裡被呼叫(被誰呼叫)，this 就是誰


```javascript=
var hero = {
  name: '悟空',
  sayMyName: function() {
    console.log(this.name);
  }
};

hero.sayMyName();   // A 悟空

var speakOut = hero.sayMyName;
speakOut();         // B undefined 沒有人呼叫speakOut 
// this = 全域物件 / 全域物件.屬性 = undefined

const someone = { name: '路人' }

hero.sayMyName.call(someone);  // C 路人

function here() {
  console.log(this);
}

const there = () => {
  console.log(this);
}

here();   // D 沒有人呼叫 = 全域物件
there();  // E 沒有人呼叫 = 全域物件
```

### 口訣：
- 規則 1. 函數執行的時候，有沒有使用 new 關鍵字？如果有，this 就是指向那個物件本身。
- 規則 2. 「誰呼叫，誰就是 this」規則。
- 規則 3. 是否使用箭頭函數？有的話就不會有自己的 this。
- 規則 4. 是否有使用 bind、apply 或是 call 方法？有的話 this 的指向也會跟著改變。
- 規則 5. 是否有開啟「嚴格模式」？

### 嚴格模式？ 'use strict'
- 向下相容的關鍵字處理
  - 使用字串放在第一行就會有嚴格模式的效果
  - 如果瀏覽器（IE?）看不懂字串，就不會執行嚴格模式

---

## 題外話
### JS 規則手冊
- 有空可以研讀，收穫會很多，但是很硬
- [JS規則書 : ecma262](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/)
### `.push()`的回傳值
- JavaScript 中 `.push()` 的回傳值為新陣列的長度
- [手冊說明](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/push)
```javascript=
let a = ['a', 'b', 'c', 'd']
let b = a.push('x')

console.log(b)  // 5
console.log(a)  // ['a', 'b', 'c', 'd', 'x']
```
---
### `for` 迴圈補充
- for 迴圈在每次通過驗證式之後都會執行結束動作
- 執行完結束動作後如果不能通過驗證式就會跳出迴圈
```javascript=
function a() {
  console.log('a')
}

function b() {
  for(var a = 1; a < 10; a++){
  }
  console.log(a)
}

b()

// 10
// 因為在 a = 9 迴圈結束時執行 a++ => a = 10 => 不能通過驗證 => 跳出迴圈
// 此時 a = 10
```
---
### `typeof`
- `typeof` 方法：以==字串==形式回傳對應的資料型態
- [MDN typeof](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/typeof)
```javascript=
typeof 1
// 'number'

typeof 2
// 'number'

typeof 'a'
// 'string'

typeof []
// 'object'

typeof {}
// 'object'

typeof true
// 'boolean'

typeof null 
//'object' 

typeof undefined
//'undefined'

typeof(typeof(1))
// 'string'
```
---
### `parseInt` 能將輸入的字串轉成整數
- [MDN parseInt](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/parseInt)
```javascript=
parseInt(string, radix);
```
---
### `!`
```javascript=

```
---
### JS 裡的 `false`
- [MDN falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy)
- 規則需要背
- 規則有列的都是假的，沒列的都是真的

![](https://i.imgur.com/PheSaSy.png)

```javascript=
// 在終端機內輸入 $ node
> var c = new Boolean(false)
// undefined <- 回傳值
> c
// [Boolean: false] <- 物件
> if (c) {console.log('aa')}
//  aa
// c 是物件 是個true

```
- 純量值沒有方法，要使用方法要先把量值物件化之後再使用方法
```javascript=
> Number(1).toString()
// '1'
> 1.toString()
// 1.toString()
//^^
// Uncaught SyntaxError: Invalid or unexpected token

```

---
### 等於的種類
- `===` 比較類型 / 嚴格模式
- `==` 比較值 / 較鬆散
- `=` 宣告、指向

---
### 非同步執行
![](https://i.imgur.com/fIJw2ug.png =400x)
```javascript=
console.log ('123')

setTimeout(
  () => {console.log('hi')},
  0      // 雖然只有０秒，但還是會去排隊，等下面的執行以後才會出現
)

console.log ('456')

======>
'123'
'456'
'hi'
```

---
### 開發方法
#### 看板方法（敏捷開發）
- 找個小黑板
  - 分成三個部分：To do - Doing(WIP) - Done(FJ)
  - 用便利貼建立任務 => 票
  - 建議一個人只能接一張票 
  - 建議一張票只能一個人做
  - 適時利用 Trello, GitHub projects 功能... 等工具輔助
 
|To Do|Doing|Done|
|---|---|---|
|票票票|票票票|票票票|

- 任務挑選：
  - 可以選擇挑戰型任務試試看
- 路障型任務
  - 快手做？比較慢的人做？
  - 可以讓新手試試看，但要設立停損點
- 建立任務點數機制
  - 點數 1 ~ 5 ，劃分任務難度
    - 注意點數應該呈現常態分配 ( 3點多一點比較好 )
    - 如果發現點數往 4 ~ 5 靠攏 => 將任務再做拆分
    - 可自行設定 1點 花費多少時間製作去推算
  - 每個人可以提出自己對點數的看法，再來討論為什麼跟背後的原因並確認每個人對任務的了解
  - 計算專案總計點數及每個人單日可使用點數
    - 利用燃盡圖輔助計算任務完成時程
    - 算出斜率後可畫出是否會超過 Demo Day
      - 超過 => 刪除功能、調整點數、加人
    - 燃盡圖波動時即時確認是否超出預設燃盡圖的線段
- 衝刺期
  - 用燃盡圖輔助
  - 可利用任務點數及每人單日可完成的點數計算整個任務時程
