###### tags: `AppWorks School`
# (Chunglin) Object-oriented programming
## 跟著神奇寶貝學OOP!
* 任務
* OOP 四大理論
* 抽象化 Abstraction
* 繼承Inheritance
* 多型 PolyMorphism
* 封裝 Encapsulation
---
## 任務
你被派到銀河隊調查組,
被穿越時空的大木博士要求用 JS 寫出你的第一隻神奇寶貝
### 任務一、產生一個物件
你選擇了小火龍,一開始你這樣寫
```javascript=
let pokemon1name = "小火龍";
let pokemon1HP = 50;
let pokemon1STR = 30;
function attack(STR) {
console.log(`attack in STR ${STR}`);
}
```
後來聽了博士助手子華建議,你決定用個object包裝
```javascript=
const pokemon1 = {
name: "小火龍",
HP: 50,
STR: 30,
attack() {
console.log("attack in STR", this.STR);
},
};
console.log(pokemon1);
// ▶{name: '小火龍', HP: 50, STR: 30, attack: ƒ}
console.log(pokemon1.name); //小火龍
pokemon1.attack;
// ƒ attack() {
// console.log("attack in STR", this.STR);
// }
pokemon1.attack();
// attack in STR 30
```
### 任務二、產生多個物件
大木博士心血來潮,要你再創兩隻神奇寶貝
ctrl+c、ctrl+v
```javascript=
const pokemon2 = {
name: "傑尼龜",
HP: 49,
STR: 29,
attack() {
console.log("attack in STR", this.STR);
},
};
const pokemon3 = {
name: "妙蛙種子",
HP: 51,
STR: 31,
attack() {
console.log("attack in STR", this.STR);
},
};
```

你發現每個神奇寶貝都有 `名字`、`HP`、`STR`、`攻擊` 四個東西
那不然把它寫成一個通用的 **設計圖** 好了
```javascript=
//class 類別 字首要大寫
class Pokemon {
constructor(name, hp, str) {
this.name = name;
this.HP = hp;
this.STR = str;
}
attack() {
console.log("attack in STR", this.STR);
}
}
console.log(Pokemon);
// class Pokemon {...}
```
我們只是完成設計圖,沒有依照設計圖把神奇寶貝創出來
```javascript=
//實體化 Instance
const 小火龍 = new Pokemon("小火龍", 50, 30);
const 傑尼龜 = new Pokemon("傑尼龜", 49, 29);
const 妙蛙種子 = new Pokemon("妙蛙種子", 51, 31);
console.log(傑尼龜);
// ▶Pokemon{name: '傑尼龜', HP: 49, STR: 29}
```
這個時候你就創好三隻活生生的神奇寶貝了

