2013-03-04 20 views
51

JavaScript ho una classe di base:Estendere Classe

function Monster() { 
    this.health = 100; 
} 

Monster.prototype.growl = function() { 
    console.log("Grr!"); 
} 

Che voglio estendere e creare un'altra classe con:

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 
} 

Ho fatto un po 'di ricerca e di là sembra essere molte soluzioni contorte per fare ciò in JavaScript. Quale sarebbe il modo più semplice e affidabile per realizzare questo in JS?

+0

Eventuali duplicati di [? Come ereditare da una classe in javascript] (http://stackoverflow.com/ domande/2107556/how-to-inherit-from-a-class-in-javascript) –

risposta

110

Aggiornato sotto per ES6

marzo 2013 e ES5

Questo documento MDN descrive estendere classi ben:

https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript

In particolare, qui è ora di gestirlo:

// 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 

noti che Object.create() è supportato in alcuni browser meno recenti, tra cui IE8:

Object.create browser support

Se siete nella posizione di dover sostenere questi, il documento MDN collegato suggerisce di utilizzare un polyfill, o la seguente approssimazione:

function createObject(proto) { 
    function ctor() { } 
    ctor.prototype = proto; 
    return new ctor(); 
} 

usando questo come Student.prototype = createObject(Person.prototype) è preferibile utilizzare new Person() in quanto avoids calling the parent's constructor function quando ereditare il prototipo, e chiede solo il costruttore genitore quando il costruttore della erede viene chiamato.

maggio 2017 e ES6

Fortunatamente, i progettisti JavaScript hanno ascoltato le nostre richieste di aiuto e hanno adottato un modo più adatto di affrontare questo problema.

MDN ha un altro grande esempio sulla classe ES6 eredità, ma te lo mostrerò esattamente lo stesso insieme di classi come sopra riprodotto in 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 

pulito e comprensibile, proprio come tutti noi vogliamo. Tenete a mente, che mentre ES6 è abbastanza comune, è not supported everywhere:

ES6 browser support

+20

Wow, non che io abbia una soluzione migliore, ma questo incantesimo magico di difficile da capire mumbo-jumbo è terribile. – user949300

+3

Cosa succede se il costruttore di Persona ha bisogno di parametri, ad esempio Persona (nome) ...? –

+2

@PhamHuyAnh Basta fare qualcosa come 'function Person (n) {this.name = n; } ' –

9

Prova questa:

Function.prototype.extends = function(parent) { 
    this.prototype = Object.create(parent.prototype); 
}; 

Monkey.extends(Monster); 
function Monkey() { 
    Monster.apply(this, arguments); // call super 
} 

Edit: Ho messo un demo veloce qui http://jsbin.com/anekew/1/edit. Nota che extends è una parola riservata in JS e potresti ricevere degli avvertimenti quando lanci il tuo codice, puoi semplicemente chiamarlo inherits, questo è quello che faccio di solito.

Con questo helper in atto e utilizzando un oggetto props come unico parametro, eredità in JS diventa un po 'più semplice:

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" 
+0

Usando questo, 'Monkey' non erediterà le proprietà di' Monster' ('health'). Avrai anche * * ReferenceError: Monkey non è definita "* se' Monkey' non è definito prima di chiamare 'Monkey.extends (Monster)' – Phil

+0

Lo chiamerà "super". Controlla edit. – elclanrs

+0

@Phil, è una dichiarazione di funzione che è issata, dovrebbe funzionare. problema "che ottieni da questo codice è" extends è una parola riservata ", ma puoi facilmente cambiarlo con qualsiasi altro identificatore. – elclanrs

6

Se non vi piace l'approccio prototipo, perché in realtà non si comporta in un bel OOP a senso unico, si potrebbe provare questo:

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; 
    } 
}) 

Utilizzando biblioteca leggero (2k minified): https://github.com/haroldiedema/joii

+0

Grazie per il link a questa libreria !! Sembra incredibile: D Sembra che questo è quello che stavo cercando da così tanto tempo! <3 – x3ro

1

questo è un estensione (scusate il gioco di parole) della soluzione di elclanrs per includere dettagli sui metodi di istanza, oltre ad adottare un approccio estensibile a tale aspetto della domanda; Riconosco pienamente che questo è stato realizzato grazie a "JavaScript: The Definitive Guide" di David Flanagan (parzialmente adattato per questo contesto). Si noti che questo è chiaramente più prolisso di altre soluzioni, ma probabilmente trarrebbe vantaggio a lungo termine.

Per prima cosa utilizzare la funzione di David semplice "estendere", che copia proprietà ad un oggetto specificato:

function extend(o,p) { 
    for (var prop in p) { 
     o[prop] = p[prop]; 
    } 
    return o; 
} 

Poi abbiamo attuare il suo programma di utilità definizione di sottoclasse:

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; 
} 

Per l'ultima po 'di preparazione miglioriamo il nostro prototipo Function con il nuovo jiggery-pokery di David:

Function.prototype.extend = function(constructor, methods, statics) { 
    return defineSubclass(this, constructor, methods, statics); 
}; 

Dopo aver definito la nostra classe mostro, facciamo la seguente (che è riutilizzabile per eventuali nuove Classi vogliamo estendere/ereditare):

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(); 
    } 
); 
0

Per tradizione si estende si può semplicemente scrivere superclasse come funzione di costruzione, e quindi applica questo costruttore per la tua classe ereditata.

 function AbstractClass() { 
     this.superclass_method = function(message) { 
      // do something 
     }; 
    } 

    function Child() { 
     AbstractClass.apply(this); 
     // Now Child will have superclass_method() 
    } 

Esempio su 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'); 
    } 
    }]) 
9

ES6 ti dà ora la possibilità di utilizzare classe & estende parole chiave:

Poi, il codice sarà :

di avere una classe di base:

class Monster{ 
     constructor(){ 
      this.health = 100; 
     } 
     growl() { 
      console.log("Grr!"); 
     } 

} 

che si desidera estendere e creare un'altra classe con:

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 
     } 

} 
+2

Questo è molto più pulito, può confermare il funzionamento in chrome 51.0, Firefox 47. – Reahreic

+1

Usa i blocchi 'try {} catch (e) {}' per gestirlo, e informa l'utente finale di aggiornare il suo browser se è necessario . –

0

Per autodidatti:

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... 

Creare "istanza" con getter e setter:

function doNotExtendMe(toBePrivate){ 
    var morePrivates; 
    return { 
     // add getters, setters and any stuff you want 
    } 
} 
0

posso proporre una variante, appena hanno letto nel libro, sembra la più semplice:

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 
Problemi correlati