Heim >Web-Frontend >js-Tutorial >So implementieren Sie die Vererbung in Javascript

So implementieren Sie die Vererbung in Javascript

青灯夜游
青灯夜游Original
2021-06-30 18:00:503507Durchsuche

So implementieren Sie die Vererbung in JavaScript: 1. Erstellen Sie einen Prototyp und verwenden Sie den Prototyp-Prototyp direkt zum Entwerfen der Vererbung. 2. Verwenden Sie den dynamischen Prototyp, um die Vererbung zu implementieren. 4. Verwenden Sie die Klassenvererbung, um den übergeordneten Klassenkonstruktor in der untergeordneten Klasse aufzurufen und die Vererbung zu implementieren.

So implementieren Sie die Vererbung in Javascript

Die Betriebsumgebung dieses Tutorials: Windows 7-System, JavaScript-Version 1.8.5, Dell G3-Computer.

Mehrere Möglichkeiten zur Implementierung der Vererbung in JS

Prototypen erstellen

Es gibt zwei Probleme bei der direkten Verwendung des Prototyp-Prototyps zum Entwerfen der Klassenvererbung.

Da der Konstruktor im Voraus deklariert wird und die Prototypeigenschaften nach der Deklaration der Klassenstruktur definiert werden, können Parameter nicht dynamisch über den Konstruktor an den Prototyp übergeben werden. Auf diese Weise sehen die instanziierten Objekte alle gleich aus und haben keine Persönlichkeit. Wenn Prototyp-Eigenschaftswerte geändert werden, sind alle Instanzen betroffen.

Wenn es sich bei dem Wert des Prototypattributs um Referenztypdaten handelt und der Attributwert in einer Objektinstanz geändert wird, wirkt sich dies auf alle Instanzen aus.

Beispiel 1

Definieren Sie einfach den Buchtyp und instanziieren Sie ihn dann.

function Book () {};  //声明构造函数
Book.prototype.o = {x : 1, y : 2};  //构造函数的原型属性o是一个对象
var book1 = new Book ();  //实例化对象book1
var book2 = new Book ();  //实例化对象book2
console.log(book1.o.x);  //返回1
console.log(book2.o.x);  //返回1
book2.o.x = 3;  //修改实例化对象book2中的属性x的值
console.log(book1.o.x);  //返回3
console.log(book2.o.x);  //返回3

Da das Prototypattribut o ein Referenzwert ist, sind die Werte des Attributs o aller Instanzen Verweise auf dasselbe Objekt. Sobald sich der Wert von o ändert, wirkt sich dies auf alle Instanzen aus.

Der Konstruktionsprototyp ist ein hybrides Entwurfsmuster, das zur Lösung des Prototypmusters entwickelt wurde. Es mischt das Konstruktormuster und das Prototypmuster, um die oben genannten Probleme zu vermeiden.

Implementierungsmethode: Prototypeigenschaften, die sich gegenseitig beeinflussen können, und Eigenschaften, die Parameter dynamisch übergeben möchten, können mithilfe des Konstruktormusters unabhängig voneinander entworfen werden. Für Methoden oder Attribute, die kein individuelles Design erfordern und gemeinsame Merkmale aufweisen, können Sie das Prototypmuster zum Entwerfen verwenden.

Beispiel 2

Befolgen Sie die oben genannten Entwurfsprinzipien, entwerfen Sie zwei der Eigenschaften in das Konstruktormuster und die Entwurfsmethode ist das Prototypmuster.

function Book (title, pages) {  //构造函数模式设计
    this.title = title;
    this.pages = pages;
}
Book.prototype.what = function () {  //原型模式设计
    console.log(this.title + this.pages);
};
var book1 = new Book("JavaScript 程序设计", 160);
var book2 = new Book("C语言程序设计", 240);
console.log(book1.title);
console.log(book2.title);

Das Constructed Prototype-Muster ist der empfohlene Standard zum Definieren von Klassen in ECMAScript. Im Allgemeinen wird empfohlen, das Konstruktormuster zum Definieren aller Eigenschaften und das Prototypmuster zum Definieren aller Methoden zu verwenden. Auf diese Weise werden alle Methoden nur einmal erstellt und jede Instanz kann Eigenschaftswerte nach Bedarf festlegen. Dies ist auch das am weitesten verbreitete Designmuster.

