Inheritances ในภาษา JavaScript

ในบทนี้ คุณจะได้เรียนรู้การสืบทอดคลาสในภาษา JavaScript การสืบทอดคลาสนั้นเป็นคุณสมบัติอย่างหนึ่งในการเขียนโปรแกรมเชิงวัตถุ (OOP) ที่ทำให้เราสามารถสร้างคลาสใหม่โดยเพิ่มเติม (Extend) ความสามารถจากคลาสเดิมที่มีอยู่ได้ซึ่งเป็นแนวคิดของการนำโค้ดกลับมาใช้ใหม่ (Reuse) นี่เป็นเนื้อหาในบทนี้

  • การสืบทอดคลาส
  • การใช้คำสั่ง super
  • การ Override เมธอด
  • การสืบทอดคลาสแบบหลายขั้น
  • คลาส Object

การสืบทอดคลาส

การสืบทอดคลาส (Inheritances) คือคุณสมบัติของคลาสในภาษา JavaScript ที่ให้เราสามารถสร้างคลาสใหม่โดยการสืบทอด Property และเมธอดจากคลาสเดิมที่มีอยู่แล้วได้ นี่เป็นคุณสมบัติที่มีประโยชน์มากในการเขียนโปรแกรมเชิงวัตถุสำหรับการนำโค้ดเดิมกลับมาใช้ใหม่ เนื่องจากเราไม่ต้องเขียนโค้ดขึ้นมาใหม่ทั้งหมด แต่เพียงแค่สืบทอดคุณสมบัติจากคลาสเดิมที่มีอยู่แล้ว

นี่เป็นรูปแบบของการสืบทอดคลาสในภาษา JavaScript

class DeriveredClass extends BaseClass {
    // Class definitions
}

เมื่อ BaseClass นั้นเป็นคลาสหลักที่ต้องการสืบทอดไปยังคลาสย่อย DeriveredClass การสืบทอดนั้นจะใช้คำสั่ง extends หลังจากการประกาศคลาสย่อยตามด้วยชื่อของคลาสหลักที่ต้องการสืบทอด นี่จะทำให้ Property และเมธอดที่ถูกกำหนดในคลาส BaseClass ถูกสืบทอดมากยังคลาส DeriveredClass

นี่เป็นตัวอย่างของโปรแกรมสำหรับการสืบทอดคลาสในภาษา JavaScript เราจะสร้างคลาสของบุคคลที่ประกอบไปด้วยคุณสมบัติพื้นฐานที่ทั้งคลาสโปรแกรมเมอร์และคลาสนักกีฬาสามารถสืบทอดไปใช้งานได้ นี่เป็นตัวอย่าง

person.js
class Person {

    constructor(fistName, lastName) {
        this.fistName = fistName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.fistName + " " + this.lastName;
    }
}

class Programmer extends Person {

    setLanguage(lang) {
        this.language = lang;
    }

    introduce() {
        let str = this.getFullName();
        str += " is a " + this.language;
        str += " programmer";
        console.log(str);
    }
}

class Athlete extends Person {

    setSport(sport) {
        this.sport = sport;
    }

    playSport() {
        let str = this.getFullName();
        str += " plays " + this.sport;
        console.log(str);
    }
}

let p = new Programmer("Matteo", "Marcus");
p.setLanguage("JavaScript");
a.introduce();
a.getFullName();

let a = new Athlete("Lionel", "Messi");
a.setSport("Football");
a.playSport();
a.getFullName();

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Matteo Marcus is a JavaScript programmer
Matteo Marcus
Lionel Messi plays Football
Lionel Messi

ในตัวอย่างเราได้สร้างคลาส Person คลาสนี้ประกอบไปด้วยสอง Property คือ fistName และ lastName สำหรับเก็บชื่อและนามสกุลตามลำดับ และเมธอด getFullName รับเอาชื่อแบบเต็ม เราจะใช้คลาสนี้เป็นคลาสหลักที่จะสืบทอดโดยคลาสอื่นๆ ที่ต้องการคุณสมบัติเหล่านี้

class Programmer extends Person {
    ...
}

class Athlete extends Person {
    ...
}

