Εκτέλεση Μηχανισμού Εκπαίδευσης ECMAScript

Υλοποίηση μηχανισμού κληρονομικότητας

Για να υλοποιήσετε το μηχανισμό κληρονομικότητας με το ECMAScript, μπορείτε να ξεκινήσετε από την κλάση που θέλετε να κληρονομήσετε. Οποιαδήποτε κλάση που ορίζεται από τους προγραμματιστές μπορεί να χρησιμοποιηθεί ως βάση κλάσης. Για λόγους ασφαλείας, οι τοπικές κλάσεις και οι κλάσεις κινητού υπολογιστή δεν μπορούν να χρησιμοποιηθούν ως βάση κλάσης, για να αποφευχθεί η κοινή πρόσβαση στο κωδικό που έχει εκτελεστεί από το επίπεδο του περιηγητή, ο οποίος μπορεί να χρησιμοποιηθεί για κακόβουλα επιθέσεις.

Μετά την επιλογή της βάσης κλάσης, μπορείτε να δημιουργήσετε τους κλάδους της. Η χρήση της βάσης κλάσης εξαρτάται από εσάς. Μερικές φορές μπορεί να θέλετε να δημιουργήσετε μια κλάση που δεν μπορεί να χρησιμοποιηθεί άμεσα, αλλά μόνο για να παρέχει κοινές λειτουργίες στους κλάδους. Σε αυτή την περίπτωση, η βάση κλάση θεωρείται αβύθμιση κλάση.

Αν και το ECMAScript δεν ορίζει αυστηρά τις αβύθμιες κλάσεις όπως άλλες γλώσσες, μερικές φορές δημιουργεί κλάσεις που δεν επιτρέπεται να χρησιμοποιηθούν. Τυπικά, αυτές οι κλάσεις ονομάζονται αβύθμιες κλάσεις.

Ο κλάδος που δημιουργείται 继承所有父类的属性和方法,包括构造函数及方法的实现。记住,所有属性和方法都是公用的,因此子类可以直接访问这些方法。Ο κλάδος μπορεί επίσης να προσθέσει νέες ιδιότητες και μεθόδους που δεν υπάρχουν στον πατέρα κλάδο και μπορεί να καλύψει τις ιδιότητες και μεθόδους του πατέρα κλάδου.

Τρόποι κληρονομικότητας

Όπως και με άλλες λειτουργίες, η μεθόδος που το ECMAScript υλοποιεί για την κληρονομικότητα δεν είναι καθορισμένη. Είναι μια μιμήση. Αυτό σημαίνει ότι δεν όλες οι λεπτομέρειες της κληρονομικότητας χειρίζονται από το διαβάστη. Καθώς开发者, έχετε το δικαίωμα να επιλέξετε το πιο κατάλληλο τρόπο κληρονομικότητας.

Κατεβάστε παρακάτω μερικές συγκεκριμένες μεθόδους κληρονομικότητας.

Υποδυσιμότητα αντικειμένων

Όταν η αρχική ECMAScript δεν ήθελε να σχεδιαστεί η υποδυσιμότητα αντικειμένων (object masquerading). Αυτή αναπτύχθηκε όταν οι разработτές άρχισαν να κατανοούν τον τρόπο λειτουργίας των συναρτήσεων, ειδικότερα πώς χρησιμοποιούνται οι λέξεις-κλειδιά this στο περιβάλλον των συναρτήσεων.

Το πώς λειτουργεί είναι το εξής: ο κατασκευαστής χρησιμοποιεί το this για να δώσει αξία σε όλες τις ιδιότητες και τις μεθόδους (δηλαδή, χρησιμοποιώντας τον κατασκευαστή της κλάσης που ανακοινώνεται). Ως συνάρτηση, ο κατασκευαστής μπορεί να γίνει μέθοδος της ClassB, και τότε μπορεί να καλείται. Η ClassB θα λάβει τις ιδιότητες και τις μεθόδους που ορίστηκαν στον κατασκευαστή της ClassA. Για παράδειγμα, μπορείτε να ορίσετε την ClassA και την ClassB ως εξής:

function ClassA(sColor) {
    this.color = sColor;
    this.sayColor = function () {
        alert(this.color);
    };
}
function ClassB(sColor) {
}

