Jak dziedziczyć po klasie w javascript?

W PHP / Javie można zrobić:

class Sub extends Base
{
}

I automatycznie wszystkie publiczne / chronione metody, właściwości, pola itp. Super klasy stają się częścią pod klasy, która może zostać nadpisana w razie potrzeby.

Jaki jest odpowiednik tego w Javascript?

Author: nickel715, 2010-01-21

15 answers

Zmieniłem sposób, w jaki to robię, staram się unikać używania funkcji konstruktora i ich właściwości prototype, ale moja stara odpowiedź z 2010 roku wciąż jest na dole. Teraz wolę Object.create(). Object.create jest dostępny we wszystkich nowoczesnych przeglądarkach.

Powinienem zauważyć, że Object.create jest zwykle znacznie wolniejsze niż używanie new z konstruktorem funkcji.

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

Jedna z wielkich zalet używania obiektu.Tworzenie jest w stanie przekazać w defineProperties argument, który daje Ci znaczącą kontrolę nad tym, jak właściwości na klasie mogą być dostępne i wyliczane, a także używam funkcji do tworzenia instancji, które służą jako konstruktory w pewien sposób, ponieważ możesz zrobić inicjalizację na końcu zamiast zwracać instancję.

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

Oto moja oryginalna odpowiedź z 2010 roku:

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"
 77
Author: Bjorn Tipling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-04 20:49:31

W JavaScript nie masz klas, ale możesz uzyskać dziedziczenie i zachowanie na wiele sposobów:

Dziedziczenie Pseudoklasyczne (poprzez prototypowanie):

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

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

Należy używać z operatorem new:

var subInstance = new Sub();

Aplikacja funkcji lub "constructor chaining":

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


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

Podejście to powinno być również stosowane z operatorem new:

var subInstance = new Sub();

Różnica w pierwszym przykładzie jest taka, że gdy Super konstruktor do this obiektu wewnątrz Sub, dodaje właściwości przypisane do this na Super, bezpośrednio na nowej instancji, np. subInstance zawiera właściwości member1 i member2 bezpośrednio (subInstance.hasOwnProperty('member1') == true;).

W pierwszym przykładzie te właściwości są osiągane przez łańcuch prototypów , istnieją one na wewnętrznym obiekcie [[Prototype]].

Dziedziczenie pasożytnicze lub konstruktory mocy:

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

  return obj;
}

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

To podejście opiera się w zasadzie na " obiekt augmenting", nie musisz używać operatora new, a jak widzisz, słowo kluczowe this nie jest zaangażowane.

var subInstance = createSub();

ECMAScript 5th Ed. Object.create metoda:

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

Powyższa metoda jest prototypową techniką dziedziczenia zaproponowaną przez Crockforda .

Instancje obiektów dziedziczą od innych instancji obiektów, to wszystko.

Ta technika może być lepsza niż zwykła "augmentacja obiektów", ponieważ odziedziczone właściwości nie są kopiowane na wszystkie nowe instancje obiektu, ponieważ baza obiekt jest ustawiony jako [[Prototype]] z rozszerzonego obiektu, w powyższym przykładzie subInstance zawiera fizycznie tylko właściwość member3.

 185
Author: CMS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 15:09:00

Dla tych, którzy dotrą do tej strony w 2015 roku lub później

Z najnowszą wersją standardu ECMAScript (ES6), możesz użyć słowa kluczowego de extends

Zauważ, że definicja klasy nie jest regularna object stąd nie ma przecinków między członkami klasy. Aby utworzyć instancję klasy, musisz użyć słowa kluczowego new. Aby dziedziczyć z klasy bazowej, użyj 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'

Aby dziedziczyć z klasy bazowej, użyj 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

Z klasy pochodnej, możesz użyć super z dowolnego konstruktora lub metody, aby uzyskać dostęp do jego podstawowej klasy: {]}

  • aby wywołać konstruktor nadrzędny, użyj super().
  • aby wywołać innego członka, Użyj na przykład super.getName().

Używanie klas to coś więcej. Jeśli chcesz zagłębić się w ten temat, polecam "klasy w ECMAScript 6" Dr. Axela Rauschmayera.*

Źródło

 41
Author: Merlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-13 22:14:02

Cóż, w JavaScript nie ma "dziedziczenia klas", jest tylko "dziedziczenie prototypu". Więc nie tworzysz klasy "ciężarówka", a następnie oznaczasz ją jako podklasę "samochód". Zamiast tego tworzysz obiekt "Jack" i mówisz, że używa on" John " jako prototypu. Jeśli John wie, ile to jest "4+4", to Jack też to wie.

Proponuję przeczytać artykuł Douglasa Crockforda o dziedziczeniu prototypów tutaj: http://javascript.crockford.com/prototypal.html on również pokazuje, jak można sprawić, by JavaScript miał" podobne " dziedziczenie, jak w innych językach OO, a następnie wyjaśnia, że w rzeczywistości oznacza to łamanie javaScript w sposób, w jaki nie miał być używany.

 7
Author: naivists,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 07:45:32

Uważam ten cytat za najbardziej pouczający:

W istocie JavaScript "klasa" jest tylko obiektem funkcyjnym, który służy jako konstruktor plus dołączony obiekt prototypowy. (Źródło: Guru Katz )

Lubię używać konstruktorów zamiast obiektów, więc jestem częściowy dla metody " pseudoklasycznego dziedziczenia "opisanej tutaj przez CMS . Oto przykład wielokrotnego dziedziczenia z prototypem łańcuch :

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

Oto kolejny dobry zasób z MDN , a oto jsfiddle, abyś mógł go wypróbować.

 6
Author: Luke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:28

Dziedziczenie Javascript różni się nieco od Javy i PHP, ponieważ tak naprawdę nie ma klas. Zamiast tego posiada prototypowe obiekty, które dostarczają metody i zmienne członkowskie. Prototypy można łączyć w łańcuchy, aby zapewnić dziedziczenie obiektów. Najczęstszy wzorzec, który znalazłem podczas badania tego pytania, jest opisany w Mozilla Developer Network . Zaktualizowałem ich przykład, aby zawierał wywołanie metody superclass i wyświetlanie alertu log in wiadomość:

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

Osobiście uważam, że dziedziczenie w Javascript jest niezręczne, ale jest to najlepsza wersja, jaką znalazłem.

 4
Author: Don Kirkby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-06 23:01:24

Nie możesz (w klasycznym sensie). Javascript jest językiem prototypowym. Zauważysz, że nigdy nie deklarujesz "klasy" w Javascript; jedynie definiujesz stan i metody obiektu. Aby stworzyć dziedziczenie, bierzesz jakiś obiekt I go prototypujesz. Prototyp został rozszerzony o nowe funkcjonalności.

 3
Author: D.C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 07:34:32

Możesz użyć .inheritWith i .fastClass biblioteka . Jest szybszy od większości popularnych bibliotek, a czasami nawet szybszy od wersji natywnej.

Bardzo łatwy w użyciu:

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

Użycie

var s = new Sub();
s.method1(); //prints:
//sub 
//super
 1
Author: Adaptabi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-18 07:47:29
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
Author: nnattawat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-28 11:36:47

Po przeczytaniu wielu postów, wymyśliłem takie rozwiązanie (jsfiddle tutaj ). Przez większość czasu nie potrzebuję czegoś bardziej wyrafinowanego

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
Author: chauwel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-11 19:29:11

Dzięki odpowiedzi CMS ' a i po chwilowym zademonstrowaniu prototypu i obiektu.Utwórz i co nie, byłem w stanie wymyślić schludne rozwiązanie dla mojego dziedziczenia za pomocą zastosuj, jak pokazano tutaj:

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
Author: pasx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-29 06:13:14

Od ES2015, dokładnie tak się robi dziedziczenie w 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
Author: Boopathi Rajaa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-22 10:32:46
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
Author: Kai Hartmann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-04 14:46:29

Nie można dziedziczyć po klasie w JavaScript, ponieważ nie ma klas w JavaScript.

 0
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-21 07:41:03

Klasy ES6:

Javascript nie ma klas. Klasy w javascript są to tylko cukier składniowy zbudowany na podstawie wzorca prototypowego dziedziczenia , który javascript. Możesz użyć JS class, aby wymusić dziedziczenie prototypów, ale ważne jest, aby zdać sobie sprawę, że nadal używasz funkcji konstruktora pod maską.

Te pojęcia mają zastosowanie również w przypadku rozszerzenia z es6 'klasy' za pomocą słowa kluczowego extends. To tylko tworzy dodatkowe ogniwo w łańcuchu prototypów. __proto__

Przykład:

class Animal {
  makeSound () {
    console.log('animalSound');
  }
}

class Dog extends Animal {
   makeSound () {
    console.log('Woof');
  }
}


console.log(typeof Dog)  // classes in JS are just constructor functions under the hood

const dog = new Dog();

console.log(dog.__proto__ === Dog.prototype);   
// First link in the prototype chain is Dog.prototype

console.log(dog.__proto__.__proto__ === Animal.prototype);  
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal

Obiekt.create ()

Object.create() jest również sposobem tworzenia dziedziczenia w JS w javascript. {[5] } jest funkcją, która tworzy nowy obiekt, an przyjmuje istniejący obiekt jako argument. Przypisze obiekt, który został odebrany jako argument do właściwości __proto__ nowo utworzonego obiektu. Ponownie ważne jest, aby uświadomić sobie, że jesteśmy związani z prototypowym paradygmatem dziedziczenia, który Js ucieleśnia.

Przykład:

const Dog = {
  fluffy: true,
  bark: () => {
      console.log('woof im a relatively cute dog or something else??');
  }
};

const dog = Object.create(Dog);

dog.bark();
 0
Author: Willem van der Veen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-28 22:45:57