Maison > interface Web > js tutoriel > le corps du texte

Explication détaillée du prototype JavaScript et des exemples d'héritage

零下一度
Libérer: 2017-06-26 10:55:54
original
996 Les gens l'ont consulté

Chaîne de prototypes

Chaque objet en JavaScript a un attribut _proto_ intégré. Cet attribut n'est pas visible pour la programmation. Un objet ou une référence à <.>nul.

Lorsqu'un objet fait référence à un attribut, le moteur JavaScript le recherchera d'abord dans la propre table d'attributs de l'objet. S'il est trouvé, il effectuera la correspondance. opérations de lecture et d'écriture. S'il n'est pas trouvé dans sa propre table attributaire, recherchez dans la table attributaire de l'objet référencé par l'attribut _proto_, et ainsi de suite jusqu'à ce que l'attribut soit trouvé ou le attribuez des points à Jusqu'à _proto_. null

La chaîne de référence de est appelée la _proto_chaîne prototype.

Notez qu'il y a ici un problème d'optimisation des performances : plus vous recherchez profondément dans la chaîne de prototypes, plus cela prend de temps.

Chaîne et constructeur de prototypes

JavaScript est un langage orienté objet et peut faire de l'

héritage prototypique

. La fonction en JavaScript a un attribut

Cet attribut prototype est un objet, et l'un de ses attributs est prototype. Référencez la fonction elle-même. C'est-à-dire : constructor

 func.prototype.constructor === func; // ==> true
Copier après la connexion
A quoi sert cet attribut ? Nous savons que lorsqu'une fonction est appelée à l'aide de l'opérateur
, elle renverra un nouvel objet en tant que constructeur.

La propriété new de cet objet fait référence à la propriété _proto_ prototype de son constructeur. Donc ce n'est pas difficile à comprendre :

var obj = new Func();

obj.constructor == Func; // ==> true
Copier après la connexion
Et celui-ci :

obj instanceof Func; // ==> true
Copier après la connexion
est aussi passé Retrouvez l'implémentation de la propriété

sur la chaîne prototype. Les propriétés constructor

des différentes instances générées par le constructeur

sont des références au même objet _proto_. Ainsi, la modification de l'objet prototype affectera toutes les instances. prototypePlusieurs façons d'implémenter l'héritage des "sous-classes"

La raison pour laquelle les sous-classes doivent être entre guillemets est que le concept de "classe" n'est pas rigoureux ici. JavaScript est un langage orienté objet, mais contrairement à Java et à d'autres langages, il ne dispose pas de définitions de classe avant la publication de la norme ES6.

Cependant, les programmeurs familiers avec des langages tels que Java espèrent également qu'en utilisant JavaScript, ils pourront générer des instances via des classes et réutiliser le code via des sous-classes, de la même manière qu'avec Java. Alors avant ES6, comment utiliser une méthode de type « classe » comme le code suivant ?

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

parent.say(); // ==> "Hello, Sam!"child.say(); // ==> "Hello, Samson! hoo~~"child instanceof Parent; // ==> true
Copier après la connexion
On voit qu'ici on utilise le constructeur comme une classe.

Discutons de plusieurs façons de l'implémenter :

La manière la plus simple

Combinée au concept de chaîne de prototypes, nous pouvons facilement écrire du code comme celui-ci :

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~~");
}
Copier après la connexion
L'inconvénient de cette méthode est évident : en tant que constructeur d'une sous-classe, elle doit s'appuyer sur un objet de la classe parent. Les propriétés

de cet objet sont complètement inutiles. nameLa première amélioration

// ...Children.prototype = Parent.prototype;// ...
Copier après la connexion
Pour que les attributs de classe parent inutiles ne soient pas générés.

Cependant, dans ce cas, les prototypes de la sous-classe et de la classe parent font référence au même objet, et la modification du

de la sous-classe affectera également le prototype de la classe parent. prototypeA cette époque nous avons découvert :

parent.say(); // ==> "Hello,Sam! hoo~~"
Copier après la connexion
Cette première amélioration est pire que de ne pas changer du tout.

La deuxième amélioration - constructeur temporaire/

Object.create()

function F(){  // empty  }

F.prototype = Parent.prototype;

Children.prototype = new F();// ...parent.say(); // ==> "Hello, Sam!"child.say();  // ==> "Hello, Samson! hoo~~"
Copier après la connexion
De cette façon, modifiez la sous-classe Le prototype ne modifie que les attributs d'une
instance

de F et ne change pas , résolvant ainsi le problème ci-dessus. Parent.prototypeA l'ère de

ES5

, on peut aussi le faire directement :

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

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

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

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

function Children(name){
    Parent.apply(this, arguments);// do other initial things}
Copier après la connexion

“圣杯”模式

现在完整的代码如下:

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~~");
}
Copier après la connexion

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

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

“圣杯”模式的问题

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

假设我们有:

Parent.prototype.a = { x: 1};// ...
Copier après la connexion

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

parent.x // ==> 1child.x  // ==> 1child.x = 2;
parent.x // ==>2
Copier après la connexion

问题在于,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~~");
    }
}
Copier après la connexion

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

上张顿悟图

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!