點燈坊

失くすものさえない今が強くなるチャンスよ

ECMAScript 之 OOP 使用 this

Sam Xiao's Avatar 2020-11-23

ECMAScript 是一個包含多種 Paradigm 的程式語言,當使用 this 時,基本上就是以 OOP 風格開發,以 this 指向 Object。目前 ECMAScript 有 Object Literal、Constructor Function 與 Class 三種方式實現 OOP,將分別討論之。

Version

ECMAScript 2020

Object Literal

let getName = function() {
  return this.name
}

let john = {
  name: 'John',
  getName
}

let sam = {
  name: 'Sam',
  getName
}

john.getName() // ?
sam.getName() // ?

Object Literal 會直接以 Object 實現 OOP。

第 5 行

let john = {
  name: 'John',
  getName
}

let sam = {
  name: 'Sam',
  getName
}

直接定義 johnsam 兩個 Object,其中 name property 有各自不同資料,但 getName() 是相同的。

第 1 行

let getName = function() {
  return this.name
}

因為 method 都相同,可單獨提出 getName(),並以 this 存取 property,此時 this 掛在哪一個 Object,則 this 就指向該 Object,如此就不必在每個 Object 都佔記憶體存放 method,getName() 可共用同一份記憶體。

可發現 ECMAScript 因為使用 this 讀取 property,因此同一個 function 可套用在多個 Object,可藉此節省記憶體使用

oop000

Constructor Function

let John = function() {
  this.name = 'John'
}

let Sam = function() {
  this.name = 'Sam'
}

let getName = function() {
  return this.name
}

John.prototype.getName = getName
Sam.prototype.getName = getName

let john = new John
john.getName() // ?

let sam = new Sam
sam.getName() // ?

也可以使用 constructor function 實現 OOP。

第 1 行

let John = function() {
  this.name = 'John'
}

let Sam = function() {
  this.name = 'Sam'
}

使用 constructor function 建立 property。

第 9 行

let getName = function() {
  return this.name
}

John.prototype.getName = getName
Sam.prototype.getName = getName

使用 prototype 建立 method,因為 getName() 都相同,也可將 getName() 提出,如此 JohnSam 都共用同一份 getName() 節省記憶體,且均以 this 存取 property,此時 this 指向 new 所建立的 Object。

16 行

let john = new John
john.getName() // ?

let sam = new Sam
sam.getName() // ?

new 對 constructor function 建立 Object。

oop001

Class

class Student {
  constructor(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

class John extends Student {
  constructor() {
    super('John')
  }
}

class Sam extends Student {
  constructor() {
    super('Sam')
  }
}

let john = new John
john.getName() // ?

let sam = new Sam
sam.getName() // ?

ES6 也提供以 class 實現 OOP。

第 1 行

class Student {
  constructor(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

若要 JohnSam class 都共用 getName(),只能將 getName() 寫在其 parent class,一樣使用 this 存取 property,此時 this 指向 new 所建立的 Object。

11 行

class John extends Student {
  constructor() {
    super('John')
  }
}

class Sam extends Student {
  constructor() {
    super('Sam')
  }
}

JohnSam class 都使用 extends 繼承 Student class,因此只有一份 getName() 節省記憶體。

23 行

let john = new John
john.getName() // ?

let sam = new Sam
sam.getName() // ?

一樣使用 new 建立 Object。

oop002

Conclusion

  • FP 不會使用 this,當使用 this 就是以 OOP 開發
  • 可發現無論使用 object literal、constructor function 或 class,三種寫法都可以使用 this 指向 Object,但別忘了 ECMAScript 是以 function 實現 OOP,因此 this 會隨 context 而變,而非如 OOP 永遠指向相同 Object