Por fim, parei de arrastar os pés durante todos esses anos e decidi aprender JavaScript "corretamente". Um dos elementos mais impressionantes do design de idiomas é a implementação de herança. Tendo experiência em Ruby, fiquei muito feliz ao ver fechamentos e digitação dinâmica; mas, por toda a minha vida, não consigo descobrir quais benefícios devem ser obtidos com as instâncias de objetos usando outras instâncias para herança.
271
Respostas:
Sei que essa resposta está 3 anos atrasada, mas realmente acho que as respostas atuais não fornecem informações suficientes sobre como a herança prototípica é melhor do que a herança clássica .
Primeiro, vamos ver os argumentos mais comuns que os programadores JavaScript afirmam em defesa da herança prototípica (estou usando esses argumentos no conjunto de respostas atual):
Agora, esses argumentos são todos válidos, mas ninguém se deu ao trabalho de explicar o porquê. É como dizer a uma criança que estudar matemática é importante. Claro que é, mas a criança certamente não se importa; e você não pode fazer uma criança gostar de matemática dizendo que é importante.
Penso que o problema com a herança prototípica é que é explicado da perspectiva do JavaScript. Eu amo JavaScript, mas a herança prototípica no JavaScript está errada. Ao contrário da herança clássica, existem dois padrões de herança prototípica:
Infelizmente, o JavaScript usa o padrão construtor de herança prototípica. Isso ocorre porque quando o JavaScript foi criado, Brendan Eich (o criador do JS) queria que ele se parecesse com o Java (que tem herança clássica):
Isso é ruim porque quando as pessoas usam construtores em JavaScript, elas pensam em construtores herdados de outros construtores. Isto está errado. Na herança prototípica, os objetos herdam de outros objetos. Os construtores nunca entram em cena. É isso que confunde a maioria das pessoas.
Pessoas de linguagens como Java, que tem herança clássica, ficam ainda mais confusas porque, embora os construtores pareçam com classes, eles não se comportam como classes. Como Douglas Crockford afirmou:
Aí está. Direto da boca do cavalo.
Verdadeira herança prototípica
A herança prototípica tem tudo a ver com objetos. Objetos herdam propriedades de outros objetos. É tudo o que há para isso. Existem duas maneiras de criar objetos usando herança prototípica:
Nota: O JavaScript oferece duas maneiras de clonar um objeto - delegação e concatenação . A partir de agora, usarei a palavra "clone" para se referir exclusivamente à herança via delegação, e a palavra "cópia" para se referir exclusivamente à herança via concatenação.
Chega de conversa. Vamos ver alguns exemplos. Digamos que eu tenha um círculo de raio
5
:Podemos calcular a área e a circunferência do círculo a partir do seu raio:
Agora eu quero criar outro círculo de raio
10
. Uma maneira de fazer isso seria:No entanto, o JavaScript fornece uma maneira melhor - delegação . A
Object.create
função é usada para fazer isso:Isso é tudo. Você acabou de fazer uma herança prototípica em JavaScript. Isso não era simples? Você pega um objeto, clona-o, muda o que precisa e, e pronto - você conseguiu um objeto novo.
Agora você pode perguntar: "Como isso é simples? Toda vez que quero criar um novo círculo, preciso clonar
circle
e atribuir manualmente um raio". Bem, a solução é usar uma função para fazer o trabalho pesado para você:De fato, você pode combinar tudo isso em um único objeto literal da seguinte maneira:
Herança Prototípica em JavaScript
Se você perceber no programa acima, a
create
função cria um clone decircle
, atribui um novoradius
a ele e depois o devolve. É exatamente isso que um construtor faz no JavaScript:O padrão construtor em JavaScript é o padrão prototípico invertido. Em vez de criar um objeto, você cria um construtor. A
new
palavra-chave vincula othis
ponteiro dentro do construtor a um clone doprototype
construtor.Parece confuso? É porque o padrão do construtor no JavaScript complica desnecessariamente as coisas. É isso que a maioria dos programadores acha difícil de entender.
Em vez de pensar em objetos herdados de outros objetos, eles pensam em construtores herdados de outros construtores e depois ficam completamente confusos.
Há várias outras razões pelas quais o padrão de construtor em JavaScript deve ser evitado. Você pode ler sobre eles no meu blog aqui: Construtores vs Protótipos
Então, quais são os benefícios da herança prototípica sobre a herança clássica? Vamos examinar novamente os argumentos mais comuns e explicar o porquê .
1. Herança prototípica é simples
O CMS declara em sua resposta:
Vamos considerar o que acabamos de fazer. Criamos um objeto
circle
com um raio de5
. Então nós o clonamos e demos ao raio um clone de10
.Portanto, precisamos apenas de duas coisas para fazer a herança prototípica funcionar:
Object.create
).Em contraste, a herança clássica é muito mais complicada. Na herança clássica, você tem:
Você entendeu a ideia. O ponto é que a herança prototípica é mais fácil de entender, mais fácil de implementar e mais fácil de raciocinar.
Como Steve Yegge coloca em seu clássico post no blog " Portrait of a N00b ":
No mesmo sentido, as classes são apenas metadados. As classes não são estritamente necessárias para herança. No entanto, algumas pessoas (geralmente n00bs) acham as aulas mais confortáveis para trabalhar. Isso lhes dá uma falsa sensação de segurança.
Como afirmei anteriormente, as aulas dão às pessoas uma falsa sensação de segurança. Por exemplo, você obtém muitos
NullPointerException
s em Java, mesmo quando seu código é perfeitamente legível. Acho que a herança clássica geralmente atrapalha a programação, mas talvez seja apenas Java. Python tem um incrível sistema de herança clássica.2. Herança prototípica é poderosa
A maioria dos programadores que têm uma formação clássica argumenta que a herança clássica é mais poderosa que a herança prototípica, porque possui:
Esta afirmação é falsa. Já sabemos que o JavaScript suporta variáveis privadas por meio de fechamentos , mas e a herança múltipla? Objetos em JavaScript possuem apenas um protótipo.
A verdade é que a herança prototípica suporta a herança de vários protótipos. Herança prototípica significa simplesmente um objeto herdado de outro objeto. Na verdade, existem duas maneiras de implementar a herança prototípica :
Sim JavaScript permite apenas que objetos sejam delegados a outro objeto. No entanto, permite copiar as propriedades de um número arbitrário de objetos. Por exemplo,
_.extend
faz exatamente isso.Claro que muitos programadores não consideram que isso seja verdade herança porque
instanceof
eisPrototypeOf
dizer o contrário. No entanto, isso pode ser facilmente remediado armazenando uma matriz de protótipos em cada objeto que herda de um protótipo por concatenação:Portanto, a herança prototípica é tão poderosa quanto a herança clássica. Na verdade, é muito mais poderoso que a herança clássica, porque na herança prototípica você pode escolher manualmente quais propriedades copiar e quais propriedades omitir de diferentes protótipos.
Na herança clássica, é impossível (ou pelo menos muito difícil) escolher quais propriedades você deseja herdar. Eles usam classes base virtuais e interfaces para resolver o problema do diamante .
No JavaScript, porém, você provavelmente nunca ouvirá falar do problema dos diamantes, porque pode controlar exatamente quais propriedades deseja herdar e de quais protótipos.
3. Herança prototípica é menos redundante
Este ponto é um pouco mais difícil de explicar porque a herança clássica não leva necessariamente a um código mais redundante. De fato, a herança, seja clássica ou prototípica, é usada para reduzir a redundância no código.
Um argumento pode ser que a maioria das linguagens de programação com herança clássica é digitada estaticamente e requer que o usuário declare explicitamente os tipos (ao contrário de Haskell, que possui tipagem estática implícita). Portanto, isso leva a um código mais detalhado.
Java é notório por esse comportamento. Lembro-me claramente de Bob Nystrom mencionando a seguinte anedota em seu post sobre Pratt Parsers :
Mais uma vez, acho que é apenas porque o Java é péssimo.
Um argumento válido é que nem todos os idiomas que têm herança clássica suportam herança múltipla. Novamente Java vem à mente. Sim, o Java tem interfaces, mas isso não é suficiente. Às vezes, você realmente precisa de herança múltipla.
Como a herança prototípica permite herança múltipla, o código que requer herança múltipla é menos redundante se escrito usando herança prototípica, em vez de em um idioma que tenha herança clássica, mas não herança múltipla.
4. Herança prototípica é dinâmica
Uma das vantagens mais importantes da herança de protótipo é que você pode adicionar novas propriedades aos protótipos após a criação. Isso permite que você adicione novos métodos a um protótipo que será automaticamente disponibilizado a todos os objetos que delegam a esse protótipo.
Isso não é possível na herança clássica porque uma vez que uma classe é criada, você não pode modificá-la em tempo de execução. Essa é provavelmente a maior vantagem da herança prototípica sobre a herança clássica, e deveria estar no topo. No entanto, eu gosto de guardar o melhor para o fim.
Conclusão
A herança prototípica é importante. É importante educar os programadores de JavaScript sobre por que abandonar o padrão construtor da herança prototípica em favor do padrão prototípico da herança prototípica.
Precisamos começar a ensinar JavaScript corretamente e isso significa mostrar aos novos programadores como escrever código usando o padrão prototípico em vez do padrão construtor.
Não só será mais fácil explicar a herança prototípica usando o padrão prototípico, como também criará melhores programadores.
Se você gostou desta resposta, também deve ler meu post no blog " Por que a herança prototípica é importante ". Confie em mim, você não ficará desapontado.
fonte
Object.create
está criando um novo objeto com o protótipo especificado. Sua escolha de palavras dá a impressão de que o protótipo é clonado.Permita-me realmente responder à pergunta em linha.
A herança de protótipo possui as seguintes virtudes:
No entanto, possui as seguintes desvantagens:
Acho que você pode ler nas entrelinhas acima e apresentar as vantagens e desvantagens correspondentes dos esquemas tradicionais de classe / objeto. É claro que há mais em cada área, então deixarei o resto para outras pessoas respondendo.
fonte
Na OMI, o principal benefício da herança prototípica é sua simplicidade.
A natureza prototípica da linguagem pode confundir as pessoas que são classicamente treinadas, mas acontece que, na verdade, esse é um conceito realmente simples e poderoso, a herança diferencial .
Você não precisa fazer a classificação , seu código é menor, menos redundante, os objetos herdam de outros objetos mais gerais.
Se você pensa em protótipo , logo perceberá que não precisa de aulas ...
A herança prototípica será muito mais popular em um futuro próximo, a especificação ECMAScript 5th Edition introduziu o
Object.create
método, que permite produzir uma nova instância de objeto que herda de outra de uma maneira realmente simples:Esta nova versão do padrão está sendo implementada por todos os fornecedores de navegadores, e acho que começaremos a ver uma herança prototípica mais pura ...
fonte
Realmente não há muito o que escolher entre os dois métodos. A idéia básica a entender é que, quando o mecanismo JavaScript recebe uma propriedade de um objeto para leitura, ele primeiro verifica a instância e, se essa propriedade estiver ausente, verifica a cadeia de protótipos. Aqui está um exemplo que mostra a diferença entre prototípico e clássico:
Prototipado
Clássico com métodos de instância (Ineficiente porque cada instância armazena sua própria propriedade)
Clássico eficiente
Como você pode ver, como é possível manipular o protótipo de "classes" declaradas no estilo clássico, não há realmente nenhum benefício em usar a herança prototípica. É um subconjunto do método clássico.
fonte
Desenvolvimento Web: Herança Prototípica vs. Herança Clássica
http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html
Classical vs herança prototípica - Stack Overflow em Português
Herança prototípica clássica versus
fonte