Heim >Web-Frontend >js-Tutorial >Detaillierte Erläuterung des JavaScript-Prototyps und Vererbungsbeispiele

Detaillierte Erläuterung des JavaScript-Prototyps und Vererbungsbeispiele

零下一度
零下一度Original
2017-06-26 10:55:541021Durchsuche

Prototypenkette

Jedes Objekt in JavaScript verfügt über ein integriertes _proto_. Dieses Attribut ist für die Programmierung nicht sichtbar. Ein Objekt oder ein Verweis auf null.

Wenn ein Objekt auf ein Attribut verweist, durchsucht die JavaScript-Engine es zunächst in der Attributtabelle des Objekts. Wenn es gefunden wird, führt es eine entsprechende Aktion aus Wenn es nicht in der eigenen Attributtabelle gefunden wird, suchen Sie in der Attributtabelle des Objekts, auf das durch das Attribut _proto_ verwiesen wird, und so weiter, bis das Attribut gefunden wird oder das _proto_ Attribut Punkte zu Bis null.

Die Referenzkette von _proto_ wird Prototypenkette genannt.

Beachten Sie, dass hier ein Leistungsoptimierungsproblem vorliegt: Je tiefer Sie in der Prototypenkette suchen, desto länger dauert es.

Prototypkette und Konstruktor

JavaScript ist eine objektorientierte Sprache und kann prototypische Vererbung durchführen.

Die Funktion in JavaScript hat ein Attribut prototype Dieses prototype-Attribut ist ein Objekt und eines seiner Attribute ist constructor Referenzieren Sie die Funktion selbst. Das heißt:

 func.prototype.constructor === func; // ==> true

Wozu dient dieses Attribut? Wir wissen, dass eine Funktion, wenn sie mit dem Operator new aufgerufen wird, ein neues Objekt als Konstruktor zurückgibt. Die _proto_-Eigenschaft dieses Objekts verweist auf die prototype-Eigenschaft seines Konstruktors.

Das ist also nicht schwer zu verstehen:

var obj = new Func();

obj.constructor == Func; // ==> true

Und dieses:

obj instanceof Func; // ==> true

auch übergeben Finden Sie die Implementierung der Eigenschaft constructor in der Prototypenkette. Die

-Eigenschaften verschiedener Instanzen, die vom _proto_-Konstruktor generiert werden, sind Verweise auf dasselbe prototype-Objekt. Das Ändern des prototype-Objekts wirkt sich also auf alle Instanzen aus.

Mehrere Möglichkeiten zur Implementierung der „Unterklassen“-Vererbung

Der Grund, warum Unterklassen in Anführungszeichen gesetzt werden müssen, liegt darin, dass das Konzept der „Klasse“ hier nicht streng ist. JavaScript ist eine objektorientierte Sprache, verfügt jedoch im Gegensatz zu Java und anderen Sprachen vor der Veröffentlichung des ES6-Standards über keine Klassendefinitionen.

Programmierer, die mit Sprachen wie Java vertraut sind, hoffen jedoch auch, dass sie bei der Verwendung von JavaScript Instanzen durch Klassen generieren und Code durch Unterklassen wiederverwenden können, ähnlich wie bei der Verwendung von Java. Wie kann man also vor ES6 eine „Klassen“-ähnliche Methode wie den folgenden Code verwenden?

var parent = new Parent("Sam");var child = new Children("Samson");

parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"child instanceof Parent; // ==> true

Wir sehen, dass wir hier den Konstruktor als Klasse verwenden.

Lassen Sie uns verschiedene Möglichkeiten zur Implementierung besprechen:

Der einfachste Weg

In Kombination mit dem Konzept der Prototypenkette können wir problemlos Code wie diesen schreiben:

function Parent(name){this.name = name;
}

Parent.prototype.say = function(){
    console.log("Hello, " + this.name + "!");
}function Children(name){this.name = name;
}

Children.prototype = new Parent();

Children.prototype.say = function(){
    console.log("Hello, " + this.name + "! hoo~~");
}

Der Nachteil dieser Methode liegt auf der Hand: Als Konstruktor einer Unterklasse muss sie auf ein Objekt der übergeordneten Klasse angewiesen sein. Die Eigenschaften name in diesem Objekt sind völlig nutzlos.

Die erste Verbesserung

// ...Children.prototype = Parent.prototype;// ...

Damit keine nutzlosen übergeordneten Klassenattribute generiert werden.

In diesem Fall beziehen sich jedoch die Prototypen der Unterklasse und der übergeordneten Klasse auf dasselbe Objekt, und eine Änderung des prototype der Unterklasse wirkt sich auch auf den Prototyp der übergeordneten Klasse aus.

Zu diesem Zeitpunkt stellten wir fest:

parent.say(); // ==> "Hello,Sam! hoo~~"

Diese erste Verbesserung ist schlimmer, als sich überhaupt nicht zu ändern.

Die zweite Verbesserung - temporärer Konstruktor/Object.create()

function F(){  // empty  }

F.prototype = Parent.prototype;

Children.prototype = new F();// ...parent.say(); // ==> "Hello, Sam!"child.say();  // ==> "Hello, Samson! hoo~~"

Ändern Sie auf diese Weise die Unterklasse. Der Prototyp ändert nur die Attribute einer F-Instanz von und ändert sich nicht Parent.prototype, wodurch das obige Problem gelöst wird.

Im Zeitalter von ES5 können wir dies auch direkt tun:

Children.prototype = Object.create(Parent.prototype);

这里的思路是一样的,都是让子类的prototype不直接引用父类prototype。目前的现代浏览器几乎已经添加了对这个方法的支持。(但我们下面会仍以临时构造函数为基础)

但是细细思考,这个方案仍有需要优化的地方。例如:如何让父类的构造函数逻辑直接运用到子类中,而不是再重新写一遍一样的?这个例子中只有一个name属性的初始化,那么假设有很多属性且逻辑一样的话,岂不是没有做到代码重用?

第三次改进——构造函数方法借用

使用apply/call,实现“方法重用”的思想。

function Children(name){
    Parent.apply(this, arguments);// do other initial things}

“圣杯”模式

现在完整的代码如下:

function Parent(name){this.name = name;
}

Parent.prototype.say = function(){
    console.log("Hello, " + this.name + "!");
}function Children(name){
    Parent.apply(this, arguments);// do other initial things}function F(){  // empty  }

F.prototype = Parent.prototype;

Child.prototype = new F();

Children.prototype.say = function(){
    console.log("Hello, " + this.name + "! hoo~~");
}

这就是所谓“圣杯”模式,听着很高大上吧?

以上就是ES3的时代,我们用来实现原型继承的一个近似最佳实践。

“圣杯”模式的问题

“圣杯”模式依然存在一个问题:虽然父类和子类实例的继承的prototype对象不是同一个实例,但是这两个prototype对象上面的属性引用了同样的对象。

假设我们有:

Parent.prototype.a = { x: 1};// ...

那么即使是“圣杯”模式下,依然会有这样的问题:

parent.x // ==> 1child.x  // ==> 1child.x = 2;
parent.x // ==>2

问题在于,JavaScript的拷贝不是 深拷贝(deepclone)

要解决这个问题,我们可以利用属性递归遍历,自己实现一个深拷贝的方法。这个方法在这里我就不写了。

ES6

ES6极大的支持了工程化,它的标准让浏览器内部实现类和类的继承:

class Parent {
    constructor(name) { //构造函数  this.name = name;
    }
    say() {
          console.log("Hello, " + this.name + "!");
    }
}

class Children extends Parent {
    constructor(name) { //构造函数super(name);    //调用父类构造函数// ...    }
    say() {
          console.log("Hello, " + this.name + "! hoo~~");
    }
}

从此走上强类型的不归路。。。

上张顿悟图

什么?还不明白?!麻烦出门左拐。推荐阮老师JavaScript万物诞生记。 

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des JavaScript-Prototyps und Vererbungsbeispiele. 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