web-dev-qa-db-fra.com

Comment hériter d'une classe en javascript?

En PHP/Java on peut faire:

class Sub extends Base
{
}

Et automatiquement, toutes les méthodes, propriétés, champs, etc. publics/protégés de la classe Super deviennent une partie de la classe Sub qui peut être remplacée si nécessaire.

Quel est l'équivalent pour cela en Javascript?

94
Click Upvote

J'ai changé la façon dont je fais cela maintenant, j'essaie d'éviter d'utiliser des fonctions de constructeur et leur propriété prototype, mais mon ancienne réponse de 2010 est toujours au bas. Je préfère maintenant Object.create(). Object.create est disponible dans tous les navigateurs modernes.

Je devrais noter que Object.create est généralement beaucoup plus lent que d'utiliser new avec un constructeur de fonction.

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

L'un des gros avantages de l'utilisation d'Object.create est de pouvoir passer un argument defineProperties , qui vous donne un contrôle important sur la manière dont les propriétés de la classe peuvent être consultées et énumérées, et j'utilise également des fonctions pour créer des instances. , ils servent en quelque sorte de constructeurs, comme vous pouvez faire l’initialisation à la fin au lieu de simplement renvoyer l’instance.

var Base = {};

function createBase() {
  return Object.create(Base, {
    doSomething: {
       value: function () {
         console.log("Doing something");
       },
    },
  });
}

var Sub = createBase();

function createSub() {
  return Object.create(Sub, {
    doSomethingElse: {
      value: function () {
        console.log("Doing something else");
      },
    },
  }); 
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

Voici ma réponse originale de 2010:

function Base ( ) {
  this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
 console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"
78
Bjorn Tipling

En JavaScript, vous n'avez pas classes mais vous pouvez réutiliser l'héritage et le comportement de nombreuses façons:

Héritage pseudo-classique (via le prototypage):

function Super () {
  this.member1 = 'superMember1';
}
Super.prototype.member2 = 'superMember2';

function Sub() {
  this.member3 = 'subMember3';
  //...
}
Sub.prototype = new Super();

Devrait être utilisé avec l'opérateur new:

var subInstance = new Sub();

Application de fonction ou "chaînage de constructeurs":

function Super () {
  this.member1 = 'superMember1';
  this.member2 = 'superMember2';
}


function Sub() {
  Super.apply(this, arguments);
  this.member3 = 'subMember3';
}

Cette approche doit également être utilisée avec l'opérateur new:

var subInstance = new Sub();

La différence avec le premier exemple est que lorsque nous apply le constructeur Super à l'objet this à l'intérieur de Sub, il ajoute les propriétés affectées à this sur Super, directement sur la nouvelle instance, par exemple. subInstance contient les propriétés member1 et member2 directement (subInstance.hasOwnProperty('member1') == true;).

Dans le premier exemple, ces propriétés sont atteintes via la chaîne de prototypes, elles existent sur un objet [[Prototype]] interne.

Héritage parasite ou constructeurs de pouvoir:

function createSuper() {
  var obj = {
    member1: 'superMember1',
    member2: 'superMember2'
  };

  return obj;
}

function createSub() {
  var obj = createSuper();
  obj.member3 = 'subMember3';
  return obj;
}

Cette approche est basée essentiellement sur "l'augmentation d'objet", vous n'avez pas besoin d'utiliser l'opérateur new et, comme vous pouvez le constater, le mot clé this n'est pas impliqué.

var subInstance = createSub();

ECMAScript 5ème Ed. Object.create méthode:

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var superInstance = {
  member1: 'superMember1',
  member2: 'superMember2'
};

var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';

La méthode ci-dessus est une technique d'héritage prototypique proposée par Crockford .

Les instances d'objet héritent d'autres instances d'objet, c'est tout.

Cette technique peut être meilleure qu'une simple "augmentation d'objet" car les propriétés héritées ne sont pas copiées sur toutes les nouvelles instances d'objet, car l'objet base est défini comme étant le [[Prototype]] du étendu Dans l'exemple ci-dessus, subInstance ne contient physiquement que la propriété member3.

186
CMS

Pour ceux qui atteignent cette page en 2015 ou après

Avec la dernière version de la norme ECMAScript (ES6), vous pouvez utiliser de keywork extends

Notez que la définition de classe n'est pas une object régulière. Par conséquent, il n'y a pas de virgule entre les membres de la classe . Pour créer une instance d'une classe, vous devez utiliser le mot clé new. Pour hériter d'une classe de base, utilisez extends:

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
var myVehicle = new Vehicle('rocky');
myVehicle.getName(); // => 'rocky'

Pour hériter d'une classe de base, utilisez extends:

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

var myCar = new Car('bumpy');

myCar.getName(); // => 'bumpy'
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true

A partir de la classe dérivée, vous pouvez utiliser super à partir de n'importe quel constructeur ou méthode pour accéder à sa classe de base:

  • Pour appeler le constructeur parent, utilisez super().
  • Pour appeler un autre membre, utilisez, par exemple, super.getName().

Il y a plus à utiliser des classes. Si vous souhaitez approfondir le sujet, je vous recommande « Classes in ECMAScript 6 » du Dr. Axel Rauschmayer. *

la source

42
Merlin

Eh bien, dans JavaScript, il n'y a pas "d'héritage de classe", il y a juste un "héritage de prototype". Donc, vous ne fabriquez pas une classe "camion" et ne la marquez pas comme une sous-classe de "automobile". Au lieu de cela, vous créez un objet "Jack" et dites qu'il utilise "John" comme prototype. Si John sait combien "4 + 4" est, alors Jack le sait aussi. 

Je vous suggère de lire l'article de Douglas Crockford sur l'héritage prototypique ici: http://javascript.crockford.com/prototypal.html Il explique également comment vous pouvez faire en sorte que JavaScript possède un héritage "similaire" comme dans d'autres OO et explique ensuite que cela signifie en réalité que casser javaScript d’une manière qui n’était pas censée être utilisée.

7
naivists

Je trouve cette citation la plus éclairante:

En substance, un JavaScript "class" est simplement un objet Function servant de constructeur et d'objet prototype associé. ( Source: Guru Katz )

J'aime utiliser des constructeurs plutôt que des objets, je suis donc partisan de la méthode "d'héritage pseudo-classique" décrite ici par CMS . Voici un exemple d'héritage multiple avec une chaîne de prototypes:

// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
    this.isLifeform = true;
}

// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
    this.isAnimal = true;
}
Animal.prototype = new Lifeform();

// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
    this.isMammal = true;
}
Mammal.prototype = new Animal();

// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true

// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
    ,tiger.hasOwnProperty("isLifeform") // false
    ,tiger.hasOwnProperty("isAnimal")   // false
    ,tiger.hasOwnProperty("isMammal")   // false
    ,tiger.hasOwnProperty("isCat")      // true
);

// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A    = 1;
Animal.prototype.B      = 2;
Mammal.prototype.C      = 3;
Cat.prototype.D         = 4;

console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4

// Look at the instance object again
console.log(tiger);
// You'll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) );  // Mammal 
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform

Voici une autre bonne ressource de MDN et voici un jsfiddle pour que vous puissiez l’essayer .

6
Luke

L'héritage Javascript est un peu différent de Java et de PHP, car il n'a pas vraiment de classes. Au lieu de cela, il a des objets prototypes qui fournissent des méthodes et des variables membres. Vous pouvez chaîner ces prototypes pour fournir un héritage d'objet. Le motif le plus courant trouvé lors de la recherche sur cette question est décrit dans le Mozilla Developer Network . J'ai mis à jour leur exemple pour inclure un appel à une méthode de superclasse et afficher le journal dans un message d'alerte:

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);

Personnellement, je trouve que l'héritage en Javascript est gênant, mais c'est la meilleure version que j'ai trouvée.

4
Don Kirkby

vous ne pouvez pas (au sens classique). Javascript est un langage prototypique. Vous remarquerez que vous ne déclarez jamais une "classe" en Javascript; vous définissez simplement l'état et les méthodes d'un objet. Pour produire un héritage, vous prenez un objet et le prototypez. Le prototype est étendu avec de nouvelles fonctionnalités. 

3
D.C.

Après avoir lu de nombreux articles, j’ai proposé cette solution ( jsfiddle here ). La plupart du temps, je n'ai pas besoin de quelque chose de plus sophistiqué

var Class = function(definition) {
    var base = definition.extend || null;
    var construct = definition.construct || definition.extend || function() {};

    var newClass = function() { 
        this._base_ = base;        
        construct.apply(this, arguments);
    }

    if (definition.name) 
        newClass._name_ = definition.name;

    if (definition.extend) {
        var f = function() {}       
        f.prototype = definition.extend.prototype;      
        newClass.prototype = new f();   
        newClass.prototype.constructor = newClass;
        newClass._extend_ = definition.extend;      
        newClass._base_ = definition.extend.prototype;         
    }

    if (definition.statics) 
        for (var n in definition.statics) newClass[n] = definition.statics[n];          

    if (definition.members) 
        for (var n in definition.members) newClass.prototype[n] = definition.members[n];    

    return newClass;
}