Dynamischer Prototyp

Gemäß objektorientierten Designprinzipien sollten alle Mitglieder eines Typs in einer Klassenstruktur gekapselt werden. Zum Beispiel:

function Book (title, pages) {  //构造函数模式设计
    this.title = title;
    this.pages = pages;
    Book.prototype.what = function () {  //原型模式设计,位于类的内部
        console.log(this.title + this.pages);
    };
}

Aber jedes Mal, wenn es instanziiert wird, werden die in der Book-Klasse enthaltenen Prototypmethoden wiederholt erstellt, wodurch eine große Anzahl von Prototypmethoden generiert und Systemressourcen verschwendet werden. Sie können if verwenden, um festzustellen, ob die Prototypmethode vorhanden ist. Andernfalls wird die Methode erstellt.

function Book (title, pages) {
    this.title = title;
    this.pages = pages;
    if (typeof Book.isLock == "undefined") {  //创建原型方法的锁,如果不存在则创建
        Book.prototype.what = function () {
            console.log(this.title + this.pages);
        };
        Book.isLock = true;  //创建原型方法后,把锁锁上,避免重复创建
    }
}
var book1 = new Book("JavaScript 程序设计", 160);
var book2 = new Book("C语言程序设计", 240);
console.log(book1.title);
console.log(book2.title);

typeof Book.isLock-Ausdruck kann den Typ des Attributwerts erkennen, wenn er eine undefinierte Zeichenfolge zurückgibt, was darauf hinweist, dass keine Prototypmethode erstellt wurde, und die Erstellung eines Prototypmethodensatzes ermöglicht Setzen Sie den Wert dieses Attributs auf true , damit Sie nicht wiederholt Prototypmethoden erstellen müssen. Stattdessen wird hier der Klassenname Book verwendet, da der Prototyp zur Klasse selbst und nicht zur Objektinstanz gehört.

Der dynamische Prototyp-Modus und der konstruierte Prototyp-Modus sind in der Leistung gleichwertig, Benutzer können frei wählen, aber der konstruierte Prototyp-Modus wird häufiger verwendet.

Factory-Muster

Factory-Muster ist die grundlegendste Art, Typen zu definieren und auch das am häufigsten verwendete Entwicklungsmuster in JavaScript. Es kapselt einfach die Objektinstanziierung in einer Funktion und ruft dann die Funktion auf, um eine schnelle und stapelweise Produktion von Instanzobjekten zu erreichen.

Beispiel 1

Das folgende Beispiel entwirft einen Autotyp: Es enthält drei Attribute: Fahrzeugfarbe, Anzahl der Antriebsräder und Kraftstoffverbrauch pro 100 Kilometer und definiert außerdem eine Methode zur Anzeige der Fahrzeugfarbe.

function Car (color, drive, oil) {  //汽车类
    var _car =  new Object();  //临时对象
        _car.color = color;  //初始化颜色
        _car.drive = drive;  //初始化驱动轮数
        _car.oil = oil;  //初始化百公里油耗
        _car.showColor = function () {  //方法,提示汽车颜色
            console.log(this.color);
        };
        return _car;  //返回实例
}
var car1 = Car("red", 4, 8);
var car2 = Car("blue", 2, 6);
car1.showColor();  //输出“red”
car2.showColor();  //输出“blue”

Der obige Code ist ein einfacher Fabrikmustertyp, mit dem Sie schnell mehrere Autoinstanzen erstellen können. Ihre Strukturen sind jedoch unterschiedlich. Sie können unterschiedliche Farben, Anzahl der Antriebsräder usw. initialisieren Kraftstoffverbrauch pro 100 Kilometer.

Beispiel 2

In einem Typ ist eine Methode ein Verhalten oder eine Operation, die eine bestimmte Aufgabe basierend auf Initialisierungsparametern ausführen kann und gemeinsame Merkmale aufweist. Daher können Sie erwägen, die Methode außerhalb der Funktion Car() zu platzieren, um zu vermeiden, dass bei jeder Instanziierung eine Funktion erstellt wird, und damit jede Instanz dieselbe Funktion gemeinsam nutzen kann.

