Métodos de substituição de JavaScript

87

Digamos que você tenha o código abaixo:

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

Agora a questão é, se há alguma maneira pela qual eu posso substituir o método modifyde Ccom os métodos que estão em Ae B. Em Java, você usaria a super-palavra - chave, mas como você pode conseguir algo assim em JavaScript?

Daniel Nastase
fonte
6
modifynão é um método, mas uma função aninhada - há uma diferença entre os dois ...
Šime Vidas
2
Em Java, você usa a superpalavra-chave para acessar os campos e métodos não privados de uma superclasse. Você não o usa para substituí-los.
FK82

Respostas:

138

Edit: Já se passaram seis anos desde que a resposta original foi escrita e muita coisa mudou!

  • Se estiver usando uma versão mais recente de JavaScript, possivelmente compilada com uma ferramenta como o Babel , você pode usar classes reais .
  • Se você estiver usando os construtores de componentes do tipo classe fornecidos por Angular ou React , você vai querer procurar nos documentos por essa estrutura.
  • Se você estiver usando o ES5 e criando classes "falsas" à mão usando protótipos, a resposta abaixo ainda está certa como sempre.

Boa sorte!


A herança do JavaScript parece um pouco diferente do Java. Esta é a aparência do sistema de objeto 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"

Infelizmente, isso é um pouco feio e não inclui uma maneira muito boa de "super": você deve especificar manualmente o método das classes pai que deseja chamar. Como resultado, há uma variedade de ferramentas para tornar a criação de classes mais agradável. Tente examinar Prototype.js, Backbone.js ou uma biblioteca semelhante que inclua uma sintaxe mais agradável para fazer OOP em js.

Adão
fonte
2
Como alternativa ao uso de uma ferramenta para "tornar a criação de classes mais agradável", você não poderia criar classes. A emulação OO clássica em js sempre fica complicada.
Raynos
1
(comentário não construtivo) por ser a linguagem de "baixo nível" no mundo dos navegadores é muito feia sem motivo. Ainda aprendendo, obrigado!
dnuske
9
Eu acredito que em vez de Car.prototype = new Vehicle (); deve ser Car.prototype = Object.create (Vehicle.prototype); não?
Jordan
@Martin está certo, consulte javascript-inheritance
matoni
Uma das razões pelas quais Car.prototype = new Vehicle (); está incorreto por não ter nada para passar para o Veículo () enquanto recebe uma cor.
Tushar Arora
62

Como este é um dos principais sucessos do Google, gostaria de dar uma resposta atualizada.

O uso de classes ES6 torna a herança e a substituição de métodos muito mais fáceis:

'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

A superpalavra-chave refere-se à classe pai quando usada na classe herdada. Além disso, todos os métodos da classe pai são vinculados à instância do filho, portanto, você não precisa escrever super.method.apply(this);.

Quanto à compatibilidade: a tabela de compatibilidade ES6 mostra apenas as versões mais recentes das classes de suporte dos principais players (principalmente). Os navegadores V8 os têm desde janeiro deste ano (Chrome e Opera), e o Firefox, usando o mecanismo SpiderMonkey JS, terá aulas no próximo mês com o lançamento oficial do Firefox 45. No lado móvel, o Android ainda não oferece suporte a esse recurso, enquanto o iOS 9, lançado há cinco meses, tem suporte parcial.

Felizmente, existe a Babel , uma biblioteca JS para recompilar o código Harmony em código ES5. Classes e muitos outros recursos interessantes no ES6 podem tornar seu código Javascript muito mais legível e sustentável.

Yotam Ofek
fonte
10
Esta resposta merece mais crédito e atualmente é a resposta certa.
Klompenrunner
6

O Once deve evitar emular OO clássico e usar OO prototípico. Uma boa biblioteca de utilitários para OO prototípico é traits .

Em vez de sobrescrever métodos e configurar cadeias de herança (deve-se sempre favorecer a composição de objetos em vez da herança de objetos), você deve agrupar funções reutilizáveis ​​em características e criar objetos com elas.

Exemplo ao 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
fonte
10
Você não está respondendo à pergunta. Isso deve ser um comentário, se alguma coisa.
FK82
@ FK82 vamos continuar esta discussão no chat
Raynos
3

modify () em seu exemplo é uma função privada, que não estará acessível de qualquer lugar, exceto dentro de sua definição A, B ou C. Você precisaria declará-lo como

this.modify = function(){}

C não tem referência a seus pais, a menos que você o passe para C. Se C for configurado para herdar de A ou B, ele herdará seus métodos públicos (não suas funções privadas, como você definiu modify ()). Depois que C herda os métodos de seu pai, você pode substituir os métodos herdados.

Alex Heyd
fonte
modificar é uma função local. Não há privado em javascript
Raynos
local / privado, não é a mesma coisa, apenas um termo diferente?
Alex Heyd
2

o método modify()que você chamou por último é chamado no contexto global se você deseja substituir, modify()primeiro precisa herdar Aou B.

Talvez você esteja tentando fazer isso:

Neste caso CherdaA

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();
amor
fonte
1
C.prototype.modify()teria o thisvalor errado . Ou seja, em C.prototypevez da instância de c. Por favor, use, .call(this)mas então sua resposta é apenas uma duplicata :)
Raynos
1

A não ser que você torne todas as variáveis ​​"públicas", isto é, torne-as membros do Functiondiretamente ou por meio da prototypepropriedade.

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

Você notará algumas mudanças.

Mais importante ainda, a chamada ao suposto construtor de "superclasses" agora está implícita nesta linha:

<name>.prototype = new C ;

Ambos Ae Bagora terão membros modificáveis ​​individualmentex e yque não seria o caso se tivéssemos escrito em ... = Cvez disso.

Em seguida, x, ye modifysão todos os membros "públicas" de modo a que a atribuição de um diferente Functionpara eles

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

irá "substituir" o original Functioncom esse nome.

Por último, a chamada para modifynão pode ser feita na Functiondeclaração porque a chamada implícita para a "superclasse" seria então executada novamente quando configurássemos a suposta "superclasse" para a prototypepropriedade das supostas "subclasses".

Mas bem, é mais ou menos assim que você faria esse tipo de coisa em JavaScript.

HTH,

FK

FK82
fonte
De qualquer forma, não adianta <name>.prototype = new C;você seguir todos os membros do protótipo
Raynos
@Raynos: Sim, existe. A saber, toda a herança Objectsestaria compartilhando o mesmo membro em Cse você não instanciar um Cobjeto. Assim, mudar xem Amudaria xem Ce, portanto, mudaria xem B. O que é obviamente indesejável.
FK82
você perdeu o ponto. Você pode remover a linha e o código ainda funcionará
Raynos
@Raynos: Você está perdendo seu próprio ponto, infelizmente. ;-) Queremos Ae Bherdar de C. Se essa linha estivesse faltando, não seria o caso. Na verdade, o único protótipo Aao qual B"sombra" teria acesso nesse caso seria Object.prototype.
FK82
olhe para o código. A e B não usam nenhum dos membros de C no protótipo. Portanto, "herdar" de C é inútil. Isso ocorre porque A e B redefinem e x, portanto, sombreiam todos os membros de. Qual é o ponto de colocar C no protótipo se você não o estiver usando? É um código morto. 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
fonte