จากนั้นเราประกาศคลาส Programmer ซึ่งเป็นคลาสของโปรแกรมเมอร์ และคลาส Athlete ซึ่งเป็นคลาสของนักกีฬาโดยสืบทอดมาจากคลาส Person เนื่องจากทั้งโปรแกรมเมอร์และนักกีฬาต้องมีชื่อและนามสกุลอยู่แล้ว ดังนั้นมันสามารถสืบทอดจากคลาส Person เพื่อใช้ข้อมูลเหล่านี้โดยไม่ต้องเขียนขึ้นมาใหม่

สิ่งที่ทั้งสองคลาสต้องเพิ่มเข้ามาก็คือ คลาสโปรแกรมเมอร์จะมีการเก็บภาษาเขียนโปรแกรม และคลาสของนักกีฬาจะมีการเก็บชื่อของกีฬาที่เล่น นี่จะแสดงให้เห็นว่าคลาส Person เป็นคลาสที่มีคุณสมบัติทั่วไปแบบกว้างๆ ส่วนคลาส Programmer และ Athlete นั้นเป็นที่เฉพาะเจาะจงมากขึ้น

let p = new Programmer("Matteo", "Marcus");
p.setLanguage("JavaScript");
a.introduce();
a.getFullName();

let a = new Athlete("Lionel", "Messi");
a.setSport("Football");
a.playSport();
a.getFullName();

จากนั้นเป็นการนำคลาสมาสร้างออบเจ็ค และอย่างที่คุณเห็น คลาสทั้งสองสามารถเรียกใช้เมธอด getFullName ที่ถูกประกาศในคลาส Person ซึ่งเป็นคลาสที่มันสืบทอดมาจาก เหมือนกับว่าเมธอดนี้ถูกประกาศไว้ในคลาสของมันเองได้ และนอกจากนี้แต่ละคลาสยังมีการเรียกเมธอดของมันเองที่ถูกประกาศไว้ในแต่ละคลาส

นี่แสดงให้เห็นว่าเราสามารถนำคลาสเดิมที่มีอยู่แล้วกลับมาใช้ใหม่โดยการสืบทอดและเพิ่มเติมความสามารถเข้าไป ซึ่งนี่มีประโยชน์มากในการเขียนโปรแกรม เมื่อหลายคลาสต้องการแชร์คุณสมบัติบางอย่างร่วมกัน เราสามารถนำคุณสมบัติเหล่านั้นกำหนดในคลาสหลักได้และใช้การสืบทอดมาแทน

และข้อดีอีกอย่างหนึ่งของการสืบทอดคือเมื่อมีการเปลี่ยนแปลงในคลาสหลัก จะส่งผลไปยังคลาสที่ได้รับการสืบทอดทุกคลาส ยกตัวอย่างเช่น ถ้าหากเราเพิ่มเมธอดใหม่ในคลาส Person ทั้งคลาส Programmer และ Athlete จะได้รับการเปลี่ยนแปลงนั้นด้วย

การใช้คำสั่ง super

คำสั่ง super เป็นคำสั่งที่่ใช้สำหรับอ้างถึงคอนสตรัคเตอร์หรือเมธอดของคลาสหลัก เมื่อเราไม่ได้สร้างคอนสตรัคเตอร์ในคลาสย่อย ภาษา JavaScript จะสร้างคอนสตรัคเตอร์ว่างเปล่าให้อัติโนมัติในตอนที่โปรแกรมทำงาน แต่เมื่อไรก็ตามที่คลาสย่อยมีการประกาศคอนสตรัคเตอร์ เราจะต้องเรียกใช้งานคอนสตรัคเตอร์ของคลาสหลักเสมอด้วยเมธอด super()

โดยทั่วไปแล้วคลาสย่อยมักจะกำหนดคอนสตรัคเตอร์เพื่อทำงานบางอย่างที่แตกต่างไปจากคลาสหลัก นั่นเป็นเรื่องปกติเนื่องจากเราสืบทอดคลาสเพื่อใช้โค้ดเดิมที่มีอยู่และเพิ่มความสามารถให้กับมัน

ในตัวอย่างนี้ จะเป็นการกำหนดคอนสตรัคเตอร์ในคลาสย่อย อย่างที่คุณรู้ว่ารูปสี่เหลี่ยมและลูกบาศก์นั้นมีคุณสมบัติบางอย่างที่เหมือนกัน เช่น ความยาวและความสูง เราจะใช้มันสำหรับแสดงตัวอย่างการใช้งานคำสั่ง super ในภาษา JavaScript