### 任務三、統整類別
子華提醒你,火系要會生火,而且應該有火屬性!
應該為火系創一個類別
可是每個火系都是神奇寶貝,應該有共用的地方
```javascript=
//繼承
class FirePokemon extends Pokemon {
constructor(name, hp, str, fireproperty) {
// 用super呼叫父層 Pokemon class
super(name, hp, str);
this.fire = fireproperty;
}
flame() {
console.log("I Created some fire");
}
}
let 小火龍 = new FirePokemon("小火龍", 50, 30, 40);
console.log(小火龍);
// ▶FirePokemon{name: '小火龍', HP: 50, STR: 30, fire: 40}
小火龍.flame();
// I Created some fire
```
### 任務四、共同方法、不同內容
子華突然想到,神奇寶貝都會攻擊,可是各個屬性攻擊不同
火系神奇寶貝子攻擊時會噴火、水系的會噴水、草系的...
```javascript=
// 多型
class Pokemon {
constructor(name, hp, str) {
this.name = name;
this.HP = hp;
this.STR = str;
}
attack() {
console.log("attack in STR", this.STR);
}
}
//火系會噴火
class FirePokemon extends Pokemon {
attack() {
console.log("Fire attack in STR", this.STR);
}
}
//水系會噴水
class WaterPokemon extends Pokemon {
attack() {
console.log("Water attack in STR", this.STR);
}
}
//草系會...
class WeedPokemon extends Pokemon {
attack() {
console.log("Weed attack in STR", this.STR);
}
}
let 小火龍 = new FirePokemon("小火龍", 50, 30);
let 傑尼龜 = new WaterPokemon("傑尼龜", 49, 29);
let 妙蛙種子 = new WeedPokemon("妙蛙種子", 51, 31);
小火龍.attack(); // Fire attack in STR 30
傑尼龜.attack(); // Water attack in STR 29
妙蛙種子.attack(); // Weed attack in STR 31
```
### 緊急任務、資料被竄改了!?
火箭隊趁子華去開會,潛入研究室偷改小火龍的數值
```javascript=
小火龍.HP = -1000;
console.log(小火龍);
// ▶FirePokemon {name: '小火龍', HP: -1000, STR: 30}
// 被竄改成功!?
```
你趕緊向組員道歉,一行一行看code決定用這樣的方式挽救
```javascript=
// #封裝
class PokemonNew {
#name;
#HP;
#STR;
constructor(name, hp, str) {
this.#name = name;
this.#HP = hp;
this.#STR = str;
}
attack() {
console.log("attack in STR", this.#STR);
}
}
const 噴火龍 = new PokemonNew("噴火龍", 170, 100);
console.log(噴火龍);
// ▶PokemonNew {#name: '噴火龍', #HP: 170, #STR: 100}
噴火龍.attack();
// attack in STR 100
```
火箭隊又出沒,但他們這次發現改不動,你在旁邊滑ptt呵呵笑
```javascript=
// 封裝
噴火龍.STR = -200;
噴火龍.attack();
// attack in STR 100
噴火龍.#STR = -300;
// ✖ Uncaught SyntaxError: Private field '#STR' must be declared in an enclosing class
噴火龍.attack();
// attack in STR 100
```

---
## RECAP
### 屬性與方法
**任務一、產生一隻小火龍**
物件內通常會包含 `屬性Property`、`方法 Method`
屬性Property:該物件的資訊。
方法 Method:定義該物件的功能。

---
### 抽象化 Abstraction
**任務二、產生御三家**
既有物件拆成class

### 實體化 Instance
依照class 實際產生物件

#### new 做了什麼?
1. 產生一個新的 obj
2. 把this 指到新的obj
3. 呼叫constructor
O
減少重複程式碼
易修改
---
### 繼承Inheritance
**任務三、創造火系類別**
重複使用相同程式碼,減少浪費重複工作時間

#### super
呼叫父層class的方法
---
### 多型 PolyMorphism
**任務四、各屬性不同的攻擊方式**
* #### 函式過載 Overloading (JS原則無)
* #### 函式覆寫 Overriding
先繼承、修改當中函式,爸媽和孩子的方法可產生不同結果
每個方法在不同class上會有不同結果

```javascript=
let 小火龍 = new FirePokemon("小火龍", 50, 30);
小火龍.attack(); // Fire attack in STR 30
```
! 方法名稱要相同
O
不會動到既有程式
好維護
---
### 封裝 Encapsulation
**緊急任務、避免資料遭更動**
保護資訊、不被輕易修改
#ES2019才支援,包含 public,protected,private

```javascript=
// 封裝
噴火龍.STR = -200;
console.log(噴火龍.attack());
// attack in STR 100
噴火龍.#STR = -300;
// ✖ Uncaught SyntaxError: Private field '#STR' must be declared in an enclosing class
噴火龍.attack();
// attack in STR 100
```
---
## OOP 是什麼?

#### OOP Object-oriented programming 物件導向
#### POP procedure-oriented programming 程序導向
一條一條寫程式,依照排序讓code執行
如果不用物件導向

物件導向