var Animal = Class({

    construct: function() {        
    },

    members: {

        speak: function() {
            console.log("nuf said");                        
        },

        isA: function() {        
            return "animal";           
        }        
    }
});


var Dog = Class({  extend: Animal,

    construct: function(name) {  
        this._base_();        
        this.name = name;
    },

    statics: {
        Home: "House",
        Food: "Meat",
        Speak: "Barks"
    },

    members: {
        name: "",

        speak: function() {
            console.log( "ouaf !");         
        },

        isA: function(advice) {
           return advice + " dog -> " + Dog._base_.isA.call(this);           
        }        
    }
});


var Yorkshire = Class({ extend: Dog,

    construct: function(name,gender) {
        this._base_(name);      
        this.gender = gender;
    },

    members: {
        speak: function() {
            console.log( "ouin !");           
        },

        isA: function(advice) {         
           return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);       
        }        
    }
});


var Bulldog = function() { return _class_ = Class({ extend: Dog,

    construct: function(name) {
        this._base_(name);      
    },

    members: {
        speak: function() {
            console.log( "OUAF !");           
        },

        isA: function(advice) {         
           return "bulldog -> " + _class_._base_.isA.call(this,advice);       
        }        
    }
})}();


var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();

var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();

var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();

var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("Nice"));
bulldog.speak();
1
chauwel
function Base() {
    this.doSomething = function () {
    }
}

function Sub() {
    Base.call(this); // inherit Base's method(s) to this instance of Sub
}

var sub = new Sub();
sub.doSomething();
1
Kai Hartmann

Vous pouvez utiliser .inheritWith et .fastClasslibrary . Il est plus rapide que la plupart des bibliothèques populaires et parfois même plus vite que la version native.

Très facile à utiliser:

function Super() {
   this.member1 = "superMember";//instance member
}.define({ //define methods on Super's prototype
   method1: function() { console.log('super'); } //prototype member
}.defineStatic({ //define static methods directly on Super function 
   staticMethod1: function() { console.log('static method on Super'); }
});

var Sub = Super.inheritWith(function(base, baseCtor) {
   return {
      constructor: function() {//the Sub constructor that will be returned to variable Sub
         this.member3 = 'subMember3'; //instance member on Sub
         baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
      },
      method1: function() { 
         console.log('sub'); 
         base.method1.apply(this, arguments); //call the base class' method1 function
      }
}

Usage

var s = new Sub();
s.method1(); //prints:
//sub 
//super
1
Adaptabi
function Person(attr){
  this.name = (attr && attr.name)? attr.name : undefined;
  this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;

  this.printName = function(){
    console.log(this.name);
  }
  this.printBirthYear = function(){
    console.log(this.birthYear);
  }
  this.print = function(){
    console.log(this.name + '(' +this.birthYear+ ')');
  }
}

function PersonExt(attr){
  Person.call(this, attr);

  this.print = function(){
    console.log(this.name+ '-' + this.birthYear);
  }
  this.newPrint = function(){
    console.log('New method');
  }
}
PersonExt.prototype = new Person();

// Init object and call methods
var p = new Person({name: 'Mr. A', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A

var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A
1
nnattawat

Depuis ES2015, c'est exactement comme ça que vous faites l'héritage en JavaScript

class Sub extends Base {

}
  1. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes
  2. http://exploringjs.com/es6/ch_classes.html
1
Boopathi Rajaa

Grâce à la réponse de CMS et après quelques manipulations avec prototype et Object.create et tout le reste, j'ai pu trouver une solution intéressante pour mon héritage en utilisant apply comme indiqué ci-dessous:

var myNamespace = myNamespace || (function() {
    return {

        BaseClass: function(){
            this.someBaseProperty = "someBaseProperty";
            this.someProperty = "BaseClass";
            this.someFunc = null;
        },

        DerivedClass:function(someFunc){
            myNamespace.BaseClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "DerivedClass";
        },

        MoreDerivedClass:function(someFunc){
            myNamespace.DerivedClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "MoreDerivedClass";
        }
    };
})();
1
pasx

Vous ne pouvez pas hériter d'une classe en JavaScript, car il n'y en a pas.

0
Jörg W Mittag