function showColor () {  //公共方法,提示汽车颜色
    console.log(this.color);
};
function Car (color, drive, oil) {  //汽车类
    var _car = new Object();  //临时对象
        _car.color = color;  //初始化颜色
        _car.drive = drive;  //初始化驱动轮数
        _car.oil = oil;  //初始化百公里油耗
        _car.showColor = showColor;  //引用外部函数
    return _car;  //返回实例
}

Im oben umgeschriebenen Code ist die Funktion showColor() vor der Funktion Car() definiert. Indem wir in Car() auf die externe Funktion showColor() verweisen, vermeiden wir, dass bei jeder Instanziierung eine neue Funktion erstellt werden muss. Funktional löst dies das Problem der wiederholten Erstellung einer Funktion; semantisch ähnelt die Funktion jedoch weniger einer Objektmethode.

Klassenvererbung

Die Entwurfsmethode der Klassenvererbung: Rufen Sie den Konstruktor der übergeordneten Klasse in der untergeordneten Klasse auf.

Bei der Implementierung der Klassenvererbung in JavaScript müssen Sie die folgenden drei technischen Probleme beachten.

在子类中,使用 apply 调用父类,把子类构造函数的参数传递给父类父类构造函数。让子类继承父类的私有属性,即 Parent.apply(this, arguments); 代码行。

在父类和子类之间建立原型链,即 Sub.prototype = new Parent(); 代码行。通过这种方式保证父类和子类是原型链上的上下级关系,即子类的 prototype 指向父类的一个实例。

恢复子类的原型对象的构造函数,即 Sub.prototype.constructor=Sub;语句行。当改动 prototype 原型时,就会破坏原来的 constructor 指针,所以必须重置 constructor。

示例1

下面示例演示了一个三重继承的案例,包括基类、父类和子类,它们逐级继承。

//基类Base
function Base (x) {  //构造函数Base
    this.get = function () {  //私有方法,获取参数值
        return x;
    }
}
Base.prototype.has = function () {  //原型方法,判断get()方法返回值是否为0
    return ! (this.get() == 0);
}
//父类Parent
function Parent () {  //构造函数Parent
    var a = [];  //私有数组a
    a = Array.apply(a, arguments);  //把参数转换为数组
    Base.call(this, a.length);  //调用Base类,并把参数数组长度传递给它
    this.add = function () {  //私有方法,把参数数组补加到数组a中并返回
        return a.push.apply(a, arguments)
    }
    this.geta = function () {  //私有方法,返回数组a
        return a;
    }
}
Parent.prototype = new Base();  //设置Parent原型为Base的实例,建立原型链
Parent.prototype.constructor = Parent;  //恢复Parent类原型对象的构造器
Parent.prototype.str = function (){  //原型方法,把数组转换为字符串并返回
    return this.geta().toString();
}
//子类Sub
function Sub () {  //构造函数
    Parent.apply(this, arguments);  //调用Parent类,并把参数数组长度传递给它
    this.sort = function () {  //私有方法,以字符顺序对数组进行排序
        var a = this.geta();  //获取数组的值
        a.sort.apply(a, arguments);  //调用数组排序方法 sort()对数组进行排序
    }
}
Sub.prototype = new Parent();  //设置Sub原型为Parent实例,建立原型链
Sub.prototype.constructor = Sub;  //恢复Sub类原型对象的构造器
//父类Parent的实例继承类Base的成员
var parent = new Parent (1, 2, 3, 4);  //实例化Parent类
console.log(parent.get());  //返回4,调用Base类的方法get()
console.log(parent.has());  //返回true,调用Base类的方法has()
//子类Sub的实例继承类Parent和类Base的成员
var sub = new Sub (30, 10, 20, 40);  //实例化Sub类
sub.add(6, 5);  //调用Parent类方法add(),补加数组
console.log(sub.geta());  //返回数组30,10,20,40,6,5
sub.sort();  //排序数组
console.log(sub.geta());  //返回数组10,20,30,40,5,6
console.log(sub.get());  //返回4,调用Base类的方法get()
console.log(sub.has());  //返回true,调用Base类的方法has()
console.log(sub.str());  //返回10,20,30,40,5,6

【设计思路】

