Herança Revisitada

by 3 colaboradores:

Veja Herança e o protótipo de construtor para uma descrição de Herança Javascript e o protótipo de construtor.

Herança sempre esteve disponível no JavaScript, mas os exemplos desta página usam alguns métodos introduzidos na ECMAScript 5. Veja as diferentes páginas de método para ver se eles podem ser emulados.

Exemplo

B deve herdar de A:

function A(a){
  this.varA = a;
}
A.prototype = {
  varA : null,
  doSomething : function(){
    // ...
  }
}
function B(a, b){
  A.call(this, a);
  this.varB = b;
}
B.prototype = Object.create(new A(), {
  varB : { value: null, enumerable: true, configurable: true, writable: true },
  doSomething : { value: function(){ // override
       A.prototype.doSomething.apply(this, arguments); // call super
       // ...
    }, enumerable: true, configurable: true, writable: true }
})

var b = new B();
b.doSomething();

As partes importantes são:

  • Tipos são definidos em .prototype
  • Você usa Object.create() para herdar

prototype e Object.getPrototypeOf

JavaScript é um pouco confuso para desenvolvedores vindos de Java e C++, já que é toda dinâmica, toda tempo de execução e não possui classes de forma alguma. Até as "classes" que simulamos são apenas um objeto de função.

Você provavelmente já percebeu que a nossa function A tem uma propriedade especial chamada prototype. Essa propriedade especial funciona com o operador JavaScript new. A referência para o objeto protótipo é copiada para a propriedade interna [[Prototype]] da nova instância. Por exemplo, quando você faz var a1 = new A(), JavaScript (depois de criar o objeto em memória e antes de executar function A() com this definida para ela) define a1.[[Prototype]] = A.prototype. Quando você então acessa propriedades da instância, JavaScript primeiro verifica se elas existem naquele objeto diretamente, e se não, procura em [[Prototype]]. Isso significa que tudo o que você define em prototype é efetivamente compartilhado por todas as instâncias, e você pode até mudar partes de prototype depois e ter estas mudanças aparecendo em todas as instâncias existentes, se você quiser.

Se, no exemplo acima, você fizer var a1 = new A(); var a2 = new A(); então a1.doSomething na verdade se referirira a Object.getPrototypeOf(a1).doSomething, que é o mesmo que o A.prototype.doSomething que você definiu, ou seja Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething.

Resumindo, prototype é para tipos, enquanto que Object.getPrototypeOf() é a mesma coisa para instâncias.

[[Prototype]] é procurado recursivamente, ou seja a1.doSomething, Object.getPrototypeOf(a1).doSomething, Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething etc., até ser encontrado ou Object.getPrototypeOf retorna null.

Então quando você chama

var o = new Foo();

JavaScript na verdade faz apenas

var o = new Object();
o.[[Prototype]] = Foo.prototype;
o.Foo();

(ou algo parecido) e quando mais tarde você faz 

o.someProp;

verifica se o possui a propriedade someProp. Em caso negativo verifica Object.getPrototypeOf(o).someProp  e se isso não existir verifica  Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp e assim por diante.

Etiquetas do documento e colaboradores

Contributors to this page: teoli, jaydson, galvao
Última atualização por: teoli,