OOP 中的封裝,為類別擁有私有屬性或方法,使外部直接無法存取,必須呼叫特定的方法才可以存取類別內部的狀態,因此可以保護類別內的狀態不受外部引響。

Read more »

物件間的繼承

在 JavaScript 中的繼承與 OOP 的繼承意義不同,從技術上來說,原型繼承更像是實例將方法委託給其原型,模擬 OOP 子類別繼承父類別方法。

構造函式實現

使用 Object.create() 指定一構造函式的 prototype 屬性為另一構造函式 prototype 屬性物件的原型,實現兩構造函式間的原型繼承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const Dog = function (firstName, birthYear) {
this.firstName = firstName
this.birthYear = birthYear
}

Dog.prototype.calcAge = function () {
console.log(2023 - this.birthYear)
}

const Pet = function (firstName, birthYear, skill) {
// call() 呼叫 Dog 函式
Dog.call(this, firstName, birthYear)
this.skill = skill
}

Pet.prototype = Object.create(Dog.prototype)
Pet.prototype.constructor = Pet

Pet.prototype.introduce = function () {
console.log(`My name is ${this.firstName} and I can ${this.skill}.`)
}

const aben = new Pet("ABen", 2021, "bark at strangers")

console.log(aben) // Pet {firstName: 'ABen', birthYear: 2021, skill: 'bark at strangers'}
aben.introduce() // My name is ABen and I can bark at strangers.
aben.calcAge() // 2

使用 Object.create()Dog.prototype 屬性物件設為 Pet.prototype 屬性物件的原型,使 Pet 的實例 aben 可以使用 Dog 上的 calcAge() 方法,但因為 Object.create() 不會設置新物件的 constructor 屬性,所以 Pet.prototype.constructor 仍然指向 Dog ,只能以 Pet.prototype.constructor = Pet 校正。

原型鏈示意圖 :

構造函式間的原型鏈圖

ES6 Classes 實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Dog {
constructor(firstName, birthYear) {
this.firstName = firstName
this.birthYear = birthYear
}

calcAge() {
console.log(2023 - this.birthYear)
}
}

class Pet extends Dog {
constructor(firstName, birthYear, skill) {
// super 負責創建子類別的 this
super(firstName, birthYear)
this.skill = skill
}

introduce() {
console.log(`My name is ${this.firstName} and I can ${this.skill}.`)
}
}
const aben = new Pet("ABen", 2021, "bark at strangers")

console.log(aben) // Pet {firstName: 'ABen', birthYear: 2021, skill: 'bark at strangers'}

aben.calcAge() // 2

aben.introduce() // My name is ABen and I can bark at strangers.

使用 extends 擴展類別作為子類別的父類,並於子類別的 constructor() 函式中呼叫 super() 方法,相當於自動執行父類別的 constructor() 函式。

若子類別中只有與父類別相同的屬性,沒有新增自己的屬性,就可以不用定義 constructor() 函式,子類別會自動調用父類別的 constructor()

Object.create( ) 實現

Object.create() 不用在意 prototype 屬性與 constructor()方法,只要創建物件並指定原型,就可以實現物件間的原型繼承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const DogPrototype = {
init(firstName, birthYear) {
this.firstName = firstName
this.birthYear = birthYear
},

calcAge() {
console.log(2023 - this.birthYear)
},
}

const PetPrototype = Object.create(DogPrototype)
PetPrototype.init = function (firstName, birthYear, skill) {
DogPrototype.init.call(this, firstName, birthYear)
this.skill = skill
}
PetPrototype.introduce = function () {
console.log(`My name is ${this.firstName} and I can ${this.skill}.`)
}

const aben = Object.create(PetPrototype)
aben.init("ABen", 2021, "bark at strangers")

console.log(aben) // {firstName: 'ABen', birthYear: 2021, skill: 'bark at strangers'}

aben.calcAge() // 2

aben.introduce() // My name is ABen and I can bark at strangers.

原型鏈示意圖 :

Object.create() 物件間的原型鏈圖

原型鏈(Prototype Chain)

原型鏈圖 :

原型鏈圖

重點整理 :

  • 實例的隱式原型 __proto__ 屬性等於構造函式的顯式原型 prototype 屬性。
  • 所有函式的 prototype 屬性默認指向一個空物件,但 Object 除外。
  • 所有函式都是 Function 的實例,包括 Function 本身。

參考資料

JS 引擎中包含 Call Stack 與 Heap,在程式碼進入 Call Stack 之前,會先進行解析與編譯的過程,JavaScript 為即時編譯語言,可以比直譯語言更快的執行程式碼,提升運行速度。

Read more »

文件物件模型(Document Object Model,DOM),為 HTML 文件的程式介面,提供 HTML 文件樹狀結構的模型,本身並不是 JavaScript 的一部分。

Read more »

Set 與 Map 都是 JavaScript 中特殊的資料結構,Set 與 Array 相似,但其中的每一個元素都是唯一值,不會有重複元素,主要用途是去除重複出現的元素與判斷元素存在與否;Map 則與 Object 相似,其中的 key 值映射對應 value,而 key 可以使用任意類型的值定義,且亦具備唯一性。

Read more »

在取得 API 響應並存取數據時,時常會遇到 TypeError: Cannot read property 'xxx' of null 或是 TypeError: Cannot read property 'xxx' of undefined 的錯誤,而造成錯誤的原因就是試圖從 nullish value 上讀取屬性或調用方法,為了解決這個問題,可以在取值時使用可選鏈,以免程式拋出錯誤。

Read more »