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.