rectangle.js
class Rectangle {

    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

class Cube extends Rectangle {

    constructor(width, height, dept) {
        super(width, height);
        this.dept = dept;
    }

    volume() {
        return this.area() * this.dept;
    }
}

let rect = new Rectangle(3, 4);
console.log("Area of rectangle:", rect.area());

let cube = new Cube(1, 2, 3);
console.log("Volume of cube:", cube.volume());

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Area of rectangle: 12
Volume of cube: 6

คลาส Rectangle นั้นเป็นคลาสสำหรับรูปสี่เหลี่ยมที่ประกอบไปด้วยสอง Property คือความยาว width และความสูง height เราสามารถใช้คลาสนี้สำหรับสร้างออบเจ็คของรูปสี่เหลี่ยมที่มีขนาดใดๆ ได้ แต่นั่นไม่ใช้สิ่งที่เราต้องการทำในตอนนี้

class Cube extends Rectangle {

    constructor(width, height, dept) {
        super(width, height);
        this.dept = dept;
    }
...

เราสร้างคลาส Cube ที่สืบทอดมาจากคลาส Rectangle คลาสนี้เป็นคลาสสำหรับลูกบาศก์ที่ประกอบไปด้วยความยาว width ความสูง height หรือเป็นการเพิ่มมิติที่สามจากรูปสี่เหลี่มเข้ามานั่นคือความลึก dept เนื่องจากคลาสนี้มี Property บางอย่างที่เหมือนกับคลาส Rectangle ดังนั้นการสืบทอดจากคลาสที่มันอยู่แล้วแทน

แม้ว่าคลาส Cube จะได้รับการสืบทอด Property ทั้งหมดจากคลาส Rectangle แล้ว แต่มันยังคงมี Property เพิ่มเข้าสำหรับความลึกเข้ามา ดังนั้นมันจำเป็นจะต้องมีคอนสตรัคเตอร์เป็นของมันเองสำหรับรับค่าความลึกในตอนที่สร้างออบเจ็ค

constructor(width, height, dept) {
    super(width, height); // Must call first
    this.dept = dept;
}

คลาส Cube จะต้องสร้างคอนสตรัคเตอร์เป็นของมันเองเนื่องจากมันในการสร้างกล่องนั่นต้องการสามพารามิเตอร์ แต่คอนสตรัคเตอร์ในคลาส Rectangle มีเพียงแค่สอง

แต่เนื่องจากว่า width และ height นั้นเป็น Property ที่สืบทอดมาจากคลาสหลัก Rectangle เราสามารถเรียกใช้คอนสตรัคเตอร์ของคลาสหลักเพื่อกำหนดค่าเหล่านี้ได้ด้วยคำสั่ง super(width, height) สิ่งที่เราต้องทำเพิ่มในคอนสตรัคเตอร์ของคลาส Cube คือกำหนด Property dept สำหรับความลึกที่เพิ่มเข้ามานั่นเอง

หรือเราสามารถกำหนด Property ใหม่ทั้งหมดในคอนสตรัคเตอร์ของคลาส Cube ได้ ซึ่งให้ผลลัพธ์การทำงานที่เหมือนกัน แต่อย่างี่บอกแล้วว่าเนื่องจากคอนสตรัคเตอร์ของคลาสหลักทำหน้าที่นี้อยู่แล้ว เราควรใช้มันแทนที่จะเขียนขึ้นใหม่

constructor(width, height, dept) {
    super(); // Must call first
    this.width = width;
    this.height = height;
    this.dept = dept;
}

แต่สิ่งหนึ่งที่เป็นข้อบังคับในภาษา JavaScript ก็คือเมื่อคลาสย่อยมีคอนสตรัคเตอร์เป็นของมันเอง เราจะต้องเรียกใช้คอนสตรัคเตอร์ของคลาสหลักก่อนเสมอ ไม่เช่นนั้นเราจะไม่สามารถใช้คำสั่ง this สำหรับกำหนดค่า Property ในคลาสนี้ได้

การ Override เมธอดและคำสั่ง super

การ Override เมธอดคือการกำหนดการทำงานใหม่ให้กับเมธอดในคลาสย่อยโดยที่เมธอดยังมีชื่อเหมือนเดิมแต่การทำงานเปลี่ยนไป เมื่อทำการ Override เมธอด การเรียกใช้งานเมธอดจากออบเจ็คของคลาสย่อยจะเป็นการทำงานใหม่ของเมธอดในคลาสใหม่แทน มาดูตัวอย่างการ Override เมธอดในภาษา JavaScript

method_override.js
class Animal {