#### 物件導向的寫法:
1. 定義參與這件事的物品 (aka 「物件」)
1. 定義物品所需的數量及行為 (aka 「屬性」 & 「方法」)
1. 用 1+2 描述事情發生(in 「主程式」)
#### 好處:
1. 便於程式碼「重用」
1. 把程式細節隱藏在物件內,
1. 讓主程式變短,
1. 簡化主程式邏輯
1. 多人開發方便
2. 方便製作同類物件
3. 提高開發與維護效率
⇔ 須了解內容 v.s. 只需知道方法
---
## 名詞複習
**OOP** : 物件導向,以物件為核心,減少重工、提高開發維護效率的概念
**Object 物件**
* property 屬性 : 物件資訊
* method 方法 : 物件功能
**class 類別** : 設計圖,分門別類
**Instance 實體化** : class → object
**Abstraction 抽象化** : object → class
**Inheritance 繼承** : 重複使用相同程式碼,減少浪費重複工作時間
**PolyMorphism 多型** : 一個子類繼承多個父類。相同名稱處理不同功能
**Encapsulation 封裝** : 將屬性、方法、建構式封入類別,避免外部干擾跟誤用
## Reference
[MDN - new operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)
[React - DOM界的彼方 Day 10: ES6篇 - Class(類別)](https://ithelp.ithome.com.tw/articles/10185583)
[從不懂,到玩上手的Python語言 DAY26-物件導向設計](https://ithelp.ithome.com.tw/articles/10227231)
[PTT - 物件導向的好處是....(JAVA)](https://www.ptt.cc/bbs/java/M.1292317058.A.8A3.html?fbclid=IwAR15OTTRYLHtxFUUyNbQs9h-L_WSIME1R1dT0O4xEDilahg9BPj07QZxLFw)
[初心者向けに徹底解説!オブジェクト指向とは?](https://eng-entrance.com/what-oop)
[オブジェクト指向の「カプセル化」とはなにか?超わかりやすく解説します!](https://webpia.jp/encapsulation/)
-------------------------------------------------
# (Dragon) Object-oriented programming (實作篇)
## Javascript中的物件有哪些?
* 在 JavaScript 裡面,從諸如字串與陣列的核心功能、到以 JavaScript 建構的瀏覽器 API,**大部分的東西都可算是「物件 (Object)」**(Array, function...)
> **非物件型別(原始型別Primitive type):**
> - Number
> - String
> - Boolean
> - null
> - undefined
> - bigInt
包含browser內的document, localstorage都是物件


-------------------------------------------------
## 如何自己創建一個物件
#### 在沒有class以前(function版本)
```javascript=
function Car(color, model) {
this.color = color;
this.model = model;
}
const modelY = new Car('white', 'Y');
console.log(modelY); // Car { color: 'red', model: 'Y' }
```
**在ES6後 有了class跟constructor**
#### class的版本
```javascript=
class Car {
constructor(color, model) {
this.color = color;
this.model = model;
}
}
const modelY = new Car('red', 'Y');
console.log(modelY); // Car { color: 'red', model: 'Y' }
```
這樣我們就創建好一個可重複使用且帶有**Property**的物件了
### 那如果我們要給物件加上Method呢?
#### class 版本
```javascript=
class Car {
constructor(color, model) {
this.color = color;
this.model = model;
}
speak() {
console.log(`I am a ${this.color}'s model ${this.model}`);
}
}
const modelY = new Car('red', 'Y');
modelY.speak(); // I am a red's model Y
```
#### function 版本
透過`prototype`
```javascript=
function Car(color, model) {
this.color = color;
this.model = model;
}
Car.prototype.speak = function () {
console.log(`I am a ${this.color}'s model ${this.model}`);
};
const modelY = new Car('red', 'Y');
modelY.speak(); // I am a red's model Y
```
**可以發現到class可以做到的事情,用function也做的到**
另外你可能也會想到,**function的版本其實可以寫得簡單一點**
不需要用到`prototype`
```javascript=
function Car(color, model) {
this.color = color;
this.model = model;
this.speak = function () {
console.log(`I am a ${this.color}'s model ${this.model}`);
};
}
const modelY = new Car('red', 'Y');
modelY.speak(); // I am a red's model Y
const modelX = new Car('white', 'X');
console.log(modelY.speak === modelX.speak); // false
```
是可以達到一樣的功能,但就會造成每次都會產生一個**新的`speak` function**,會造成記憶體的浪費
> 類似於我們在寫React function component時,function內沒有被hook管理的變數、funcion、物件,會被重新宣告
**用prototype寫可以避免這個問題**,可以想像成function裡面寫的東西是每次被物件化時,會被執行的程式碼`等同於class內的constructor`,用prototype產生的method則是被呼叫時才會執行
```javascript=
function Car(color, model) {
this.color = color;
this.model = model;
}
Car.prototype.speak = function () {
console.log(`I am ${this.color}'s model ${this.model}`);
};
const modelY = new Car('red', 'Y');
const modelX = new Car('white', 'X');
console.log(modelY.speak === modelX.speak); // true
```
## 那為什麼要有class
在ES6中的`class`(類別)語法,並不是真的是以類別為基礎`class-based`的物件導向,在骨子裡仍然是以原型為基礎`prototype-based`的物件導向,它只是個**語法糖`syntactical sugar`**。加入Class(類別)語法的目的,並不是要建立另一套物件導向的繼承模型,而是為了
* **提供更簡潔的語法來作物件建立與繼承**
* 讓已經熟悉以類別為基礎的物件導向程式語言的開發者使用,以此提供另一種在物件導向語法上的選擇。
> 語法糖:讓程式更加簡潔,有更高的可讀性
### 物件的繼承 (inheritance )
#### function版本
```javascript=
function Car(color, model) {
this.color = color;
this.model = model;
}
Car.prototype.speak = function () {
console.log(`I am ${this.color}'s model ${this.model}`);
};
function NewCar(color, model) {
Car.call(this, color, model);
}
NewCar.prototype.speak = function () {
console.log(`I am a NEW ${this.color}'s model ${this.model}`);
};
const modelY = new NewCar('red', 'Y');
modelY.speak() // I am a NEW red's model Y
```
#### class版本
```javascript=
class Car {
constructor(color, model) {
this.color = color;
this.model = model;
}
speak() {
console.log(`I am ${this.color}'s model ${this.model}`);
}
}
class NewCar extends Car {
speak() {
console.log(`I am a NEW ${this.color}'s model ${this.model}`);
}
}
const modelY = new NewCar('red', 'Y');
modelY.speak(); // I am a NEW red's model Y
```
* 這邊會發現如果用`function`去寫,雖然可以達到一樣的功能,但就寫的比較麻煩
> 可以注意到我們用新的`class`取代被繼承的`class`的`speak method`,這牽扯到了`javascript`的原型鏈 `prototype chain`,簡單來說,一個物件本身如果本身有這個method,就會被執行,如果沒有的話,他就會往上層(被繼承的物件的`__proto__`)去找,如果有,就會執行,如果都沒有,就會是`undefined`
## 讓我們繼續學習class的語法
### 如果我們要繼承class並且增加constructor內容該怎麼做?
很簡單,只要在`constructor`的內容第一行打上`super()`,並且裡面帶上原`class consturctor`所需的參數即可
```javascript=
class Car {
constructor(color, model) {
this.color = color;
this.model = model;
}
speak() {
console.log(`I am ${this.color}'s model ${this.model}`);
}
}
class NewCar extends Car {
constructor(color, model, year) {
super(color, model);
this.year = year;
}
speak() {
console.log(
`I am a ${this.color}'s model ${this.model} car from future ${this.year}`
);
}
}
const modelY = new NewCar('black', 'Z', 2025);
modelY.speak(); // I am a black's model Z car from future 2025
```
* 如果為繼承的`class`要創建`constructor`,一定要在寫上第一個`this`之前加上`super()`
* `super()`的概念可以理解成:將原本的`class`內容先寫一遍
* 到這邊以前如果是用`function`搭配`prototype`產生出來的`method`或是`class`的寫法,又稱作`Prototype method`原型方法
-------------------------------------------------
### `class`中的 `static method` 靜態方法
* 如果我們今天創建一個紀錄位置的`class`,然後想要計算兩者之間的距離,可以怎麼做?
#### 如果你不知道`static`你可能只能這樣做
```javascript=
class Position {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const p1 = new Position(0, 0);
const p2 = new Position(1, 1);
const distanceX = p1.x - p2.x;
const distanceY = p1.y - p2.y;
const distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
```
#### `static`的寫法
```javascript=
class Position {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(p1, p2) {
const distanceX = p1.x - p2.x;
const distanceY = p1.y - p2.y;
return Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2));
}
}
const p1 = new Position(0, 0);
const p2 = new Position(1, 1);
const distance = Position.distance(p1, p2); // 1.414...
const distance2 = p1.distance(p1, p2); // TypeError: p1.distance is not a function
```
* `static`就是一個`class`的`method`,可以幫把當作一個`function`來使用,但只存在於`class`中,無法被物件使用,白話文說,就是**沒辦法透過被new的物件去呼叫他**
* 這邊又突顯出OOP的一個好處,就是不需要了解`class`內部`function`如何撰寫的,只要執行就好
-------------------------------------------------
### Getter / Setter
* 顧名思義,適用來**拿變數**跟**設定變數**的,直接看程式碼
#### `Getter`用來拿物件的`property`
```javascript=
class Car {
constructor(color, model) {
this._color = color;
this.model = model;
}
get color() {
return this._color;
}
}
const modelY = new Car('white', 'Y');
console.log(modelY.color); // white
console.log(modelY._color); // white
modelY.color = 'black'; // 無法更改
console.log(modelY.color); // white
modelY._color = 'black';
console.log(modelY.color); // black
```
* 這邊可以看到把原本的`this.color`改成`this._color`是想要避免被外部直接更改,但如果知道變數的命名,還是可以直接修改的
#### `Setter` 用來設定物件的`property`
```javascript=
class Car {
constructor(color, model) {
this._color = color;
this.model = model;
}
get color() {
return this._color;
}
set color(color) {
if (color === 'black' || color === 'white') {
this._color = color;
}
}
}
const modelY = new Car('white', 'Y');
console.log(modelY.color); // white
modelY.color = 'black';
console.log(modelY.color); // black
modelY.color = 'blue';
console.log(modelY.color); // black
modelY._color = 'blue';
console.log(modelY.color); // blue
```
* `Setter`可以用來當作一個判斷式、如果符合才更改變數
### Getter / Setter的主要運用時機
* 用**同一個`method`名稱去拿取或更改`Object`的`Property`**,同時避免被直接存取
* 做一些邏輯處理 (`if`) 或資料處理 (數據轉換)
> class內的參數還是有方法可以避免被直接修改的,詳情可以搜尋`public/private class fields`
## OOP實際可以如何運用?
以Stylisy product page舉例,我們一般的寫法可能會像是這樣
由一個主要的function`productDetail`去呼叫其他function
```javascript=
function getProductData(url) {}
function arrangeInStockData(variants) {}
function renderProductDetail(productData, container) {}
function renderProductDescription(productData, container) {}
function checkAllStatus(inStock) {}
function bindSelectionEvent(inStock) {}
function bindAddToCartBtn(inStock, productData) {}
function productDetail(url) {
const container = document.querySelector('.product');
const productData = getProductData(url);
const inStockData = arrangeInStockData(productData.variants);
renderProductDetail(productData, container);
renderProductDescription(productData, container);
checkAllStatus(inStock);
bindSelectionEvent(inStock);
bindAddToCartBtn(inStock, productData);
}
```
可以看到很多同樣的參數被傳遞來傳遞去的,可能會衍伸出以下問題
- **漏寫參數**,導致某個function無法運作
- 當別人想沿用你的Code,複製過去的時候,不小心**漏掉某個function**
### 用Class來改善這個問題
```javascript=
class ProductDetail {
constructor(url) {
this.url = url;
this.container = document.querySelector('.product');
this.productData = this.getProductData();
this.inStockData = this.arrangeInStockData();
this.renderProductDetail();
this.renderProductDescription();
this.checkAllStatus();
this.bindSelectionEvent();
this.bindAddToCartBtn();
}
getProductData(){}
arrangeInStockData(){}
renderProductDetail(){}
renderProductDescription(){}
checkAllStatus(){}
bindSelectionEvent(){}
bindAddToCartBtn(){}
}
```
這樣一來,當別人要複製你的code,僅需要整包class複製
參數也不需要一直重複宣告、傳遞
同時可以把主要架構放在constructor,較容易閱讀
### 小結
- `javascript`中,大部分的東西都是物件
- `class`是一個語法糖,單純用function也可以寫出一樣的程式
- 物件可以繼承,可以免去寫出重複的程式碼,同時可以複寫掉舊有的method
- `static`僅限於class使用,被物件化`new XXX()`的無法使用
- Setter / Getter 主要用於保護內部property被直接修改,以及在存取數值時可以動些手腳
- 將複雜且相互依賴高的function合起來寫成一個class,可以避免參數重複宣告、易於使用且容易閱讀
-------------------------------------------------
# (Emma) What is “this” in JavaScript? What does “this” refer to?
## Outline
- Simple call (直接的調用)
- As an object method (物件的調用)
- Arrow Functions (箭頭函式的調用)
- As a DOM event handler (DOM 物件調用)
- Strict mode (嚴格模式調用)
- `call`, `apply`, `bind` (強制綁定 `this` 的調用)
## Simple call
如果直接調用函式,此函式的 this 會指向 window,以下兩個範例都是直接調用函式,所以都是指向 window
#### Example 1: this refers to window object
```javascript=
window.name = 'window';
function callName() {
console.log('call:', this.name);
}
callName(); //window
```
#### Example 2: 不管在哪裡宣告調用,simple call -> window
```javascript=
window.name = 'window';
function callName () {
console.log('call:', this.name);
// function 內的 function
function callAgainName () {
console.log('call again:', this.name);
}
callAgainName();
}
callName(); //"call:" "window" //"call again:" "window"
```
小結: ==無論在哪一層,純粹的調用方式 this 都會指向 window==
## As an object method
如果 function 是在物件下被called,那麼 this 則會指向此object,無論 function 是在哪裡宣告
#### Example 1
```javascript=
var age = 18;
var person = {
age: 28,
displayAge: displayAge
}
function displayAge() {
console.log(this.age);
console.log(this);
}
displayAge(); // 18 //window
person.displayAge();
// 28, //{age: 28, displayAge: ƒ}, 在物件下呼叫,this 則是該物件
```
#### Example 2: 把object裡面的function指給一個變數, 但還是用simple call
```javascript=
window.age = 18;
var person = {
age: 28,
displayAge: function () {
console.log(this.age);
console.log(this);
}
}
//物件內的 function指給一個變數
var callThisName = person.displayAge;
callThisName(); // 18, window
```
#### Example 3: nested object
```javascript=
window.age = 18;
function displayAge() {
console.log(this.age);
console.log(this);
}
var person = {
age: 28,
displayAge: displayAge,
nestedPerson: {
age:40,
displayAge: displayAge
}
}
displayAge(); //18 //window
person.displayAge(); //28, //{age: 28, displayAge: ƒ}. this refers to the person object
person.nestedPerson.displayAge(); //40 //{age: 40, displayAge: ƒ}. this refers to the nestedPerson object
```
## Arrow functions: they don’t have their “own” this
- 依據語彙環境的父層區域(parent scope)來綁定。白話的說,arrow function **定義位置**(不是呼叫順序)的上一層 this 代表誰,arrow function 內的 this 就代表誰
```javascript=
var person = {
age:28,
displayAge:function() {
const displayAge2 = () => {
console.log(this.age);
console.log(this);
}
displayAge2(); //arrow function->parent this
}
}
person.displayAge(); //28, {age: 28, displayAge: ƒ}
```
## As a DOM event handler
- DOM 搭配 addEventListener 時,this 指的是觸發事件的元素。以下這段程式碼可以貼在任何網頁下的 Console,接下來點擊畫面上任何一區域,該區域則會加上紅線
#### Example 1: DOM + addEventListener, this 是 DOM boject
```javascript=
const box = document.querySelector('.box')
box.addEventListener('click', function(){
console.log(this); // 這裡的 this 是 DOM box
})
```
#### Example 2: DOM + addEventListener + arrow function, this 是 parent 的 this
```javascript=
const box = document.querySelector('.box')
box.addEventListener('click', function(){
console.log(this); // DOM box
setTimeout(()=>{
console.log(this); // DOM box. arror function->parent this
},500)
})
```
## Strict mode: 讓simple call的 this 不再是全域 window, 需要給它this
- 現在會建議寫 JavaScript 的時候加入 'use strict',這可以改正一些coding不良習慣,但也有可以因此導致專案無法運作,此時可以考慮將 'use strict' 加在函式內,避免影響過去的程式碼及相關套件
#### Example 1
```javascript=
window.age = 28;
function displayAge() {
'use strict';
console.log('call:', this.age);
}
displayAge();
// Uncaught TypeError: Cannot read properties of undefined (reading 'age')
```
#### Example 2:給 strict mode 一個this, object調用 or 綁定都可以
```javascript=
window.age = 38;
const person = {
age :28,
displayAge: function displayAge() {
'use strict';
console.log(this.age);
},
}
person.displayAge(); //28. object call->this is person obj
person.displayAge.call({ age: 18 }); //18. 強制綁this
```
## 強制綁定 this
`call`, `bind`, `apply` 這三者都可以傳入新的 this 給予函式使用,使其作為 this 所指向的物件,三者僅是使用方法不同
#### `call`
- **`fn.call(this, arg1, arg2..., argn)`**
- 第一個參數:想要綁定的 this
- 第二以後的參數:想要傳進目標函式的參數,如果目標函式中不需要參數則不要傳入即可
- 功能
- 執行 function
- 明確指定 this
Example 1: 強制綁定18,永遠的18歲!
```javascript=
var age = 28;
function callName() {
console.log(this.age);
}
callName(); // 28
callName.call({age: 18}); // 18
```
Example 2: 除了傳入給定 this 的參數之外還可以傳入其他argument
```javascript=
var obj1 = {
myName: 'obj 1',
fn: function (message) {
console.log(this.myName + ' ' + message);
}
}
var obj2 = {
myName: 'obj 2',
}
obj1.fn.call(obj2, 'Hello'); //obj 2 Hello
```
#### `apply`
- **`fn.apply(this, [arg1, arg2..., argn])`**
- 第一個參數:想要綁定的 this
- 第二個參數:與`call`類似, 只是第二個參數是陣列而且必須是陣列
- 功能
- 同`call`
```javascript=
var obj1 = {
myName: 'obj 1',
fn: function (message) {
console.log(this.myName + ' ' + message);
}
}
var obj2 = {
myName: 'obj 2',
}
obj1.fn.call(obj2, ['Hello']); //obj 2 Hello
```
#### `bind`
- **`fn.bind(this, arg1, arg2..., argn)`**
- 第一個參數:想要綁定的this
- 想要傳進目標函式的參數,如果目標函式中不需要參數則不要傳入即可
- 回傳:回傳包裹後的目標函式
- 功能
- 明確指定 this
- 回傳一個包裹函式,當我們執行這個函式時,同時將arguments 一起帶進 Function 中
- 無論函數怎麼被調用,原始函數的 this 在新函數將永遠與 bind 的第一個參數綁定起來, 只能綁一次的概念
Example 1
```javascript=
function fn() {
console.log(this.a);
}
var bfn = fn.bind({a: 'Mark'});
var b2fn = bfn.bind({a: 'Rose'});
fn(); // undefind, 全域沒有定義a
bfn(); // Mark
b2fn(); // Mark. bind 只能使用一次!就算再綁Rose也沒用
```
Example 2: object call + `bind`
```javascript=
function fn() {
console.log(this.a);
}
var bfn = fn.bind({a: 'Mark'});
var b2fn = bfn.bind({a: 'Rose'});
var obj = {a: 'John', fn: fn, bfn: bfn, b2fn: b2fn};
obj.fn(), obj.bfn(), obj.b2fn();
// John, Mark, Mark. object調用在bind過後會被取代
```
## 整理
- Simple call: window
- As an object method: object本身
- Arrow Functions: parent this
- As a DOM event handler: DOM object
- Strict mode: 綁進去的this
- `call`, `apply`, `bind`: 強制綁進去的`this`
- As a constructor: 建構式本身new object
## 總結
- ==Regular function: how is called ?==
- ==Arrow function: where is definded ?==
## Reference
[this: Simple call, Object method, DOM event handler, Constructor](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this)
[Regular function v.s Arrow function](https://www.youtube.com/watch?v=dWZIPIc3szg)
[Strict mode](https://www.w3schools.com/js/js_strict.asp)
[call, apply, bind](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/call)
--------------------------------------
# (Ralph) What is Big O notation?






















































![Uploading file..._d99t93it3]()











