Try   HackMD
tags: AppWorks School

(Chunglin) Object-oriented programming

跟著神奇寶貝學OOP!
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

  • 任務
  • OOP 四大理論
    • 抽象化 Abstraction
    • 繼承Inheritance
    • 多型 PolyMorphism
    • 封裝 Encapsulation

任務

你被派到銀河隊調查組,
被穿越時空的大木博士要求用 JS 寫出你的第一隻神奇寶貝

任務一、產生一個物件

你選擇了小火龍,一開始你這樣寫

let pokemon1name = "小火龍"; let pokemon1HP = 50; let pokemon1STR = 30; function attack(STR) { console.log(`attack in STR ${STR}`); }

後來聽了博士助手子華建議,你決定用個object包裝

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

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); }, };

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

你發現每個神奇寶貝都有 名字HPSTR攻擊 四個東西
那不然把它寫成一個通用的 設計圖 好了

//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 {...}

我們只是完成設計圖,沒有依照設計圖把神奇寶貝創出來

//實體化 Instance const 小火龍 = new Pokemon("小火龍", 50, 30); const 傑尼龜 = new Pokemon("傑尼龜", 49, 29); const 妙蛙種子 = new Pokemon("妙蛙種子", 51, 31); console.log(傑尼龜); // ▶Pokemon{name: '傑尼龜', HP: 49, STR: 29}

這個時候你就創好三隻活生生的神奇寶貝了

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

任務三、統整類別

子華提醒你,火系要會生火,而且應該有火屬性!
應該為火系創一個類別
可是每個火系都是神奇寶貝,應該有共用的地方

//繼承 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

任務四、共同方法、不同內容

子華突然想到,神奇寶貝都會攻擊,可是各個屬性攻擊不同
火系神奇寶貝子攻擊時會噴火、水系的會噴水、草系的

// 多型 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

緊急任務、資料被竄改了!?

火箭隊趁子華去開會,潛入研究室偷改小火龍的數值

小火龍.HP = -1000; console.log(小火龍); // ▶FirePokemon {name: '小火龍', HP: -1000, STR: 30} // 被竄改成功!?

你趕緊向組員道歉,一行一行看code決定用這樣的方式挽救

// #封裝 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呵呵笑

// 封裝 噴火龍.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


減少重複程式碼
易修改


繼承Inheritance

任務三、創造火系類別
重複使用相同程式碼,減少浪費重複工作時間

super

呼叫父層class的方法


多型 PolyMorphism

任務四、各屬性不同的攻擊方式

  • 函式過載 Overloading (JS原則無)

  • 函式覆寫 Overriding

先繼承、修改當中函式,爸媽和孩子的方法可產生不同結果
每個方法在不同class上會有不同結果

let 小火龍 = new FirePokemon("小火龍", 50, 30); 小火龍.attack(); // Fire attack in STR 30

! 方法名稱要相同


不會動到既有程式
好維護


封裝 Encapsulation

緊急任務、避免資料遭更動
保護資訊、不被輕易修改
#ES2019才支援,包含 public,protected,private

// 封裝 噴火龍.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 「物件」)
  2. 定義物品所需的數量及行為 (aka 「屬性」 & 「方法」)
  3. 用 1+2 描述事情發生(in 「主程式」)

好處:

  1. 便於程式碼「重用」
  2. 把程式細節隱藏在物件內,
  3. 讓主程式變短,
  4. 簡化主程式邏輯
  5. 多人開發方便
  6. 方便製作同類物件
  7. 提高開發與維護效率
    ⇔ 須了解內容 v.s. 只需知道方法

名詞複習

OOP : 物件導向,以物件為核心,減少重工、提高開發維護效率的概念

Object 物件

  • property 屬性 : 物件資訊
  • method 方法 : 物件功能

class 類別 : 設計圖,分門別類

Instance 實體化 : class → object
Abstraction 抽象化 : object → class

Inheritance 繼承 : 重複使用相同程式碼,減少浪費重複工作時間
PolyMorphism 多型 : 一個子類繼承多個父類。相同名稱處理不同功能
Encapsulation 封裝 : 將屬性、方法、建構式封入類別,避免外部干擾跟誤用

Reference

MDN - new operator
React - DOM界的彼方 Day 10: ES6篇 - Class(類別)
從不懂,到玩上手的Python語言 DAY26-物件導向設計
PTT - 物件導向的好處是(JAVA)
初心者向けに徹底解説!オブジェクト指向とは?
オブジェクト指向の「カプセル化」とはなにか?超わかりやすく解説します!


(Dragon) Object-oriented programming (實作篇)

Javascript中的物件有哪些?

  • 在 JavaScript 裡面,從諸如字串與陣列的核心功能、到以 JavaScript 建構的瀏覽器 API,大部分的東西都可算是「物件 (Object)」(Array, function)

非物件型別(原始型別Primitive type):

  • Number
  • String
  • Boolean
  • null
  • undefined
  • bigInt

包含browser內的document, localstorage都是物件

如何自己創建一個物件

在沒有class以前(function版本)

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的版本

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 版本

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

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

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則是被呼叫時才會執行

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版本

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版本

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取代被繼承的classspeak method,這牽扯到了javascript的原型鏈 prototype chain,簡單來說,一個物件本身如果本身有這個method,就會被執行,如果沒有的話,他就會往上層(被繼承的物件的__proto__)去找,如果有,就會執行,如果都沒有,就會是undefined

讓我們繼續學習class的語法

如果我們要繼承class並且增加constructor內容該怎麼做?

很簡單,只要在constructor的內容第一行打上super(),並且裡面帶上原class consturctor所需的參數即可

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你可能只能這樣做

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的寫法

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就是一個classmethod,可以幫把當作一個function來使用,但只存在於class中,無法被物件使用,白話文說,就是沒辦法透過被new的物件去呼叫他
  • 這邊又突顯出OOP的一個好處,就是不需要了解class內部function如何撰寫的,只要執行就好

Getter / Setter

  • 顧名思義,適用來拿變數設定變數的,直接看程式碼

Getter用來拿物件的property

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

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名稱去拿取或更改ObjectProperty,同時避免被直接存取
  • 做一些邏輯處理 (if) 或資料處理 (數據轉換)

class內的參數還是有方法可以避免被直接修改的,詳情可以搜尋public/private class fields

OOP實際可以如何運用?

以Stylisy product page舉例,我們一般的寫法可能會像是這樣
由一個主要的functionproductDetail去呼叫其他function

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來改善這個問題

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

window.name = 'window'; function callName() { console.log('call:', this.name); } callName(); //window

Example 2: 不管在哪裡宣告調用,simple call -> window

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

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

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

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 就代表誰
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

const box = document.querySelector('.box') box.addEventListener('click', function(){ console.log(this); // 這裡的 this 是 DOM box })

Example 2: DOM + addEventListener + arrow function, this 是 parent 的 this

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

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 綁定都可以

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歲!

var age = 28; function callName() { console.log(this.age); } callName(); // 28 callName.call({age: 18}); // 18

Example 2: 除了傳入給定 this 的參數之外還可以傳入其他argument

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

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

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
Regular function v.s Arrow function
Strict mode
call, apply, bind


(Ralph) What is Big O notation?























































Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →