การถ่ายทอดทางพันธุกรรมแบบคลาสสิกกับการถ่ายทอดทางพันธุกรรมของต้นแบบในจาวาสคริปต์


118

ฉัน googled ลิงก์มากมายและไม่สามารถรับความคิดที่ดีเกี่ยวกับความแตกต่างระหว่างการสืบทอดแบบคลาสสิกกับการสืบทอดต้นแบบได้หรือไม่?

ฉันได้เรียนรู้บางสิ่งจากสิ่งเหล่านี้ แต่ฉันยังสับสนเกี่ยวกับแนวคิด

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

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

//superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

//subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);

การถ่ายทอดทางพันธุกรรมแบบคลาสสิกใช้การสืบทอดต้นแบบภายในหรือไม่?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

จากลิงค์ด้านบนฉันได้เรียนรู้ว่าเราไม่สามารถเพิ่มวิธีการใหม่ในขณะดำเนินการในการสืบทอดแบบคลาสสิกได้ ถูกต้องหรือไม่ แต่คุณสามารถตรวจสอบรหัสดังกล่าวข้างต้นฉันสามารถเพิ่ม "ย้าย" วิธีการและวิธีการใด ๆ ในเวลาทำงานผ่านต้นแบบ นี่คือการสืบทอดแบบคลาสสิกต้นแบบ? ถ้าเป็นเช่นนั้นการสืบทอดคลาสสิกที่แท้จริงและการสืบทอดต้นแบบคืออะไร? ฉันสับสนเกี่ยวกับเรื่องนั้น

การถ่ายทอดทางพันธุกรรมของ Prototypal

function Circle(radius) {
    this.radius = radius;
}
Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};
Circle.prototype.circumference: function () {
    return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);

สิ่งนี้คล้ายกับการสืบทอดแบบคลาสสิกหรือไม่? ฉันสับสนโดยสิ้นเชิงเกี่ยวกับมรดกต้นแบบคืออะไร? มรดกคลาสสิกคืออะไร? เหตุใดการสืบทอดแบบคลาสสิกจึงไม่ดี?

คุณช่วยยกตัวอย่างง่ายๆเพื่อให้เข้าใจสิ่งเหล่านี้ได้ดีขึ้นอย่างง่ายๆ

ขอบคุณ

มหาเทพ


ทำซ้ำลองดูที่stackoverflow.com/questions/1595611/…
Silviu Burcea

5
ไม่แน่ใจว่าคุณกำลังทำอะไรอยู่ที่นี่ - รหัสบล็อกแรกคือการสืบทอดต้นแบบไม่ใช่แบบคลาสสิก รหัสบล็อกที่สองของคุณไม่มีการสืบทอดเลย!
Alnitak

xplain พฤษภาคมนี้: blog.stephenwyattbush.com/2012/05/01/…
HasanAboShally

@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ลิงค์นี้บอกว่าอันนี้เป็นมรดกคลาสสิก นั่นคือเหตุผลที่ฉันสับสน
SivaRajini

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่คุณอาจต้องการหลีกเลี่ยงการสืบทอดแบบคลาสสิกโปรดดูคำพูดของฉัน "มรดกคลาสสิกล้าสมัย
Eric Elliott

คำตอบ:


248

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

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern

ดังที่คุณเห็นการถ่ายทอดทางพันธุกรรมและการสืบทอดแบบคลาสสิกเป็นสองกระบวนทัศน์ที่แตกต่างกันของการถ่ายทอดทางพันธุกรรม บางภาษาเช่น Self, Lua และ JavaScript สนับสนุนการสืบทอดต้นแบบ อย่างไรก็ตามภาษาส่วนใหญ่เช่น C ++, Java และ C # รองรับการสืบทอดแบบคลาสสิก


ภาพรวมโดยย่อของการเขียนโปรแกรมเชิงวัตถุ

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

Abstraction:การแสดงสิ่งต่างๆในโลกแห่งความเป็นจริงในโปรแกรมคอมพิวเตอร์

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

ตอนนี้วัตถุบางอย่างมีหลายอย่างที่เหมือนกัน ตัวอย่างเช่นจักรยานโคลนและ Harley Davidson มีหลายอย่างที่เหมือนกัน

จักรยานโคลน:

จักรยานโคลน

ฮาร์เลย์เดวิดสัน:

Harley Davidson

จักรยานโคลนและ Harley Davidson ต่างก็เป็นมอเตอร์ไซค์ ดังนั้นจักรยานจึงเป็นลักษณะทั่วไปของทั้งจักรยานโคลนและ Harley Davidson

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson

ในตัวอย่างข้างต้นจักรยานโคลนและ Harley Davidson ล้วนเป็นนามธรรม อย่างไรก็ตามจักรยานเป็นสิ่งที่เป็นนามธรรมโดยทั่วไปของจักรยานโคลนและ Harley Davidson (กล่าวคือทั้งจักรยานโคลนและ Harley Davidson เป็นจักรยานประเภทเฉพาะ)

ลักษณะทั่วไป:สิ่งที่เป็นนามธรรมของนามธรรมที่เฉพาะเจาะจงมากขึ้น

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


การเขียนโปรแกรมเชิงวัตถุแบบคลาสสิก

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

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | Man            | Class of object johnDoe.              |
| 3                    | Human          | Superclass of class Man.              |
+----------------------+----------------+---------------------------------------+

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

อ็อบเจ็กต์ในภาษาโปรแกรมเชิงวัตถุคลาสสิกสามารถสร้างได้โดยการสร้างอินสแตนซ์คลาสเท่านั้น:

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

ในการสรุปในภาษาโปรแกรมเชิงวัตถุคลาสสิกออบเจ็กต์เป็นนามธรรมของเอนทิตีในโลกแห่งความเป็นจริงและคลาสคือการสรุป (เช่น abstractions ของอ็อบเจ็กต์หรือคลาสอื่น ๆ )

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


Prototypal Object-Oriented Programming

ภาษาโปรแกรมเชิงวัตถุ Prototypal นั้นง่ายกว่าภาษาการเขียนโปรแกรมเชิงวัตถุแบบคลาสสิกมากเนื่องจากในการเขียนโปรแกรมเชิงวัตถุต้นแบบเรามีนามธรรมเพียงประเภทเดียวเท่านั้น (เช่นวัตถุ) ตัวอย่างเช่นพิจารณา:

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | man            | Prototype of object johnDoe.          |
| 3                    | human          | Prototype of object man.              |
+----------------------+----------------+---------------------------------------+

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

อ็อบเจ็กต์ในภาษาการเขียนโปรแกรมเชิงวัตถุต้นแบบอาจถูกสร้างขึ้นได้ทั้งแบบ ex-nihilo (เช่นไม่มีอะไรเลย) หรือจากอ็อบเจ็กต์อื่น (ซึ่งกลายเป็นต้นแบบของอ็อบเจ็กต์ที่สร้างขึ้นใหม่):

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

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

  1. สิ่งที่เป็นนามธรรมมีเพียงประเภทเดียว
  2. การสรุปเป็นเพียงวัตถุ

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


Isomorphism ระดับต้นแบบ

คุณต้องสังเกตว่าต้นแบบและคลาสนั้นคล้ายกันมาก นั่นคือเรื่องจริง พวกเขาเป็น. ในความเป็นจริงมันคล้ายกันมากจนคุณสามารถใช้ต้นแบบในการจำลองคลาสได้:

function CLASS(base, body) {
    if (arguments.length < 2) body = base, base = Object.prototype;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

การใช้CLASSฟังก์ชันข้างต้นคุณสามารถสร้างต้นแบบที่ดูเหมือนคลาสได้:

var Human = CLASS(function () {
    var milliseconds = 1
      , seconds      = 1000 * milliseconds
      , minutes      = 60 * seconds
      , hours        = 60 * minutes
      , days         = 24 * hours
      , years        = 365.2425 * days;

    this.constructor = function (name, sex, dob) {
        this.name = name;
        this.sex = sex;
        this.dob = dob;
    };

    this.age = function () {
        return Math.floor((new Date - this.dob) / years);
    };
});

var Man = CLASS(Human, function (Human) {
    this.constructor = function (name, dob) {
        Human.constructor.call(this, name, "male", dob);
        if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
    };
});

var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));

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


ข้อสรุป

ในผลรวมที่เราได้เรียนรู้ว่าเป็นนามธรรมเป็น"แนวคิดทั่วไปที่เกิดขึ้นจากการสกัดคุณลักษณะทั่วไปจากตัวอย่างที่เฉพาะเจาะจง"และทั่วไปที่เป็น"นามธรรมของนามธรรมเฉพาะเจาะจงมากขึ้น" นอกจากนี้เรายังได้เรียนรู้เกี่ยวกับความแตกต่างระหว่างการถ่ายทอดทางพันธุกรรมแบบต้นแบบและแบบคลาสสิกและวิธีที่ทั้งสองเป็นสองหน้าของเหรียญเดียวกัน

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

ป.ล. ฉันเป็นคนที่เขียนบล็อกโพสต์ " Why Prototypal Inheritance Matters " และตอบคำถามว่า " ประโยชน์ของการถ่ายทอดทางพันธุกรรมของต้นแบบในรูปแบบคลาสสิก " คำตอบของฉันคือคำตอบที่ได้รับการยอมรับ


2
ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมของคุณ ฉันต้องเข้าใจว่ารูปแบบต้นแบบนั้นดีกว่าเมื่อเปรียบเทียบกับรูปแบบตัวสร้างอย่างไรตัวอย่างใดบ้าง
SivaRajini

1
ฉันได้เขียนบทวิจารณ์เชิงเปรียบเทียบเกี่ยวกับตัวสร้างเทียบกับต้นแบบในบล็อกของฉัน: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah

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

1
@Swanidhi ไม่หากคุณใช้ JavaScript แสดงว่าคุณกำลังใช้รูปแบบการสืบทอดต้นแบบ อย่างไรก็ตาม JavaScript มีสองรสชาติของการสืบทอดต้นแบบ: การใช้ฟังก์ชัน (เช่นรูปแบบตัวสร้าง) และการใช้อ็อบเจกต์ (เช่นรูปแบบโปรโตไทป์)
Aadit M Shah

5
@Swanidhi ไม่มันยังคงเป็นต้นแบบ JavaScript ไม่มี "คลาส" ดังนั้นจึงไม่มีอะไรเลยใน JavaScript ในรูปแบบคลาสสิกรวมถึงตัวสร้าง มันยังคงเป็นมรดกต้นแบบ รูปแบบแปลก ๆ ของการถ่ายทอดทางพันธุกรรมต้นแบบที่ผู้คนสับสนกับการสืบทอดแบบคลาสสิก ในช่วงสั้นprogramming with classes = classical inheritanceprogramming with prototypes = prototypal inheritance, programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance, หวังว่าจะชี้แจงสิ่งต่างๆ
Aadit M Shah

8

ก่อนที่จะข้ามไปสู่การสืบทอดเราจะดูโมเดลหลักสองแบบเพื่อสร้างอินสแตนซ์ (วัตถุ) ในจาวาสคริปต์:

แบบจำลองคลาสสิก:วัตถุถูกสร้างขึ้นจากพิมพ์เขียว (คลาส)

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();

Prototypal model:วัตถุถูกสร้างขึ้นโดยตรงจากวัตถุอื่น

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);

ไม่ว่าในกรณีใดการสืบทอด * สามารถทำได้โดยการเชื่อมโยงวัตถุโดยใช้วัตถุต้นแบบ

(* เมธอดคลาสฐานสามารถเข้าถึงได้ผ่านคลาสที่ได้รับผ่านอ็อบเจ็กต์ต้นแบบและไม่จำเป็นต้องนำเสนออย่างชัดเจนในคลาสที่ได้รับ)

นี่คือคำอธิบายที่ดีเพื่อให้เข้าใจได้ดีขึ้น ( http://www.objectplayground.com/ )


0

สุนัขเป็นสัตว์ Suzanna เป็นสุนัข ในการสืบทอดคลาสสิกAnimalเป็นคลาสDogเป็นคลาสย่อยAnimalและsuzannaเป็นตัวอย่างของไฟล์Dog.

ในมรดกต้นแบบไม่มีคลาส คุณมีanimalซึ่งเป็นวัตถุ A dogเป็นวัตถุอื่นซึ่งโคลนและขยายanimal(วัตถุต้นแบบ) เป็นวัตถุที่สามซึ่งสำเนาและขยายsuzannadog

let animal = {hasChlorophyl: false};

let dog = Object.create(animal);
Object.assign(dog, {
  speak() {
    console.log("Woof!");
  }
});

let suzanna = Object.create(dog);
Object.assign(suzanna, {
  name: "Suzanna"
});

suzanna.speak();

ถ้าคุณเขียนDogแทนdogโดยเฉพาะอย่างยิ่งถ้าคุณDogสร้างฟังก์ชัน "ตัวสร้าง" บางชนิดแสดงว่าคุณไม่ได้ทำการสืบทอดต้นแบบ คุณกำลังทำ (หลอก) มรดกคลาสสิก ความจริงที่คุณใช้Object.create()เพื่อบรรลุสิ่งนี้ไม่ได้หมายความว่าคุณกำลังทำการสืบทอดต้นแบบ

ในความเป็นจริง JavaScript รองรับเฉพาะการสืบทอดต้นแบบเท่านั้น ตัวnewดำเนินการและ.prototypeแอตทริบิวต์ที่สับสนอยู่ที่นั่นเพื่อทำให้การสืบทอดต้นแบบมีลักษณะเหมือนการสืบทอดคลาสสิก (หลอก)

ดักลาสคร็อกฟอร์ดสำรวจเรื่องนี้ในหนังสือ "JavaScript: The Good Parts"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.