设计子类 Sub 继承父类 Parent,而父类 Parent 又继承基类 Base。Base、Parent、Sub 三个类之间的继承关系是通过在子类中调用的构造函数来维护的。

例如,在 Sub 类中,Parent.apply(this, arguments); 能够在子类中调用父类,并把子类的参数传递给父类,从而使子类拥有父类的所有属性。

同理,在父类中,Base.call(this, a.length); 把父类的参数长度作为值传递给基类,并进行调用,从而实现父类拥有基类的所有成员。

从继承关系上看,父类继承了基类的私有方法 get(),为了确保能够继承基类的原型方法,还需要为它们建立原型链,从而实现原型对象的继承关系,方法是添加语句行 Parent.prototype=new Base();。

同理,在子类中添加语句 Sub.prototype=new Parent();,这样通过原型链就可以把基类、父类和子类串连在一起,从而实现子类能够继承父类属性,还可以继承基类的属性。

示例2

下面尝试把类继承模式封装起来,以便规范代码应用。

function extend (Sub, Sup) {  //类继承封装函数
    var F = function () {};  //定义一个空函数
    F.prototype = Sup.prototype;  //设置空函数的原型为父类的原型
    Sub.prototype = new F ();  //实例化空函数,并把父类原型引用传给给子类
    Sub.prototype.constructor = Sub;  //恢复子类原型的构造器为子类自身
    Sub.sup = Sup.prototype;  //在子类定义一个私有属性存储父类原型
    //检测父类原型构造器是否为自身
    if (Sup.prototype.constructor == Object.prototype.constructor) {
        Sup.prototype.constructor = Sup;  //类继承封装函数
    }
}

【操作步骤】

1) 定义一个封装函数。设计入口为子类和父类对象,函数功能是子类能够继承父类的所有原型成员,不涉及出口。

function extend (Sub, Sup) {  //类继承封装函数
    //其中参数Sub表示子类,Sup表示父类
}

2) 在函数体内,首先定义一个空函数 F,用来实现功能中转。设计它的原型为父类的原型,然后把空函数的实例传递给子类的原型,这样就避免了直接实例化父类可能带来的系统负荷。因为在实际开发中,父类的规模可能会很大,如果实例化,会占用大量内存。

3) 恢复子类原型的构造器为子类自己。同时,检测父类原型构造器是否与 Object 的原型构造器发生耦合。如果是,则恢复它的构造器为父类自身。

下面定义两个类,尝试把它们绑定为继承关系。

function A (x) {  //构造函数A
    this.x = x;  //私有属性x
    this.get = function () {  //私有方法get()
        return this.x;
    }
}
A.prototype.add = function () {  //原型方法add()
    return this.x + this.x;
}
A.prototype.mul = function () {  //原型方法mul()
    return this.x * this.x;
}
function B (x) {  //构造函数B
    A.call (this.x);  //在函数体内调用构造函数A,实现内部数据绑定
}
extend (B, A);  //调用封装函数,把A和B的原型捆绑在一起
var f = new B (5);  //实例化类B
console.log(f.get());  //继承类A的方法get(),返回5
console.log(f.add());  //继承类A的方法add(),返回10
console.log(f.mul());  //继承类A的方法mul(),返回25

在函数类封装函数中,有这么一句 Sub.sup=Sup.prototype;,在上面代码中没有被利用,那么它有什么作用呢?为了解答这个问题,先看下面的代码。

extend (B, A);
B.prototype.add = function () {  //为B类定义一个原型方法
    return this.x + "" + this.x;
}

上面的代码是在调用封装函数之后,再为 B 类定义了一个原型方法,该方法名与基类中原型方法 add() 同名,但是功能不同。如果此时测试程序,会发现子类 B 定义的原型方法 add() 将会覆盖父类 A 的原型方法 add()。

console.log(f.add());  //返回字符串55,而不是数值10

如果在 B 类的原型方法 add() 中调用父类的原型方法 add(),避免代码耦合现象发生。

B.prototype.add = function () {  //定义子类B的原型方法add()
    return B.sup.add.call(this);  //在函数内部调用父类方法add()
}

【相关推荐:javascript学习教程

Das obige ist der detaillierte Inhalt vonSo implementieren Sie die Vererbung in Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn