2013-03-04 26 views
51

Mam klasy bazowej:JavaScript Rozszerzanie klasy

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

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

że chcę przedłużyć i stworzyć kolejną klasę z:

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 
} 

Zrobiłem sporo badań i tam wydaje się być wieloma skomplikowanymi rozwiązaniami do tego celu w JavaScript. Jaki byłby najprostszy i najbardziej niezawodny sposób osiągnięcia tego w JS?

+0

Możliwy duplikat [? Jak dziedziczą z klasy w javascript] (http://stackoverflow.com/ pytania/2107556/how-to-inherit-from-a-class-in-javascript) –

Odpowiedz

110

Updated poniżej ES6

marca 2013 i ES5

Niniejszy dokument MDN opisuje rozszerzenie klasy także:

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

W szczególności, jest tu teraz poradzić:

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

Zauważ, że Object.create() jest obsługiwana w niektórych starszych przeglądarkach, w tym IE8:

Object.create browser support

Jeśli jesteś w sytuacji konieczności wspierania tych, powiązany dokument MDN sugeruje używając PolyFill lub następujące przybliżenie:

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

używając tego jak Student.prototype = createObject(Person.prototype) jest korzystniejsze new Person() tym, że avoids calling the parent's constructor function gdy dziedziczenie prototypu, a jedynie wywołuje konstruktor rodzic gdy konstruktor jest spadkobiercą jest wywoływany.

maja 2017 i ES6

szczęście projektanci JavaScript słyszeli nasze prośby o pomoc i przyjęły bardziej odpowiedni sposób podejścia do tej kwestii.

MDN ma kolejny świetny przykład na klasy ES6 dziedziczenia, ale pokażę dokładnie ten sam zestaw klas wyżej zamieszczonym w 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 

Czyste i zrozumiałe, podobnie jak wszyscy chcemy. Należy pamiętać, że podczas ES6 jest dość powszechne, to not supported everywhere:

ES6 browser support

+20

Wow, nie, że mam lepsze rozwiązanie, ale ta magiczna inkantacja trudnego do zrozumienia mumbo-jumbo jest okropna. – user949300

+3

Co jeśli konstruktor Person potrzebuje params, np. Person (name) ...? –

+2

@PhamHuyAnh Po prostu zrób coś w stylu 'function Person (n) {this.name = n; } ' –

9

Spróbuj tego:

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

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

Edit: kładę szybki demo tutaj http://jsbin.com/anekew/1/edit. Zauważ, że extends jest słowem zastrzeżonym w JS i możesz otrzymać ostrzeżenia podczas linkowania kodu, możesz to nazwać po prostu inherits, to zwykle robię.

Z tego pomocnika w miejscu i przy użyciu obiektu props jak tylko parametr, dziedziczenie w JS staje się nieco prostsze:

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

Używając tego, 'Monkey' nie dziedziczy właściwości' Monster'ów ('health') .Zostanie również otrzymane *" ReferenceError: Monkey nie jest zdefiniowane "* jeśli' Monkey' nie jest zdefiniowany przed wywołaniem 'Monkey.extends (Monster)' – Phil

+0

To będzie, jeśli zadzwonisz "super" Sprawdź edytuj – elclanrs

+0

@Phil, to deklaracja funkcji, którą podniósł, to powinno działać. " problem "który dostaniesz z tego kodu jest" rozszerza się jest zarezerwowanym słowem ", ale możesz łatwo zmienić go na dowolny inny identyfikator – elclanrs

6

Jeśli nie podoba mi się podejście prototyp, ponieważ tak naprawdę nie zachowują w ładnym OOP-sposób, można spróbować to:

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

Korzystanie lekka biblioteka (2k minified): https://github.com/haroldiedema/joii

+0

Dziękuję za link do tej biblioteki! Wygląda niesamowicie: D Wydaje się, że tak długo tego szukałem! <3 – x3ro

1

jest to rozszerzenie (usprawiedliwienie kalamburu) rozwiązania elkanerów w celu uwzględnienia szczegółów na temat metod instancji, a także przyjęcie rozszerzalnego podejścia do tego aspektu pytania; W pełni zgadzam się, że jest to połączone dzięki "JavaScript: ostateczny przewodnik" Davida Flanagana (częściowo dostosowane do tego kontekstu). Należy zauważyć, że jest to wyraźnie bardziej szczegółowe niż inne rozwiązania, ale prawdopodobnie przyniesie korzyści w dłuższej perspektywie.

Najpierw używamy prostą funkcję Dawida „Rozszerz”, który kopiuje właściwości do określonego obiektu:

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

Następnie wdrożymy jego użyteczność definicji podklasy:

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

dla ostatniego bitu przygotowania Udoskonalamy nasz prototyp Function z nowym jiggery-pokerem Davida:

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

Po zdefiniowaniu naszą klasę potwór, mamy następujące (który jest ponownie wykorzystywane do wszelkich nowych klas chcemy przedłużyć/Dziedzicz):

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

dla tradycyjnych rozciągającej można po prostu napisać superklasę jako konstruktor, i następnie zastosuj ten konstruktor dla swojej odziedziczonej klasy.

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

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

Przykład na 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 daje teraz możliwość korzystania klasa & rozciąga słowa kluczowe:

Następnie Twój kod będzie :

Masz klasę bazową:

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

} 

że chcesz rozszerzyć i utworzyć inną klasę z:

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

To jest o wiele czystsze, może potwierdzić pracę w chrome 51.0, Firefox 47. – Reahreic

+1

Użyj bloków 'try {} catch (e) {}', aby zarządzać tym, i powiedz użytkownikowi końcowemu, aby zaktualizował swoją przeglądarkę, jeśli jest to wymagane . –

0

Dla samouków:

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

Create "wystąpienie" z getter i seter:

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

mogę zaproponować jeden wariant, po prostu przeczytać w książce, wydaje się najprostsze:

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