Θυμάστε; η λέξη-κλειδί this αναφέρεται στο αντικείμενο που δημιουργείται από τον κατασκευαστή. Ωστόσο, σε αυτή τη μέθοδο, το this δείχνει το αντικείμενο που ανήκει. Αυτός ο μηχανισμός είναι να χρησιμοποιείται η ClassA ως κανονική συνάρτηση για να δημιουργήσει το μηχανισμό κληρονομικότητας, αντί για κατασκευαστή. Μπορείτε να επιτύχετε τον μηχανισμό κληρονομικότητας με τον παρακάτω τρόπο χρησιμοποιώντας τον κατασκευαστή ClassB:

function ClassB(sColor) {
    this.newMethod = ClassA;
    this.newMethod(sColor);
    delete this.newMethod;
}

Σε αυτόν τον κώδικα, η μέθοδος newMethod της ClassA αποδίδεται (θυμηθείτε, το όνομα της συνάρτησης είναι απλώς ένας δείκτης προς αυτήν). Στη συνέχεια, καλούνται αυτές οι μεθόδους, μεταφέροντας στη συνάρτηση την παραμέτρο sColor του κατασκευαστή της ClassB. Η τελευταία γραμμή κώδικα διαγράφει την αναφορά στην ClassA, οπότε δεν μπορεί να καλείται πλέον.

Όλες οι νέες ιδιότητες και μεθόδους πρέπει να οριστούν μετά την αφαίρεση της γραμμής κώδικα με την νέα μέθοδο. Εάν όχι, μπορεί να καλύψουν τις ιδιότητες και τις μεθόδους της υπερκλάσης:

function ClassB(sColor, sName) {
    this.newMethod = ClassA;
    this.newMethod(sColor);
    delete this.newMethod;
    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

Για να αποδείξουμε ότι ο παραπάνω κώδικας είναι έγκυρος, μπορείτε να εκτελέσετε τον παρακάτω παράδειγμα:

var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor();	// Εξαγωγή "blue"
objB.sayColor();	// Εξαγωγή "red"
objB.sayName();		//ηξοδίαση "John"

Η υποδυσιμότητα αντικειμένων μπορεί να επιτύχει πολυπληρωματική κληρονομικότητα

Είναι ενδιαφέρον ότι η υποδυσιμότητα αντικειμένων υποστηρίζει πολυπληρωματική κληρονομικότητα. Δηλαδή, μια κλάση μπορεί να κληρονομήσει πολλαπλές υπερκλάσεις. Η πολυπληρωματική κληρονομικότητα που δείχνεται με UML είναι όπως εμφανίζεται στο παρακάτω σχήμα:

Η μηχανισμός 继承 UML 图像 παράδειγμα

Για παράδειγμα, αν υπάρχουν δύο κλάσεις ClassX και ClassY, η ClassZ θέλει να 继承这两个类, μπορεί να χρησιμοποιήσει τον παρακάτω κώδικα:

function ClassZ() {
    this.newMethod = ClassX;
    this.newMethod();
    delete this.newMethod;
    this.newMethod = ClassY;
    this.newMethod();
    delete this.newMethod;
}

TIY

这里存在一个弊端,如果存在两个类 ClassX 和 ClassY 具有同名的属性或方法,ClassY 具有高优先级。因为它从后面的类继承。除这点小问题之外,用对象冒充实现多重继承机制轻而易举。

由于这种继承方法的流行,ECMAScript 的第三版为 Function 对象加入了两个方法,即 call() 和 apply()。

call() 方法

call() 方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作 this 的对象。其他参数都直接传递给函数自身。例如:

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "blue";
sayColor.call(obj, "The color is ", "a very nice color indeed.");

在这个例子中,函数 sayColor() 在对象外定义,即使它不属于任何对象,也可以引用关键字 this。对象 obj 的 color 属性等于 blue。调用 call() 方法时,第一个参数是 obj,说明应该赋予 sayColor() 函数中的 this 关键字值是 obj。第二个和第三个参数是字符串。它们与 sayColor() 函数中的参数 sPrefix 和 sSuffix 匹配,最后生成的消息 "The color is blue, a very nice color indeed." 将被显示出来。

要与继承机制的对象冒充方法一起使用该方法,只需将前三行的赋值、调用和删除代码替换即可:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.call(this, sColor);
    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

TIY

这里,我们需要让 ClassA 中的关键字 this 等于新创建的 ClassB 对象,因此 this 是第一个参数。第二个参数 sColor 对两个类来说都是唯一的参数。

apply() 方法

apply() 方法有两个参数,用作 this 的对象和要传递给函数的参数的数组。例如:

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "blue";
sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));