    move() {
        console.log("Animal is moving");
    }
}

class Dog extends Animal {

    move() {
        console.log("Dog is running");
    }

    thisMove() {
        this.move();
    }

    baseMove() {
        super.move();
    }
}

let dog = new Dog();
dog.move();
dog.thisMove();
dog.baseMove();

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Dog is running
Dog is running
Animal is moving

ในตัวอย่างนี้ เราได้สร้างคลาส Animal ซึ่งเป็นคลาสของสัตว์ที่มีหนึ่งเมธอด move สำหรับการเคลื่อนที่ของสัตว์ จากนั้นเราสร้างคลาส Dog โดยสืบทอดมาจากคลาส Animal

move() {
    console.log("Dog is running");
}

คลาส Dog นั้นเป็นคลาสที่สืบทอดมาจากคลาส Animal แต่ในบางครั้งเราอาจต้องการกำหนดการทำงานเมธอด move ของคลาสนี้ใหม่ที่แตกต่างไปจากคลาสหลักของมัน แต่ยังคงต้องการใช้ชื่อเมธอดเดิม เราสามารถทำเช่นนั้นได้โดยการสร้างเมธอดในคลาส Dog อีกครั้งและกำหนดการทำงานใหม่สำหรับคลาสนี้ ซึ่งนี่เรียกว่าการ Override เมธอด

let dog = new Dog();
dog.move();
dog.thisMove();

นั่นจะทำให้ตอนนี้เมื่อเราเรียกใช้เมธอด this.move ภายในคลาสหรือจากออบเจ็ค dog.move จะเป็นการทำงานใหม่ของเมธอดจากคลาส Dog แทน ในกรณีนี้ ทำให้เราสามารถเปลี่ยนแปลงการทำงานของเมธอดไปตามหน้าที่ของแต่ละคลาสได้

baseMove() {
    super.move();
}

อย่างไรก็ตาม เรายังสามารถเข้าถึงเมธอดของคลาสหลักได้ด้วยคำสั่ง super ตามด้วยชื่อของเมธอดได้ แต่การเข้าถึงนี้จะต้องทำภายในคลาส Dog เท่านั้น เราจะไม่สามารถเข้าถึงเมธอดของคลาสหลักได้จากคำสั่ง this.move หรือ dog.move ซึ่งเป็นออบเจ็คของคลาส Dog ได้เนื่องจากว่ามันถูก Override ไปแล้วนั่นเอง

นี่จะทำให้แม้ว่าเมธอดจะถูก Override ไปแล้วแต่คลาสย่อยยังสามารถเข้าถึงการทำงานพื้นฐานจากคลาสหลักได้ในกรณีที่ต้องการใช้มัน ซึ่งจะแสดงให้คุณเห็นในตัวอย่างต่อไป

ในตัวอย่างนี้จะเป็นการ Override เมธอด draw เพื่อให้คลาสย่อยสามารถทำงานตามบริษทของแต่ละคลาสได้ และยังคงสามารถเรียกใช้เมธอดของคลาสหลัก ในขณะที่ชื่อของเมธอดนั้นยังคงเหมือนเดิมไม่เปลี่ยนแปลง

polygon.js
class Polygon {

    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    draw(data) {
        console.log("Perform actual draw from base class");
        console.log(data);
    }
}

class Rectangle extends Polygon {

    draw(ch) {
        let buffer = [];
        for (let i = 0; i < this.height; i++) {
            buffer.push(ch.repeat(this.width));
        }
        super.draw(buffer.join("\n"));
    }
}

let rect = new Rectangle(4, 3);
rect.draw("#");
rect.draw("M");

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Perform actual draw from base class
####
####
####
Perform actual draw from base class
MMMM
MMMM
MMMM

ในตัวอย่างนี้ เราได้สร้างคลาส Rectangle ที่สืบทอดมาจากคลาส Polygon เนื่องจากว่าในเมธอด draw ของคลาสหลักนั้นทำหน้าที่แค่วาดข้อความออกทางหน้าจอเท่านั้น ดังนั้นในคลาส Rectangle จะสามารถกำหนดรูปแบบการวาดเป็นของมันเอง และเรียกใช้เมธอดของคลาสหลักสำหรับการวาดจริงๆ

ซึ่งนี่จะทำให้ชัดเจนเมื่อเรามีอีกคลาสที่สืบทอดจากคลาส Polygon เช่นเดียวกัน แต่มันต้องการทำงานสำหรับการวาดที่แตกต่างกันออกไป ยกตัวอย่างเช่น

class Polygon {
    ...
}

class Triangle extends Polygon {

