1. Objektvererbungsmechanismus
In diesem Beispiel wird UML verwendet, um den Vererbungsmechanismus sehr gut zu erklären.
Der Vererbungsmechanismus lässt sich am einfachsten anhand eines klassischen Beispiels geometrischer Formen veranschaulichen. Tatsächlich gibt es nur zwei Arten von geometrischen Formen: Ellipsen (die kreisförmig sind) und Polygone (die eine bestimmte Anzahl von Seiten haben). Ein Kreis ist eine Art Ellipse, die nur einen Brennpunkt hat. Dreiecke, Rechtecke und Fünfecke sind alle Arten von Polygonen, die eine unterschiedliche Anzahl von Seiten haben. Ein Quadrat ist eine Art Rechteck, bei dem alle Seiten gleich lang sind. Dies stellt eine perfekte Vererbungsbeziehung dar und erklärt den objektorientierten Vererbungsmechanismus gut.
In diesem Beispiel ist die Form die Basisklasse von Ellipse und Polygon (normalerweise können wir sie auch als übergeordnete Klasse bezeichnen, und alle Klassen erben von ihr). Die Ellipse hat einen Brennpunkt, der angibt, wie viele Brennpunkte die Ellipse hat. Der Kreis erbt von der Ellipse, daher ist der Kreis eine Unterklasse der Ellipse und die Ellipse die Oberklasse des Kreises. Ebenso sind Dreiecke, Rechtecke und Fünfecke Unterklassen von Polygonen, die ihre Oberklasse sind. Schließlich erbt das Quadrat das Rechteck.
Diese Vererbungsbeziehung lässt sich am besten anhand eines Diagramms erläutern, hier kommt UML (Unified Modeling Language) ins Spiel. Eine der Hauptanwendungen von UML ist die visuelle Darstellung komplexer Objektbeziehungen wie Vererbung. Das folgende Diagramm ist ein UML-Diagramm, das die Beziehung zwischen einer Form und ihren Unterklassen erläutert:
In UML stellt jedes Feld eine Klasse dar, die durch den Klassennamen beschrieben wird. Die Liniensegmente an der Spitze von Dreiecken, Rechtecken und Fünfecken kommen zusammen und zeigen auf Formen, was darauf hinweist, dass diese Klassen von Formen erben. Ebenso veranschaulicht der vom Quadrat zum Rechteck zeigende Pfeil die Vererbungsbeziehung zwischen ihnen.
2. Implementierung des ECMAScript-Vererbungsmechanismus
Um den Vererbungsmechanismus mit ECMAScript zu implementieren, können Sie mit der Basisklasse beginnen, von der Sie erben möchten. Alle vom Entwickler definierten Klassen können als Basisklassen dienen. Aus Sicherheitsgründen können native Klassen und Hostklassen nicht als Basisklassen dienen. Dadurch wird der öffentliche Zugriff auf kompilierten Code auf Browserebene verhindert, der für böswillige Angriffe verwendet werden könnte.
Nachdem Sie die Basisklasse ausgewählt haben, können Sie deren Unterklassen erstellen. Es liegt ganz bei Ihnen, ob Sie eine Basisklasse verwenden oder nicht. Manchmal möchten Sie möglicherweise eine Basisklasse erstellen, die nicht direkt verwendet werden kann, sondern nur dazu dient, Unterklassen gemeinsame Funktionen bereitzustellen. In diesem Fall wird die Basisklasse als abstrakte Klasse betrachtet. Obwohl ECMAScript abstrakte Klassen nicht so streng definiert wie andere Sprachen, erstellt es manchmal Klassen, deren Verwendung nicht zulässig ist. Normalerweise nennen wir diese Klasse eine abstrakte Klasse.
Die erstellte Unterklasse erbt alle Eigenschaften und Methoden der Oberklasse, einschließlich der Implementierung von Konstruktoren und Methoden. Denken Sie daran, dass alle Eigenschaften und Methoden öffentlich sind, sodass Unterklassen direkt auf diese Methoden zugreifen können. Unterklassen können auch neue Eigenschaften und Methoden hinzufügen, die in der Oberklasse nicht vorhanden sind, oder sie können Eigenschaften und Methoden der Oberklasse überschreiben. Da JS keine orthodoxe objektorientierte Sprache ist, müssen auch einige Substantive geändert werden.
3. ECMAScript-Vererbungsmethoden
In der ECMAScript-Sprache wird die geerbte Klasse (Basisklasse) als Supertyp und die Unterklasse (oder abgeleitete Klasse) als Untertyp bezeichnet. Wie bei anderen Funktionen implementiert ECMAScript die Vererbung auf mehr als eine Weise. Dies liegt daran, dass der Vererbungsmechanismus in JavaScript nicht explizit spezifiziert, sondern durch Nachahmung implementiert wird. Dies bedeutet, dass nicht alle Vererbungsdetails vollständig vom Interpreter behandelt werden. Als Entwickler haben Sie das Recht zu entscheiden, welche Vererbungsmethode für Sie am besten geeignet ist. Hier sind einige spezifische Vererbungsmethoden für Sie.
(1) Prototypenkettenmethode
Diese Form der Vererbung wurde ursprünglich für die Prototypenkette in ECMAScript verwendet. Im vorherigen Blogbeitrag wurde die Prototypmethode zum Erstellen von Objekten vorgestellt. Prototypenketten erweitern diesen Ansatz, um den Vererbungsmechanismus auf interessante Weise zu implementieren. Das Prototypobjekt ist eine Vorlage, und die zu instanziierenden Objekte basieren auf dieser Vorlage. Zusammenfassend lässt sich sagen, dass alle Eigenschaften und Methoden des Prototypobjekts an alle Instanzen dieser Klasse übergeben werden. Die Prototypkette nutzt diese Funktionalität, um den Vererbungsmechanismus zu implementieren. Schauen wir uns ein Beispiel an:
function A() {//超类型A中必须没有参数 this.color = "red"; this.showColor = function () { return this.color; }; }; function B() {//子类型B this.name = "John"; this.showName = function () { return this.name; }; }; B.prototype = new A();//子类型B继承了超类型A,通过原型,形成链条 var a = new A(); var b = new B(); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
In der Prototypenkette funktioniert der Instanzoperator ebenfalls auf einzigartige Weise. Für alle Instanzen von B gibt „instanceof“ sowohl für A als auch für B „true“ zurück. Dies ist ein äußerst nützliches Werkzeug in der schwach typisierten Welt von ECMAScript, kann jedoch nicht verwendet werden, wenn Objektidentität verwendet wird. Zum Beispiel:
var b = new B(); document.write(b instanceof A);//输出:true document.write(b instanceof B);//输出:true
使用原型链方式实现了继承,但是这种方式无法共享和子类型给超类型传递参数。我们可以借用构造函数方式(也就是对像冒充)的方式来解决这两个问题。
(2)对象冒充方式
对象冒充方式的其原理如下:构造函数使用this关键字给所有属性和方法赋值(即采用对象声明的构造函数方式)。因为构造函数只是一个函数,所以可使A构造函数成为B的方法,然后调用它。B就会收到A的构造函数中定义的属性和方法。例如,用下面的方式改写上面的例子创建对象A和B:
call()方法
function A(Color) {//创建超类型A this.color = Color; this.showColor = function () { return this.color; }; }; function B(Color,Name) {//创建子类型B A.call(this, Color);//对象冒充,给超类型传参 this.name = Name;//新添加的属性 this.showName = }; var a = new A("blue"); var b = new B("red", "John"); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
apply()方法
和上面call()方法唯一的区别就是在子类型B中的代码:
A.call(this,arguments);//对象冒充,给超类型传参
当然,只有超类型中的参数顺序与子类型中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。
使用对象冒充方式虽然解决了共享和传参的问题,但是没有原型,复用就更不可能了,所以我们组合上述的两种方式,即原型链方式和对象冒充的方式实现JS的继承。
(3)混合方式
这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。由于这种混合方式使用了原型链,所以instanceof运算符仍能正确运行。
在上一篇文章,创建对象的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。用这两种方式重写前面的例子,代码如下:
function A(Color) { this.color = Color; }; A.prototype.showColor = function () { return this.color; }; function B(Color, Name) { A.call(this, Color);//对象冒充 this.name = Name; }; B.prototype = new A();//使用原型链继承 B.prototype.showName = function () { return this.name; }; var a = new A("blue"); var b = new B("red", "John"); document.write(a.showColor());//输出:blue document.write(b.showColor());//输出:red document.write(b.showName());//输出:John
继承的方式和创建对象的方式有一定的联系,推荐使用的继承方式还时原型链和对象冒充的混合方式。使用这种混合方式可以避免一些不必要的问题。
看这篇文章的时候,必须看一下前面的创建对象的方式:详解JavaScript基于面向对象之创建对象(1)和详解JavaScript基于面向对象之创建对象(2)。
以上就是本文的全部内容,希望对大家的学习有所帮助。