Αυτός ο παράδειγμα είναι ίδιο με το προηγούμενο παράδειγμα, αλλά τώρα καλείται η μέθοδος apply(). Όταν καλείται η μέθοδος apply(), ο πρώτος παράμετρος παραμένει obj, δηλαδή πρέπει να δοθεί η τιμή του κλειδιού this στη συνάρτηση sayColor(). Ο δεύτερος παράμετρος είναι ένας πίνακας από δύο αλφαριθμητικούς συμβόλα, που ταιριάζει με τις παραμέτρους sPrefix και sSuffix στη συνάρτηση sayColor(), και η τελική ειδοποίηση είναι "The color is blue, a very nice color indeed." που θα εμφανιστεί.

Αυτή η μέθοδος χρησιμοποιείται επίσης για να αντικαταστήσει τα τρία προηγούμενα σώματα κώδικα αναθέσεων, κλήσεων και διαγραφής νέων μεθόδων:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.apply(this, new Array(sColor));
    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

Επίσης, ο πρώτος παράμετρος παραμένει this, και ο δεύτερος είναι ένας πίνακας με μια τιμή color. Μπορεί να μεταφερθεί ολόκληρο το αντικείμενο arguments της ClassB ως δεύτερη παράμετρος στη μέθοδο apply():

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.apply(this, arguments);
    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

TIY

Φυσικά, μπορεί να μεταφερθεί το αντικείμενο παραμέτρων μόνο όταν η σειρά των παραμέτρων στη μητέρα κλάση είναι ταυτόσημη με τη σειρά των παραμέτρων στη θυγατρική κλάση. Αν δεν είναι, πρέπει να δημιουργηθεί ένας διαφορετικός αριθμητικός πίνακας, με τις παραμέτρους να τοποθετούνται στη σωστή σειρά. Επιπλέον, μπορεί να χρησιμοποιηθεί η μέθοδος call().

Prototype chaining (prototype chaining)

Αυτή η μορφή κληρονομικότητας χρησιμοποιείται αρχικά για το prototype chaining στο ECMAScript. Το προηγούμενο κεφάλαιο περιέγραψε τον τρόπο ορισμού της κλάσης με τρόπο prototype. Το prototype chaining επεκτείνει αυτόν τον τρόπο, επιτυγχάνοντας μια ενδιαφέρουσα μορφή κληρονομικότητας.

Καθώς μάθαμε στο προηγούμενο κεφάλαιο, το prototype είναι ένα μοντέλο, και κάθε αντικείμενο που δημιουργείται βασίζεται σε αυτό το μοντέλο. Συνοψίζοντας, οποιαδήποτε ιδιότητα ή μέθοδος του prototype μεταφέρεται σε κάθε παράδειγμα της κλάσης. Η μηχανισμός κληρονομικότητας χρησιμοποιεί αυτή τη λειτουργία.

Εάν οριστεί ξανά με τρόπο πρωτοτύπου η κλάση από το προηγούμενο παράδειγμα, θα γίνει ως εξής:

function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();

Η μαγεία της μεθόδου πρωτοτύπων βρίσκεται στη βελτιωμένη γραμμή κώδικα που επισημαίνεται με μπλε. Εδώ, η ιδιότητα prototype του ClassB ορίζεται ως παράδειγμα του ClassA. Αυτό είναι πολύ ενδιαφέρον, καθώς θέλουμε όλες τις ιδιότητες και τα μεθόδους του ClassA, αλλά δεν θέλουμε να τις προσθέσουμε μια προς μια στη ιδιότητα prototype του ClassB. Υπάρχει καλύτερη μέθοδος από αυτήν για να αποδώσουμε την παράδειγμα του ClassA στη ιδιότητα prototype;

Σημείωση:Κλήση του κατασκευαστή του ClassA χωρίς να του παραδώσουμε παραμέτρους. Αυτό είναι το πρότυπο στη αλυσίδα πρωτοτύπων. Βεβαιώσου ότι ο κατασκευαστής δεν έχει παραμέτρους.

Όπως και στην αντικατάσταση αντικειμένων, όλες οι ιδιότητες και τα μεθόδους της υποκλάσης πρέπει να εμφανίζονται μετά την αποθήκευση της ιδιότητας prototype, γιατί όλες οι μεθόδους που αποθηκεύονται πριν από αυτή θα διαγραφούν. Γιατί; Γιατί η ιδιότητα prototype αντικαταστάθηκε από ένα νέο αντικείμενο, το αρχικό αντικείμενο που προσθέτει τις μεθόδους θα διαγραφεί. Επομένως, το κώδικα για την προσθήκη της ιδιότητας name και της μεθόδου sayName() στη κλάση ClassB είναι ως εξής:

function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
    alert(this.name);
};

Μπορείς να δοκιμάσεις αυτό το κώδικα για να ελέγξεις το παρακάτω κείμενο:

var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName();

TIY

Επιπλέον, στη αλυσίδα πρωτοτύπων, ο τρόπος λειτουργίας του运算ateur instanceof είναι επίσης μοναδικός. Για όλες τις εκδόσεις του ClassB, instanceof επιστρέφει true για ClassA και ClassB. Για παράδειγμα:

var objB = new ClassB();
alert(objB instanceof ClassA);	// Εκτύπωση "true"
alert(objB instanceof ClassB);	// Εκτύπωση "true"

Στο κόσμο των 弱 τύπων του ECMAScript, αυτό είναι ένα εξαιρετικά χρήσιμο εργαλείο, αλλά δεν μπορεί να χρησιμοποιηθεί κατά τη διάρκεια της αντικατάστασης αντικειμένων.

Η δυσκολία της αλυσίδας πρωτοτύπων είναι ότι δεν υποστηρίζει την πολυκληρονομικότητα. θυμήσου ότι η αλυσίδα πρωτοτύπων θα χρησιμοποιήσει αντικείμενα άλλου τύπου για να αντικαταστήσει την ιδιότητα prototype της κλάσης.

Μέθοδος μίξης

Η μέθοδος κληρονομικότητας αυτή χρησιμοποιεί τη συνάρτηση κατασκευής για να ορίσει την κλάση, και όχι οποιαδήποτε πρωτότυπο. Το κύριο πρόβλημα της αντικατάστασης αντικειμένων είναι ότι πρέπει να χρησιμοποιηθεί η μέθοδος κατασκευής, η οποία δεν είναι η καλύτερη επιλογή. Ωστόσο, αν χρησιμοποιηθεί η αλυσίδα πρωτοτύπων, δεν μπορεί να χρησιμοποιηθεί η συνάρτηση κατασκευής με παραμέτρους. Πώς θα επιλέξει ο προγραμματιστής; Η απάντηση είναι απλή, και τα δύο τα χρησιμοποιεί.

Στο προηγούμενο κεφάλαιο, εξήγησα πώς η καλύτερη μέθοδος δημιουργίας κλάσεων είναι η χρήση κατασκευαστών για την προσδιορισμό ιδιοτήτων και η χρήση prototypes για την προσδιορισμό μεθόδων. Αυτή η μέθοδος είναι επίσης ιδανική για τον μηχανισμό προσδοχής, χρησιμοποιώντας το αντικείμενο για να προσαρμόσει τις ιδιότητες του κατασκευαστή και τη prototype χάραξη για να προσαρμόσει τα μεθόδους του prototype αντικειμένου. Με αυτές τις δύο μεθόδους, μπορείτε να επαναγράψετε τον παρακάτω παράδειγμα ως εξής:}}

function ClassA(sColor) {
    this.color = sColor;
}
ClassA.prototype.sayColor = function () {
    alert(this.color);
};
function ClassB(sColor, sName) {
    ClassA.call(this, sColor);
    this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
    alert(this.name);
};

Σε αυτό το παράδειγμα, ο μηχανισμός προσδοχής υλοποιείται από δύο βασικά κόκκινα κείμενα. Στο πρώτο βασικό κόκκινο κείμενο, στο κατασκευαστή του ClassB, χρησιμοποιείται το αντικείμενο για να προσαρμόσει την ιδιότητα sColor της κλάσης ClassA. Στο δεύτερο βασικό κόκκινο κείμενο, χρησιμοποιείται η prototype χάραξη για να προσαρμόσει τα μεθόδους της κλάσης ClassA. Επειδή αυτή η μείξη χρησιμοποιεί τη prototype χάραξη, ο συναγωνιστής εξακολουθεί να λειτουργεί σωστά.

Ο παρακάτω παράδειγμα δοκιμάζει αυτόν τον κώδικα:

var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor();	// Εξαγωγή "blue"
objB.sayColor();	// Εξαγωγή "red"
objB.sayName();	// Εξαγωγή "John"

TIY