    draw(ch) {
        let buffer = [];
        for (let i = 0; i < this.height; i++) {
            buffer.push(
                ch.repeat((i + 1) / this.height * this.width)
            );
        }
        super.draw(buffer.join("\n"));
    }
}

let tri = new Triangle(5, 5);
tri.draw("#");

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Perform actual draw from base class
#
##
###
####
#####
Perform actual draw from base class
M
MM
MMM
MMMM
MMMMM

คลาส Triangle นั้นต้องการสืบทอดจากคลาส Polygon เช่นเดียวกัน เนื่องจากวิธีการวาดรูปสามเหลี่ยมนั้นจะแตกต่างจากรูปสี่เหลี่ยม นั่นทำให้คลาสนี้ทำการ Override เมธอด draw เพื่อให้สอดคล้องกับการทำงานของมันเอง แต่มันยังคงสามารถเรียกใช้งานเมธอด draw จากคลาสหลักสำหรับการวาดจริงๆ ได้

กล่าวคือเมธอด draw ในคลาส Polygon นั้นเป็นเมธอดหลักสำหรับการวาดที่คลาสย่อยสามารถเรียกใช้งานได้ เมื่อการทำงานในเมธอด draw ของแต่ละคลาสเสร็จสิ้น และในตอนท้ายเมธอด draw ของทั้งคลาส Rectangle และ Triangle เรียกใช้เมธอด super.draw ของคลาสหลัก

super.draw(buffer.join("\n"));

นี่เพื่อแสดงให้คุณเห็นว่าคลาสย่อยแต่ละคลาสสามารถ Override เมธอดเพื่อเพิ่มความสามารถที่มีเข้ามาได้ในขณะที่ยังสามารถใช้โค้ดบางอย่างร่วมกันจากคลาสหลักของมัน ถึงแม้ว่าในความเป็นจริงแล้ว เราสามารถเขียนโค้ดจากเมธอด draw ไว้ในคลาสย่อยทั้งสองได้ เช่นนำโค้ดสองบรรทัดนี้เขียนแยกในเมธอด draw ของคลาสย่อยแต่ละคลาส

console.log("Perform actual draw from base class");
console.log(data);

แต่การทำเช่นนี้จะเป็นการเขียนโค้ดซ้ำ ลองจินตนาการว่าในเมธอด draw ของคลาสหลักมีโค้ดที่การทำงานเป็นจำนวนมาก การนำมันมาเขียนใหม่ทั้งหมดคงจะไม่ใช้วิธีที่ดีและไม่ได้เป็นการใช้งานสามารถของการสืบทอด

การสืบทอดคลาสแบบหลายขั้น

คลาสในภาษา JavaScript นั้นสามารถสืบทอดมากกว่าหนึ่งลำดับชั้นได้ ยกตัวอย่างเช่น คลาส B สืบทอดมาจากคลาส A จากนั้นเราสามารถสร้างคลาส C เพื่อสืบทอดจากคลาส B ได้ นั่นจะทำให้คลาส C มีความสามารถเหมือนกับที่คลาส A และคลาส B มี เราเรียกการสืบทอดในรูปแบบนี้ว่าการสืบทอดแบบลำดับชั้น

ในตัวอย่างถัดไป เราจะมาแสดงวิธีการสืบทอดคลาสในรูปแบบดังกล่าว โดยการแสดงการทำงานของคลาสลูกบอล ซึ่งสามารถสืบทอดมาจากคลาสของทรงกลมและวงกลม นี่เป็นตัวอย่าง

circle.js
class Circle {

    constructor(r) {
        this.radius = r;
    }

    getDiameter() {
        return this.radius * 2;
    }

    getArea() {
        return Math.PI * (this.radius ** 2);
    }
}

class Sphere extends Circle {

    constructor(r) {
        super(r);
    }

    getVolume() {
        return 4 / 3 * Math.PI * (this.radius ** 3);
    }
}

class Ball extends Sphere {

    constructor(r, color, price) {
        super(r);
        this.color = color;
        this.price = price;
    }
}

let balls = [
    new Ball(3.5, "Red", 8.90),
    new Ball(4.33, "Blue", 15.00)
];

for (let ball of balls) {
    console.log("Product name: %s Ball", ball.color);
    console.log("Color: %s", ball.color);
    console.log("Price: %s USD", ball.price);
    console.log("Diameter: %s Inches", ball.getDiameter());
    console.log("Wind volume: %s Cubic inches",
        ball.getVolume().toFixed(2));
}

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Product name: Red Ball
Color: Red
Price: 8.9 USD
Diameter: 7 Inches
Wind volume: 179.59 Cubic inches
Product name: Blue Ball
Color: Blue
Price: 15 USD
Diameter: 8.66 Inches
Wind volume: 340.06 Cubic inches

ในตัวอย่างนี้ เป็นการแสดงการสืบทอดคลาสแบบหลายชั้นในภาษา JavaScript เราได้ประกาศสามคลาสซึ่งมีการสืบทอดแบบต่อเนื่องกัน คลาส Circle เป็นคลาสของวงกลมที่มีหนึ่ง Property radius ซึ่งเป็นรัศมีของวงกลม และมีสองเมธอดคือ getDiameter และ getArea สำหรับหาเส้นผ่านศูนย์กลางและพื้นที่ของวงกลมตามลำดับ

class Sphere extends Circle {

    constructor(r) {
        super(r);
    }

    getVolume() {
        return 4 / 3 * Math.PI * (this.radius ** 3);
    }
}

จากนั้นเราสร้างคลาส Sphere ซึ่งเป็นคลาสของทรงกลมที่สืบทอดมาจากคลาส Circle คลาสนี้มี Property radius เหมือนกับที่วงกลมมี แต่สิ่งที่เพิ่มเข้ามาคือเมธอด getVolume สำหรับค่าปริมาตรของทรงกลมจากรัศมีของมัน และอย่างที่เรารู้ว่าทรงกลมคือก็วงกลมในสามมิตินั่นเอง

class Ball extends Sphere {

    constructor(r, color, price) {
        super(r);
        this.color = color;
        this.price = price;
    }
}

และสุดท้ายเราสร้างคลาส Ball ที่สืบทอดมาจากคลาส Sphere เนื่องจากลูกบอลนั้นมีรัศมีและเป็นทรงกลม และคลาส Sphere มีสิ่งเหล่านี้อยู่แล้ว การสืบทอดทำให้เราไม่ต้องกำหนดคุณสมบัติและเมธอดทั้งหมดที่ลูกบอลควรมีใหม่ ทั้งหมดที่เราทำในคลาสของลูกบอลคือกำหนด Property เพิ่มเติมที่ลูกบอลควรมีนั่นก็คือ สีและราคาของลูกบอล

let balls = [
    new Ball(3.5, "Red", 8.90),
    new Ball(4.33, "Blue", 15.00)
];

for (let ball of balls) {
    console.log("Product name: %s Ball", ball.color);
    console.log("Color: %s", ball.color);
    console.log("Price: %s USD", ball.price);
    console.log("Diameter: %s Inches", ball.getDiameter());
    console.log("Wind volume: %s Cubic inches",
        ball.getVolume().toFixed(2));
}

จากนั้นเราสร้างออบเจ็คของลูกบอลจากคลาส Ball เก็บไว้ในอาเรย์และใช้คำสั่ง for loop เพื่อวนออบเจ็คเพื่อแสดงรายละเอียดของลูกบอลออกทางหน้าจอ นี่เป็นการแสดงรายละเอียดของสินค้าลูกบอลเหมือนกันในเว็บไซต์ขายของออนไลน์ที่คุณเคยเจอ

นอกจากนี้ เราสามารถสร้างคลาสอื่นที่มีคุณสมบัติเป็นทรงกลมเพื่อสืบทอดจากคลาส Sphere ได้ ในตัวอย่างนี้เราได้สร้างคลาส Star ซึ่งเป็นคลาสของดวงดาว นี่เป็นตัวอย่าง

class Circle {
    ...
}

class Sphere extends Circle {
    ...
}

class Star extends Sphere {

