Métodos de anulación de JavaScript

87

Digamos que tiene el siguiente código:

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

Ahora la pregunta es, si hay alguna manera en la que puedo reemplazar el método modifyde Clos métodos que se encuentran en Ay B. En Java, usaría la superpalabra clave, pero ¿cómo puede lograr algo como esto en JavaScript?

Daniel Nastase
fuente
6
modifyno es un método, sino una función anidada; hay una diferencia entre esos dos ...
Šime Vidas
2
En Java, usa la superpalabra clave para acceder a los campos y métodos no privados de una superclase. No lo usa para anularlos.
FK82

Respuestas:

138

Editar: ¡Han pasado seis años desde que se escribió la respuesta original y muchas cosas han cambiado!

  • Si está usando una versión más reciente de JavaScript, posiblemente compilada con una herramienta como Babel , puede usar clases reales .
  • Si está utilizando los constructores de componentes similares a clases proporcionados por Angular o React , querrá buscar en los documentos ese marco.
  • Si está usando ES5 y está haciendo clases "falsas" a mano usando prototipos, la respuesta a continuación sigue siendo tan correcta como siempre.

¡Buena suerte!


La herencia de JavaScript se ve un poco diferente a la de Java. Así es como se ve el sistema de objetos de JavaScript nativo:

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

Desafortunadamente, esto es un poco feo y no incluye una forma muy agradable de "super": tienes que especificar manualmente el método de las clases principales que quieres llamar. Como resultado, hay una variedad de herramientas para hacer que la creación de clases sea más agradable. Intente buscar en Prototype.js, Backbone.js o una biblioteca similar que incluya una sintaxis más agradable para hacer OOP en js.

Adán
fuente
2
Alternativamente a usar una herramienta para "hacer que la creación de clases sea más agradable", no podría hacer clases en absoluto. La emulación clásica de OO en js siempre se complica.
Raynos
1
(comentario no constructivo) por ser el lenguaje de "bajo nivel" en el mundo de los navegadores es muy feo sin ninguna razón. ¡Aún lo estoy aprendiendo, gracias!
dnuske
9
Creo en lugar de Car.prototype = new Vehicle (); debe ser Car.prototype = Object.create (Vehicle.prototype); ¿No?
Jordania
@Martin tiene razón, consulte la herencia de javascript
matoni
Una de las razones por las que Car.prototype = new Vehicle (); es incorrecta porque no tiene nada que pasar al Vehículo () mientras recibe un color.
Tushar Arora
62

Dado que este es un gran éxito en Google, me gustaría dar una respuesta actualizada.

El uso de clases de ES6 hace que la herencia y la anulación de métodos sea mucho más fácil:

'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

La superpalabra clave se refiere a la clase principal cuando se usa en la clase heredada. Además, todos los métodos de la clase principal están vinculados a la instancia del niño, por lo que no tiene que escribirsuper.method.apply(this); .

En cuanto a la compatibilidad: la tabla de compatibilidad de ES6 muestra solo las versiones más recientes de las clases de soporte de los principales jugadores (en su mayoría). Los navegadores V8 los han tenido desde enero de este año (Chrome y Opera), y Firefox, usando el motor SpiderMonkey JS, verá clases el próximo mes con su lanzamiento oficial de Firefox 45. En el lado móvil, Android aún no es compatible con esta función, mientras que iOS 9, lanzado hace cinco meses, tiene soporte parcial.

Afortunadamente, existe Babel , una biblioteca JS para recompilar código Harmony en código ES5. Las clases y muchas otras características interesantes de ES6 pueden hacer que su código Javascript sea mucho más legible y fácil de mantener.

Yotam Ofek
fuente
10
Esta respuesta merece más crédito y actualmente es la respuesta correcta.
Klompenrunner
6

Once debería evitar emular OO clásico y usar OO prototípico en su lugar. Una buena biblioteca de utilidades para OO prototípicos son los rasgos .

En lugar de sobrescribir métodos y configurar cadenas de herencia (siempre se debe favorecer la composición de objetos sobre la herencia de objetos), debe agrupar funciones reutilizables en rasgos y crear objetos con ellos.

Ejemplo en vivo

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
fuente
10
No estás respondiendo a la pregunta. Esto debería ser un comentario en todo caso.
FK82
3

En su ejemplo, modificar () es una función privada, a la que no se podrá acceder desde ningún lugar excepto dentro de su definición A, B o C. Debería declararlo como

this.modify = function(){}

C no tiene ninguna referencia a sus padres, a menos que se lo pase a C. Si C está configurado para heredar de A o B, heredará sus métodos públicos (no sus funciones privadas como ha definido modified ()). Una vez que C hereda los métodos de su padre, puede anular los métodos heredados.

Alex Heyd
fuente
modificar es una función local. No hay privado en javascript
Raynos
local / privado, ¿no es lo mismo, solo un término diferente?
Alex Heyd
2

el método modify()que llamó en el último se llama en contexto global si desea anular modify()primero debe heredar Ao B.

Quizás estás intentando hacer esto:

En este caso CheredaA

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();
ama
fuente
1
C.prototype.modify()tendría el thisvalor incorrecto . Es decir, en C.prototypelugar de la instancia de c. Utilice, .call(this)pero su respuesta es solo un duplicado :)
Raynos
1

No, a menos que haga todas las variables "públicas", es decir, las haga miembros de Functionla prototypepropiedad directamente o mediante la propiedad.

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

Notarás algunos cambios.

Lo más importante es que la llamada al constructor de supuestas "superclases" ahora está implícita dentro de esta línea:

<name>.prototype = new C ;

Ambos Ay Bahora tendrán miembros modificables individualmentex y ylo que no sería el caso si hubiéramos escrito en su ... = Clugar.

Entonces, x, yy modifyson todos los miembros "públicos" de manera que la asignación de una diferente Functiona ellos

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

"anulará" el original Functioncon ese nombre.

Por último, la llamada a modifyno se puede hacer en la Functiondeclaración porque la llamada implícita a la "superclase" se volvería a ejecutar cuando establezcamos la supuesta "superclase" en la prototypepropiedad de las supuestas "subclases".

Pero bueno, así es más o menos cómo harías este tipo de cosas en JavaScript.

HTH,

FK

FK82
fuente
De <name>.prototype = new C;todos modos
sigas a
@Raynos: Sí, la hay. A saber, todas las herencias Objectscompartirían el mismo miembro Csi no crea una instancia de un Cobjeto. Por lo tanto, cambiar xen Acambiaría xen Cy, por lo tanto, cambiaría xen B. Lo cual es obviamente indeseable.
FK82
te perdiste el punto. Puede eliminar la línea y el código seguiría funcionando
Raynos
@Raynos: Me temo que estás perdiendo tu propio punto. ;-) Queremos Ay Bheredar C. Si faltara esa línea, ese no sería el caso. De hecho, el único prototipo tanto A, así como Bsería "sombra" tendría acceso a que en ese caso sería Object.prototype.
FK82
mira el código. A y B no utilizan ninguno de los miembros de C del prototipo. Entonces "heredar" de C es inútil. Esto se debe a que A y B redefinen y x, por lo tanto, ensombrecen a todos los miembros. ¿Cuál es el punto de poner C en el prototipo si no lo estás usando? Es un código muerto. ymodifyC
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
fuente