JavaScript-Überschreibungsmethoden

84

Angenommen, Sie haben den folgenden Code:

function A() {
    function modify() {
       x = 300;
       y = 400;
    }

    var c = new C();
}

function B() {
    function modify(){
       x = 3000;
       y = 4000;
    }

    var c = new C();
}

C = function () {
   var x = 10;
   var y = 20;

   function modify() {
      x = 30;
      y = 40;
   };

   modify();
   alert("The sum is: " + (x+y));
}

Nun ist die Frage, ob es eine Möglichkeit ist , in dem ich die Methode außer Kraft setzen kann modifyvon Cden Methoden, die in Aund B. In Java würden Sie das superSchlüsselwort -key verwenden, aber wie können Sie so etwas in JavaScript erreichen?

Daniel Nastase
quelle
6
modifyist keine Methode, sondern eine verschachtelte Funktion - es gibt einen Unterschied zwischen diesen beiden ...
Šime Vidas
2
In Java verwenden Sie das superSchlüsselwort, um auf die nicht privaten Felder und Methoden einer Superklasse zuzugreifen . Sie verwenden es nicht, um sie zu überschreiben.
FK82

Antworten:

137

Bearbeiten: Es ist nun sechs Jahre her, dass die ursprüngliche Antwort geschrieben wurde und sich viel geändert hat!

  • Wenn Sie eine neuere Version von JavaScript verwenden, die möglicherweise mit einem Tool wie Babel kompiliert wurde , können Sie echte Klassen verwenden .
  • Wenn Sie die von Angular oder React bereitgestellten klassenähnlichen Komponentenkonstruktoren verwenden , sollten Sie in den Dokumenten nach diesem Framework suchen.
  • Wenn Sie ES5 verwenden und mithilfe von Prototypen "gefälschte" Klassen von Hand erstellen, ist die Antwort unten immer noch so richtig wie nie zuvor.

Viel Glück!


Die JavaScript-Vererbung sieht etwas anders aus als Java. So sieht das native JavaScript-Objektsystem aus:

// Create a class
function Vehicle(color){
  this.color = color;
}

// Add an instance method
Vehicle.prototype.go = function(){
  return "Underway in " + this.color;
}

// Add a second class
function Car(color){
  this.color = color;
}

// And declare it is a subclass of the first
Car.prototype = new Vehicle();

// Override the instance method
Car.prototype.go = function(){
  return Vehicle.prototype.go.call(this) + " car"
}

// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"

var c = new Car("red");
c.go() // "Underway in red car"

Leider ist dies ein bisschen hässlich und beinhaltet keinen sehr guten Weg zu "super": Sie müssen manuell angeben, welche Methode der übergeordneten Klassen Sie aufrufen möchten. Infolgedessen gibt es eine Vielzahl von Werkzeugen, um das Erstellen von Klassen zu vereinfachen. Schauen Sie sich Prototype.js, Backbone.js oder eine ähnliche Bibliothek an, die eine schönere Syntax für OOP in js enthält.

Adam
quelle
2
Alternativ zur Verwendung eines Tools, um das Erstellen von Klassen zu vereinfachen, konnten Sie überhaupt keine Klassen erstellen. Die klassische OO-Emulation in js wird immer chaotisch.
Raynos
1
(kein konstruktiver Kommentar) Die "Low-Level" -Sprache in der Browser-Welt zu sein, ist ohne Grund sehr hässlich. Ich lerne es immer noch, danke!
dnuske
9
Ich glaube anstelle von Car.prototype = new Vehicle (); Dies sollte Car.prototype = Object.create (Vehicle.prototype) sein. Nein?
Jordanien
@ Martin ist richtig, siehe Javascript-Vererbung
Matoni
Einer der Gründe, warum Car.prototype = new Vehicle (); ist falsch, weil nichts an Vehicle () übergeben werden muss, während es eine Farbe erhält.
Tushar Arora
58

Da dies ein Top-Hit bei Google ist, möchte ich eine aktualisierte Antwort geben.

Die Verwendung von ES6-Klassen erleichtert das Überschreiben von Vererbung und Methoden erheblich:

'use strict';

class A {
    speak() {
        console.log("I'm A");
    }
}

class B extends A {
    speak() {
        super.speak();

        console.log("I'm B");
    }
}

var a = new A();
a.speak();
// Output:
// I'm A

var b = new B();
b.speak();
// Output:
// I'm A
// I'm B

Das superSchlüsselwort bezieht sich auf die übergeordnete Klasse, wenn es in der ererbenden Klasse verwendet wird. Außerdem sind alle Methoden in der übergeordneten Klasse an die Instanz des untergeordneten Elements gebunden, sodass Sie nicht schreiben müssen super.method.apply(this);.

In Bezug auf die Kompatibilität: In der ES6-Kompatibilitätstabelle werden (meistens) nur die neuesten Versionen der Support-Klassen der Hauptakteure angezeigt. V8-Browser haben sie seit Januar dieses Jahres (Chrome und Opera), und Firefox, das die SpiderMonkey JS-Engine verwendet, wird nächsten Monat Klassen mit ihrer offiziellen Firefox 45-Version sehen. Auf der mobilen Seite unterstützt Android diese Funktion immer noch nicht, während iOS 9, das vor fünf Monaten veröffentlicht wurde, teilweise unterstützt wird.

Glücklicherweise gibt es Babel , eine JS-Bibliothek zum erneuten Kompilieren von Harmony-Code in ES5-Code. Klassen und viele andere coole Funktionen in ES6 können Ihren Javascript-Code viel lesbarer und wartbarer machen.

Yotam Ofek
quelle
9
Diese Antwort verdient mehr Anerkennung und ist derzeit die richtige Antwort.
Klompenrunner
6

Einmal sollte es vermieden werden, klassisches OO zu emulieren, und stattdessen prototypisches OO verwenden. Eine schöne Utility-Bibliothek für prototypische OO sind Merkmale .

Anstatt Methoden zu überschreiben und Vererbungsketten einzurichten (man sollte immer die Objektzusammensetzung der Objektvererbung vorziehen), sollten Sie wiederverwendbare Funktionen in Merkmalen bündeln und damit Objekte erstellen.

Live-Beispiel

var modifyA = {
    modify: function() {
        this.x = 300;
        this.y = 400;
    }
};

var modifyB = {
    modify: function() {
        this.x = 3000;
        this.y = 4000;
    }
};

C = function(trait) {
    var o = Object.create(Object.prototype, Trait(trait));

    o.modify();
    console.log("sum : " + (o.x + o.y));

    return o;
}

//C(modifyA);
C(modifyB);
Raynos
quelle
10
Sie beantworten die Frage nicht. Dies sollte ein Kommentar sein, wenn überhaupt.
FK82
@ FK82 lassen Sie uns diese Diskussion im Chat fortsetzen
Raynos
3

modify () ist in Ihrem Beispiel eine private Funktion, auf die nur innerhalb Ihrer A-, B- oder C-Definition zugegriffen werden kann. Sie müssten es als deklarieren

this.modify = function(){}

C hat keinen Verweis auf seine Eltern, es sei denn, Sie übergeben es an C. Wenn C so eingerichtet ist, dass es von A oder B erbt, erbt es seine öffentlichen Methoden (nicht seine privaten Funktionen, wie Sie sie modifiziert haben ()). Sobald C Methoden von seinem übergeordneten Element erbt, können Sie die geerbten Methoden überschreiben.

Alex Heyd
quelle
Ändern ist eine lokale Funktion. Es gibt kein privates in Javascript
Raynos
lokal / privat, ist das nicht dasselbe, nur ein anderer Begriff?
Alex Heyd
2

Die Methode modify(), die Sie zuletzt aufgerufen haben, wird im globalen Kontext aufgerufen, wenn Sie überschreiben möchten, müssen modify()Sie zuerst Aoder erben B.

Vielleicht versuchen Sie das:

In diesem Fall CerbtA

function A() {
    this.modify = function() {
        alert("in A");
    }
}

function B() {
    this.modify = function() {
        alert("in B");
    }
}

C = function() {
    this.modify = function() {
        alert("in C");
    };

    C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}

C.prototype = new A();
Lovesh
quelle
1
C.prototype.modify()hätte den falschen thisWert. Das heißt , C.prototypestatt der Instanz von c. Bitte verwenden Sie, .call(this)aber dann ist Ihre Antwort nur ein Duplikat :)
Raynos
1

Nicht, wenn Sie nicht alle Variablen "öffentlich" machen, dh sie Functionentweder direkt oder über die prototypeEigenschaft zu Mitgliedern der machen .

var C = function( ) {
    this.x = 10 , this.y = 20 ;
    this.modify = function( ) {
        this.x = 30 , this.y = 40 ;
        console.log("(!) C >> " + (this.x + this.y) ) ;
    } ;
} ;

var A = function( ) {
    this.modify = function( ) {
       this.x = 300 , this.y = 400 ;
       console.log("(!) A >> " + (this.x + this.y) ) ;
    } ;
} ;
    A.prototype = new C ;

var B = function( ) {
    this.modify = function( ) {
       this.x = 3000 , this.y = 4000 ;
       console.log("(!) B >> " + (this.x + this.y) ) ;
    } ;
} ;


new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ; 

Sie werden einige Änderungen bemerken.

Am wichtigsten ist, dass der Aufruf des vermeintlichen Konstruktors "Superklassen" jetzt in dieser Zeile impliziert ist:

<name>.prototype = new C ;

Beide Aund Bwerden nun individuell veränderbare Mitglieder haben xund ywas nicht der Fall wäre, wenn wir ... = Cstattdessen geschrieben hätten .

Dann x, yund modifyalle „öffentlichen“ Mitglieder , so dass eine andere Zuordnung von Functionihnen

 <name>.prototype.modify = function( ) { /* ... */ }

überschreibt das Original Functionmit diesem Namen.

Schließlich kann der Aufruf von modifyin der FunctionDeklaration nicht ausgeführt werden, da der implizite Aufruf der "Superklasse" dann erneut ausgeführt wird, wenn wir die vermeintliche "Oberklasse" auf die prototypeEigenschaft der angeblichen "Unterklassen" setzen.

Aber so würden Sie mehr oder weniger so etwas in JavaScript machen.

HTH,

FK

FK82
quelle
Es hat <name>.prototype = new C;sowieso keinen Sinn , alle Mitglieder des Prototyps zu beschatten
Raynos,
@ Raynos: Ja, das gibt es. Wenn Sie kein Objekt instanziieren Objects, teilen sich alle Erben dasselbe Mitglied . Also ändernCCx in Awürde sich ändern xin Cund damit ändern xin B. Welches ist offensichtlich unerwünscht.
FK82
Du hast den Punkt verpasst. Sie können die Zeile entfernen und der Code würde immer noch funktionieren
Raynos
@ Raynos: Du vermisst leider deinen eigenen Punkt. ;-) Wir wollen AundB erben von C. Wenn diese Zeile fehlen würde, wäre dies nicht der Fall. Tatsächlich wäre in diesem Fall der einzige Prototyp, auf den sowohl "Schatten" Aals Bauch "Schatten" Zugriff hätten Object.prototype.
FK82
Schau dir den Code an. A und B verwenden keines der Mitglieder von C im Prototyp. "Erben" von C ist also nutzlos. Dies liegt daran , A und B neu zu definieren x, yund modifyund damit alle Schatten C‚s Mitglieder. Was bringt es, C in den Prototyp einzufügen, wenn Sie es nicht verwenden? Es ist toter Code.
Raynos
0

function A() {
    var c = new C();
	c.modify = function(){
		c.x = 123;
		c.y = 333;
	}
	c.sum();
}

function B() {
    var c = new C();
	c.modify = function(){
		c.x = 999;
		c.y = 333;
	}
	c.sum();
}


C = function () {
   this.x = 10;
   this.y = 20;

   this.modify = function() {
      this.x = 30;
      this.y = 40;
   };
   
   this.sum = function(){
	this.modify();
	console.log("The sum is: " + (this.x+this.y));
   }
}

A();
B();

nghien_net 89
quelle