###### tags: `AppWorks School` # (Chunglin) Object-oriented programming ## 跟著神奇寶貝學OOP!![](https://i.imgur.com/ril0V3z.png) * 任務 * 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); }, }; ``` ![](https://i.imgur.com/gzdKLv4.png) 你發現每個神奇寶貝都有 `名字`、`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} ``` 這個時候你就創好三隻活生生的神奇寶貝了 ![](https://i.imgur.com/VQ4wQs0.png) ### 任務三、統整類別 子華提醒你,火系要會生火,而且應該有火屬性! 應該為火系創一個類別 可是每個火系都是神奇寶貝,應該有共用的地方 ```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 ``` ![](https://i.imgur.com/gdHI12P.png) --- ## RECAP ### 屬性與方法 **任務一、產生一隻小火龍** 物件內通常會包含 `屬性Property`、`方法 Method` 屬性Property:該物件的資訊。 方法 Method:定義該物件的功能。 ![](https://i.imgur.com/dyOVx66.png) --- ### 抽象化 Abstraction **任務二、產生御三家** 既有物件拆成class ![](https://i.imgur.com/u0Gzqjp.png) ### 實體化 Instance 依照class 實際產生物件 ![](https://i.imgur.com/f8rnEwm.png) #### new 做了什麼? 1. 產生一個新的 obj 2. 把this 指到新的obj 3. 呼叫constructor O 減少重複程式碼 易修改 --- ### 繼承Inheritance **任務三、創造火系類別** 重複使用相同程式碼,減少浪費重複工作時間 ![](https://i.imgur.com/3Yw1MQG.png) #### super 呼叫父層class的方法 --- ### 多型 PolyMorphism **任務四、各屬性不同的攻擊方式** * #### 函式過載 Overloading (JS原則無) * #### 函式覆寫 Overriding 先繼承、修改當中函式,爸媽和孩子的方法可產生不同結果 每個方法在不同class上會有不同結果 ![](https://i.imgur.com/WWfFeQI.png) ```javascript= let 小火龍 = new FirePokemon("小火龍", 50, 30); 小火龍.attack(); // Fire attack in STR 30 ``` ! 方法名稱要相同 O 不會動到既有程式 好維護 --- ### 封裝 Encapsulation **緊急任務、避免資料遭更動** 保護資訊、不被輕易修改 #ES2019才支援,包含 public,protected,private ![](https://i.imgur.com/ca7DQJ2.png) ```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 是什麼? ![](https://i.imgur.com/56eBdA0.png) #### OOP Object-oriented programming 物件導向 #### POP procedure-oriented programming 程序導向 一條一條寫程式,依照排序讓code執行 如果不用物件導向 ![](https://i.imgur.com/1mrJMRY.png) 物件導向 ![](https://i.imgur.com/QECGOn3.png) #### 物件導向的寫法: 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都是物件 ![](https://i.imgur.com/cX9vaL4.png) ![](https://i.imgur.com/nWHxAu0.png) ------------------------------------------------- ## 如何自己創建一個物件 #### 在沒有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? ![](https://i.imgur.com/IGWK0wa.jpg) ![](https://i.imgur.com/JeXl7iM.jpg) ![](https://i.imgur.com/43SH50c.jpg) ![](https://i.imgur.com/O2eBu2Z.jpg) ![](https://i.imgur.com/Lr2DtuK.jpg) ![](https://i.imgur.com/MQ2JANA.jpg) ![](https://i.imgur.com/1ekOibg.jpg) ![](https://i.imgur.com/4Ey8Io1.jpg) ![](https://i.imgur.com/NU3mGN1.jpg) ![](https://i.imgur.com/9UEfEu6.jpg) ![](https://i.imgur.com/q4W7zzP.jpg) ![](https://i.imgur.com/9O669RJ.jpg) ![](https://i.imgur.com/i3B21T1.jpg) ![](https://i.imgur.com/3Ryf3DN.jpg) ![](https://i.imgur.com/D0LuhEM.jpg) ![](https://i.imgur.com/LyuyUyq.jpg) ![](https://i.imgur.com/89JcGRs.jpg) ![](https://i.imgur.com/XkWn3WY.jpg) ![](https://i.imgur.com/eeQPfVS.jpg) ![](https://i.imgur.com/Kyg7x9B.jpg) ![](https://i.imgur.com/LFiY3LC.jpg) ![](https://i.imgur.com/szl4g44.jpg) ![](https://i.imgur.com/7qULYdn.jpg) ![](https://i.imgur.com/yyA1vZG.jpg) ![](https://i.imgur.com/KIuUUw2.jpg) ![](https://i.imgur.com/gwwvWdN.jpg) ![](https://i.imgur.com/eNlVtlT.jpg) ![](https://i.imgur.com/P4ZTn3Q.jpg) ![](https://i.imgur.com/xrEJ1DF.jpg) ![](https://i.imgur.com/WGKOVpL.jpg) ![](https://i.imgur.com/G6n4RSq.jpg) ![](https://i.imgur.com/hk2R5uS.jpg) ![](https://i.imgur.com/TjaVHLV.jpg) ![](https://i.imgur.com/JQ7wk5g.jpg) ![](https://i.imgur.com/6iOAjUv.jpg) ![](https://i.imgur.com/5iYL5yN.jpg) ![](https://i.imgur.com/u7wfpbm.jpg) ![](https://i.imgur.com/Sm6csnj.jpg) ![](https://i.imgur.com/ZqIWSqb.jpg) ![](https://i.imgur.com/q5JpH6p.jpg) ![](https://i.imgur.com/FA9RZzh.jpg) ![](https://i.imgur.com/k6OT2fG.jpg) ![](https://i.imgur.com/vgJKQIg.jpg) ![](https://i.imgur.com/6PoSW1v.jpg) ![](https://i.imgur.com/AFNPT8Z.jpg) ![](https://i.imgur.com/pfXTMZY.jpg) ![](https://i.imgur.com/kNKi7Ql.jpg) ![](https://i.imgur.com/im6Af5k.jpg) ![](https://i.imgur.com/mUoXusR.jpg) ![](https://i.imgur.com/EaZeaFe.jpg) ![](https://i.imgur.com/3xeVFdM.jpg) ![](https://i.imgur.com/Zt6i2QT.jpg) ![](https://i.imgur.com/n6wl32r.jpg) ![](https://i.imgur.com/OuIJBF3.jpg) ![Uploading file..._d99t93it3]() ![](https://i.imgur.com/pdrDZrI.jpg) ![](https://i.imgur.com/ksjn2yd.jpg) ![](https://i.imgur.com/oxzJjEX.jpg) ![](https://i.imgur.com/862nkYu.jpg) ![](https://i.imgur.com/vA7051U.jpg) ![](https://i.imgur.com/mVQZ1TD.jpg) ![](https://i.imgur.com/PcjCUqG.jpg) ![](https://i.imgur.com/OakqGyl.jpg) ![](https://i.imgur.com/ahAQT3c.jpg) ![](https://i.imgur.com/TaQC3sm.jpg) ![](https://i.imgur.com/m2oGKh2.jpg) ![](https://i.imgur.com/GO5kKNt.jpg)