    constructor(name, r) {
        super(r);
        this.name = name;
    }

    getSurfaceArea() {
        return 4 * Math.PI * (this.radius ** 2);
    }
}

let s1 = new Star("Earth", 6371);
let s2 = new Star("Moon", 1737.1);
let s3 = new Star("Sun", 696340);

console.log("We live on the blue planet called %s", s1.name);
console.log("Radius: %f km", s1.radius);
console.log("Surface area: %d sq km", s1.getSurfaceArea());

console.log("%s has one planet called the %s", s1.name, s2.name);
console.log("Radius: %f km", s2.radius);
console.log("Surface area: %d sq km", s2.getSurfaceArea());

console.log("%s is orbiting around the %s", s1.name, s3.name);
console.log("The %s has a diameter of %d km",
    s3.name, s3.getDiameter());

กำหนดให้คลาส Circle และคลาส Sphere ได้ถูกประกาศเหมือนกับในตัวอย่างก่อนหน้า ดังนั้นในตัวอย่างนี้เราสร้างคลาส Star ที่สืบทอดมาจากคลาส Sphere โดยคลาสนั้นมี Property เพิ่มเติมคือชื่อของดวงดาว และเมธอดสำหรับหาพื้นที่ผิวของดวงดาว

ในความเป็นจริงแล้วดวงดาวไม่ได้เป็นทรงกลมที่สมบูรณ์แบบ ดังนั้นเมธอดหาพื้นที่ผิวนั้นเป็นค่าโดยประมาณเท่านั้น จากนั้นเราสร้างสามออบเจ็คจากคลาส Star ซึ่งเป็นออบเจ็คของโลก ดวงจันทร์ และดวงอาทิตย์ และแสดงรายละเอียดเกี่ยวกับออบเจ็คออกทางหน้าจอ

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

We live on the blue planet called Earth
Radius: 6371 km
Surface area: 510064471.90978825 sq km
Earth has one planet called the Moon
Radius: 1737.1 km
Surface area: 37919229.54297058 sq km
Earth is orbiting around the Sun
The Sun has a diameter of 1392680 km

คลาส Object

จะเห็นว่าคลาสสามารถสืบทอดแบบเป็นลำดับชั้นมายังคลาสที่เฉพาะเจาะจงมากขึ้นได้ ซึ่งนี่เป็นแนวคิดของคลาสและออบเจ็คในภาษา JavaScript โดยทุกคลาสในภาษา JavaScript นั้นสืบทอดมาจากคลาสสูงสุดที่เรียกว่า Object ยกตัวอย่างเช่น

class Being {
    // empty class
}

let a = new Being();
console.log(a.toString());
console.log(a instanceof Object);

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

[object Object]
true

นี่เป็นการพิสูจน์ให้เห็นว่าคลาสทุกคลาสในภาษา JavaScript นั้นสืบทอดมาจากคลาส Object ซึ่งนี่เกิดขึ้นอัตโนมัติเมื่อเราสร้างคลาสและไม่ได้ระบุคลาสสำหรับการสืบทอด นั่นเป็นเหตุผลว่าทำไมเราสามารถเรียกใช้งานเมธอด toString บนออบเจ็คของคลาส Being เนื่องจากมันถูกกำหนดในคลาสหลัก Object นั่นเอง

console.log(a instanceof Object);

ตัวดำเนินการ instanceof ใช้สำหรับตรวจสอบว่าออบเจ็คถูกสร้างมาจากคลาสดังกล่าวหรือไม่ ตัวดำเนินการตรวจสอบว่าออบเจ็ค a เป็นออบเจ็คของคลาส Being และคลาสหลักของมันทั้งหมด และส่งค่ากลับเป็นจริงถ้าหากใช่

ในบทนี้ คุณได้เรียนรู้เกี่ยวกับการสืบทอดคลาสในภาษา JavaScript ซึ่งเป็นคุณสมบัติที่ทรงพลังในการเขียนโปรแกรมเชิงวัตถุ เนื่องจากมันช่วยให้เราสามารถใช้คลาสเดิมที่มีอยู่แล้ว เพื่อนำมาเพิ่มเติมความสามารถเข้าไปได้