J'ai une classe de base:
function Monster() {
this.health = 100;
}
Monster.prototype.growl = function() {
console.log("Grr!");
}
Que je veux étendre et créer une autre classe avec:
function Monkey extends Monster() {
this.bananaCount = 5;
}
Monkey.prototype.eatBanana {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
J'ai fait pas mal de recherches et il semble y avoir beaucoup de solutions compliquées pour le faire en JavaScript. Quel serait le moyen le plus simple et le plus fiable d’accomplir cela dans JS?
Mis à jour ci-dessous pour ES6
Ce document MDN décrit bien l’extension des classes:
_ { https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript } _
En particulier, voici maintenant qu'ils s'en occupent:
// define the Person Class
function Person() {}
Person.prototype.walk = function(){
alert ('I am walking!');
};
Person.prototype.sayHello = function(){
alert ('hello');
};
// define the Student class
function Student() {
// Call the parent constructor
Person.call(this);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function(){
alert('hi, I am a student');
}
// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
alert('goodBye');
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Notez que Object.create()
n'est pas pris en charge par certains navigateurs plus anciens, y compris IE8:
Si vous êtes en mesure de les prendre en charge, le document MDN lié suggère d'utiliser un remplissage multiple ou l'approximation suivante:
function createObject(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor();
}
Il est préférable d'utiliser Student.prototype = createObject(Person.prototype)
comme ceci plutôt que new Person()
car elle évite d'appeler la fonction constructeur du parent } lors de l'héritage du prototype et n'appelle le constructeur parent que lorsque le constructeur de l'héritier est appelé.
Heureusement, les concepteurs JavaScript ont entendu notre appel à l'aide et ont adopté une approche plus appropriée de ce problème.
MDN a un autre bon exemple sur l'héritage de classe ES6, mais je montrerai exactement le même ensemble de classes que ci-dessus, reproduit dans ES6:
class Person {
sayHello() {
alert('hello');
}
walk() {
alert('I am walking!');
}
}
class Student extends Person {
sayGoodBye() {
alert('goodBye');
}
sayHello() {
alert('hi, I am a student');
}
}
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
Propre et compréhensible, comme nous le souhaitons tous. Gardez à l'esprit que si ES6 est assez commun, il s'agit de non pris en charge partout :
ES6 vous donne maintenant la possibilité d’utiliser class & includes keywords:
Ensuite, votre code sera:
Vous avez une classe de base:
class Monster{
constructor(){
this.health = 100;
}
growl() {
console.log("Grr!");
}
}
Que vous voulez étendre et créer une autre classe avec:
class Monkey extends Monster {
constructor(){
super(); //don't forget "super"
this.bananaCount = 5;
}
eatBanana() {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
}
Essaye ça:
Function.prototype.extends = function(parent) {
this.prototype = Object.create(parent.prototype);
};
Monkey.extends(Monster);
function Monkey() {
Monster.apply(this, arguments); // call super
}
Edit: Je mets une brève démonstration ici http://jsbin.com/anekew/1/edit . Notez que extends
est un mot réservé dans JS et que vous pouvez recevoir des avertissements lorsque vous filtrez votre code, vous pouvez simplement le nommer inherits
, c'est ce que je fais habituellement.
Avec cet assistant en place et en utilisant un objet props
comme seul paramètre, l'héritage dans JS devient un peu plus simple:
Function.prototype.inherits = function(parent) {
this.prototype = Object.create(parent.prototype);
};
function Monster(props) {
this.health = props.health || 100;
}
Monster.prototype = {
growl: function() {
return 'Grrrrr';
}
};
Monkey.inherits(Monster);
function Monkey() {
Monster.apply(this, arguments);
}
var monkey = new Monkey({ health: 200 });
console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"
Si vous n'aimez pas l'approche du prototype, car elle ne se comporte pas vraiment de manière pratique, vous pouvez essayer ceci:
var BaseClass = function()
{
this.some_var = "foobar";
/**
* @return string
*/
this.someMethod = function() {
return this.some_var;
}
};
var MyClass = new Class({ extends: BaseClass }, function()
{
/**
* @param string value
*/
this.__construct = function(value)
{
this.some_var = value;
}
})
Utilisation de la bibliothèque légère (2k minifiée): https://github.com/haroldiedema/joii
Ceci est une extension (excusez le jeu de mots) de la solution elclanrs pour inclure des détails sur les méthodes d'instance, ainsi que pour adopter une approche extensible de cet aspect de la question; Je reconnais pleinement que cela a été mis en place grâce à "JavaScript: Le Guide définitif" de David Flanagan (partiellement adapté à ce contexte). Notez que ceci est clairement plus détaillé que d’autres solutions, mais qu’il en bénéficierait probablement à long terme.
Nous utilisons d’abord la simple fonction "extend" de David, qui copie les propriétés dans un objet spécifié:
function extend(o,p) {
for (var prop in p) {
o[prop] = p[prop];
}
return o;
}
Ensuite, nous implémentons son utilitaire de définition de sous-classe:
function defineSubclass(superclass, // Constructor of our superclass
constructor, // Constructor of our new subclass
methods, // Instance methods
statics) { // Class properties
// Set up the prototype object of the subclass
constructor.prototype = Object.create(superclass.prototype);
constructor.prototype.constructor = constructor;
if (methods) extend(constructor.prototype, methods);
if (statics) extend(constructor, statics);
return constructor;
}
Pour la dernière préparation, nous améliorons notre prototype Function avec le nouveau jiggery-pokery de David:
Function.prototype.extend = function(constructor, methods, statics) {
return defineSubclass(this, constructor, methods, statics);
};
Après avoir défini notre classe Monster, nous procédons comme suit (ce qui est réutilisable pour toutes les nouvelles classes que nous voulons étendre/hériter):
var Monkey = Monster.extend(
// constructor
function Monkey() {
this.bananaCount = 5;
Monster.apply(this, arguments); // Superclass()
},
// methods added to prototype
eatBanana: function() {
this.bananaCount--;
this.health++;
this.growl();
}
);
Je peux proposer une variante, il suffit de lire dans le livre, il semble le plus simple:
function Parent() {
this.name = 'default name';
};
function Child() {
this.address = '11 street';
};
Child.prototype = new Parent(); // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment
var a = new Child();
console.log(a.name); // "default name" trying to reach property of inherited class
Pour l'extension traditionnelle, vous pouvez simplement écrire superclass en tant que fonction constructeur, .__, puis appliquer ce constructeur à votre classe héritée.
function AbstractClass() {
this.superclass_method = function(message) {
// do something
};
}
function Child() {
AbstractClass.apply(this);
// Now Child will have superclass_method()
}
Exemple sur angularjs:
http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview
app.service('noisyThing',
['notify',function(notify){
this._constructor = function() {
this.scream = function(message) {
message = message + " by " + this.get_mouth();
notify(message);
console.log(message);
};
this.get_mouth = function(){
return 'abstract mouth';
}
}
}])
.service('cat',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.meow = function() {
this.scream('meooooow');
}
this.get_mouth = function(){
return 'fluffy mouth';
}
}])
.service('bird',
['noisyThing', function(noisyThing){
noisyThing._constructor.apply(this)
this.twit = function() {
this.scream('fuuuuuuck');
}
}])
Pour les autodidactes:
function BaseClass(toBePrivate){
var morePrivates;
this.isNotPrivate = 'I know';
// add your stuff
}
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// MiddleClass extends BaseClass
function MiddleClass(toBePrivate){
BaseClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// TopClass extends MiddleClass
function TopClass(toBePrivate){
MiddleClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
}
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// to be continued...
Créez une "instance" avec getter et setter:
function doNotExtendMe(toBePrivate){
var morePrivates;
return {
// add getters, setters and any stuff you want
}
}
Le problème de l’extension d’une fonction constructeur avec un prototype en Javascript peut être résolu de multiples façons. Laquelle de ces méthodes est la "meilleure" solution est basée sur l'opinion. Cependant, voici deux méthodes fréquemment utilisées pour étendre le prototype de fonction d'un constructeur.
class Monster {
constructor(health) {
this.health = health
}
growl () {
console.log("Grr!");
}
}
class Monkey extends Monster {
constructor (health) {
super(health) // call super to execute the constructor function of Monster
this.bananaCount = 5;
}
}
const monkey = new Monkey(50);
console.log(typeof Monster);
console.log(monkey);
L’approche ci-dessus consistant à utiliser les classes ES 2015
n’est rien de plus que du sucre syntaxique par rapport au modèle d’héritage prototype en javascript. Ici, dans le premier journal où nous évaluons typeof Monster
, nous pouvons observer qu’il s’agit d’une fonction. En effet, les classes ne sont que des fonctions de constructeur placées sous le capot. Néanmoins, vous pouvez aimer cette façon de mettre en œuvre un héritage prototype et l’apprendre définitivement. Il est utilisé dans les principaux cadres tels que ReactJS
et Angular2+
.
Object.create()
:function makeMonkey (bananaCount) {
// here we define the prototype
const Monster = {
health: 100,
growl: function() {
console.log("Grr!");}
}
const monkey = Object.create(Monster);
monkey.bananaCount = bananaCount;
return monkey;
}
const chimp = makeMonkey(30);
chimp.growl();
console.log(chimp.bananaCount);
Cette méthode utilise la méthode Object.create()
qui prend un objet qui sera le prototype du nouvel objet créé qu’elle renvoie. Par conséquent, nous créons d'abord l'objet prototype dans cette fonction, puis appelons Object.create()
qui renvoie un objet vide avec la propriété __proto__
définie sur l'objet Monster. Après cela, nous pouvons initialiser toutes les propriétés de l'objet. Dans cet exemple, nous affectons le compte banan à l'objet nouvellement créé.
la version absolument minimale (et correcte, contrairement à la plupart des réponses ci-dessus) est la suivante:
function Monkey(param){
this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }
C'est tout. Vous pouvez lire ici l'explication la plus longue