Declarando o método de object javascript na function construtora vs. no protótipo

Na criação de objects javascript, posso colocar uma declaração de método na function construtora ou no protótipo. Por exemplo, digamos que eu queira uma class Dog que tenha uma propriedade Name e um método Bark. Eu posso colocar a declaração do método Bark na function de construtor:

var Dog = function(name) { this.Name = name; this.Bark = function() { alert(this.Name + " bark"); }; } 

ou eu poderia colocar como um método no object de protótipo:

 var Dog = function(name) { this.Name = name; } Dog.prototype.Bark = function() { alert(this.Name + " bark"); }; 

Quando eu instancio objects do tipo Dog, ambas as abordagens parecem funcionar bem:

 var dog = new Dog("Fido"); dog.Bark(); //Both approaches show "Fido bark" 

Devo preferir uma dessas abordagens ao outro? Existem vantagens em usar uma sobre a outra? Nos bastidores, essas duas abordagens acabam fazendo exatamente a mesma coisa? Qual abordagem a maioria das pessoas tende a favorecer?

Obrigado pela ajuda.

Para o exemplo que você dá, você deve usar a abordagem do protótipo. Em geral, isso depende. A principal vantagem da primeira abordagem (inicializar methods no construtor) é que você pode tirar proveito de encerramentos usando variables ​​locais definidas dentro do construtor em seus methods. Essas variables ​​não são diretamente acessíveis fora da function de construtor, portanto, são efetivamente “privadas”, o que significa que sua API é mais limpa do que se essas variables ​​fossem definidas como propriedades do object. Algumas regras gerais:

  • Se seus methods não usam variables ​​locais definidas em seu construtor (seu exemplo não usa), então use a abordagem do protótipo.
  • Se você estiver criando muitos Dog ‘s, use a abordagem do protótipo. Desta forma, todas as “instâncias” (isto é, objects criados pelo construtor Dog ) irão compartilhar um conjunto de funções, enquanto o construtor, um novo conjunto de funções é criado toda vez que o construtor Dog é chamado, usando mais memory.
  • Se você estiver criando um pequeno número de Dog e descobrir que usar variables ​​locais “privadas” em seu construtor melhora seu código, essa pode ser a melhor abordagem. Use seu julgamento e faça alguns pontos de referência se o desempenho ou o consumo de memory forem as principais preocupações.

É possível usar uma abordagem híbrida em que apenas os methods que precisam de access a variables ​​construtoras privadas locais são definidos no construtor, enquanto outros methods são atribuídos ao protótipo.

Por exemplo, o código abaixo usa uma variável local no construtor para acompanhar o número de vezes que este cão latiu enquanto mantinha o número real privado, portanto os methods relacionados com o latido são definidos dentro do construtor. O abanar da cauda não requer access ao número de latidos, portanto, esse método pode ser definido no protótipo.

 var Dog = function(name) { this.name = name; var barkCount = 0; this.bark = function() { barkCount++; alert(this.name + " bark"); }; this.getBarkCount = function() { alert(this.name + " has barked " + barkCount + " times"); }; }; Dog.prototype.wagTail = function() { alert(this.name + " wagging tail"); }; var dog = new Dog("Dave"); dog.bark(); dog.bark(); dog.getBarkCount(); dog.wagTail(); 

Os dois são diferentes: o primeiro armazenará a referência ao método somente no object de protótipo, enquanto a segunda solução armazenará o método em cada um dos objects. Isso significa que cada object conterá um ponteiro extra e, portanto, ocupará um pouco mais de memory cada.

O método por object permite que o método se refira a variables ​​no construtor (um fechamento) e, portanto, permite que você acesse alguns dados que não podem ser acessados ​​a partir de um método de protótipo.

Finalmente, um método protótipo pode ser alterado mais tarde , ou seja, você pode redefinir o Bark em tempo de execução no object de protótipo, e essa alteração funcionará para todos os objects com esse protótipo (já que o método é sempre procurado pelo protótipo).

A grande maioria do código javascript que eu vi usa o método prototype. Eu acho que há três razões para isso que posso pensar em cima da minha cabeça.

A primeira é que você evita que toda class seja um grande construtor: a lógica do construtor entra na function construtora, a lógica para outros methods é declarada em outro lugar – isso é principalmente uma questão de clareza / separação de interesses, mas no JavaScript você precisa de cada bit de clareza você pode chegar em suas mãos.

O segundo é a eficiência. Quando você declara methods no construtor, você está criando uma nova instância do object de function para cada instância do object e também vinculando o escopo do construtor a cada uma dessas funções (ou seja, elas podem referenciar, por exemplo, o argumentos para o construtor, que nunca poderão ser gc desde que o object viva). Quando você declara methods no protótipo, há uma única cópia do object de function que é usado por todas as instâncias – as propriedades de protótipo não são copiadas em instâncias.

Uma terceira razão é que você pode “estender” uma class de várias maneiras ao usar o método prototype, como o encadeamento do protótipo usado pelo Backbone.js e pela construção de class do CoffeeScript.