Olá Habr!
Até hoje, traduzi apenas artigos interessantes, em minha opinião, de autores de língua inglesa. E agora é hora de escrever algo você mesmo. Para o primeiro artigo, escolhi um tópico que, tenho certeza, será útil para desenvolvedores juniores que desejam crescer para o "meio", porque analisará a semelhança / diferença entre JavaScript e linguagens de programação clássicas (C ++, C #, Java) em termos de POO. Então, vamos começar!
As disposições gerais do paradigma
Se observarmos a definição de JavaScript na
Wikipedia , veremos o seguinte conceito:
JavaScript (/ ˈdʒɑːvɑːˌskrɪpt /; abbr. JS /ˈdʒeɪ.ɛs./) é uma linguagem de programação com vários paradigmas. Suporta estilos orientados a objetos, imperativos e funcionais. É uma implementação da linguagem ECMAScript (padrão ECMA-262).
A seguir, a partir desta definição, o JavaScript não existe por si só, mas é uma implementação de algumas especificações do EcmaScript. Além disso, outros idiomas implementam essa especificação.
Os seguintes paradigmas estão presentes no EcmaScript (doravante ES):
- estrutural
- OOP
- funcional
- imperativo
- orientado a aspectos (em casos raros)
OOP no ES é implementado em uma
organização de protótipo . Dos desenvolvedores iniciantes em resposta à pergunta: "Como o OOP no JS é diferente do OOP nos idiomas clássicos". Como regra, eles ficam muito vagos: “As aulas estão em linguagens clássicas e os protótipos em JS”.
Na realidade, a situação é um pouco mais complicada. Em termos de comportamento, a diferença entre a
organização de classe dinâmica e a
organização de protótipo é pequena (certamente existe, mas não tão global).
Dê uma olhada no Python ou Ruby. Nesses idiomas, o OOP é baseado em uma organização de classe dinâmica. Em ambas as linguagens, podemos alterar dinamicamente a classe de um objeto à medida que o programa progride e as alterações dentro da classe também afetam dinamicamente as entidades que ele gera. Assim como no JS, mas no JS, o OOP é baseado em protótipos.
Uma diferença significativa entre idiomas com uma organização de classe estática e uma organização de protótipo. A diferença em si é que “existem classes. aqui protótipos ”não são tão significativos.
Em que a organização de classe estática se baseia?
A base desse tipo de POO são os conceitos de "Classe" e "Essência".
Uma classe é um determinado conjunto de características generalizadas formalizadas de entidades que pode gerar. I.e. este é um certo plano geral de todos os objetos gerados por ele.
As características são de dois tipos. Propriedades (descrição de uma entidade) e métodos (atividade de uma entidade, seu comportamento).
As entidades geradas por uma classe são cópias dessa classe, mas com propriedades inicializadas. Como vemos, a classe regula estritamente a descrição de uma entidade (fornecendo um conjunto estritamente definido de propriedades) e seu comportamento (fornecendo uma lista estritamente definida de métodos).
Aqui está um pequeno exemplo em JAVA:
class Person{ String name;
Agora crie uma instância da classe:
public class Program{ public static void main(String[] args) { Person tom; } }
Nossa entidade
tom tem todas as características da classe
Person , também possui todos os métodos de sua classe.
O paradigma OOP fornece uma paleta muito ampla de possibilidades para reutilizar código, um desses recursos é a
Herança .
Uma classe pode estender outra classe, criando assim um relacionamento de generalização-especialização. Nesse caso, as propriedades da classe geral (superclasse) são copiadas para a essência da classe descendente quando são criadas, e os métodos estão disponíveis por referência (de acordo com a cadeia hierárquica de herança). No caso de digitação de classe estática, essa cadeia é
estática e, no caso de digitação dinâmica, pode ser alterada durante a execução do programa. Essa é a diferença mais importante. Aconselho agora a lembrar deste momento. Além disso, quando chegarmos à organização Prototype, a essência do problema da resposta "existem classes, existem protótipos" se tornará óbvia.
Quais são as desvantagens dessa abordagem?
Eu acho que é óbvio que:
- Em essência, pode haver características que nunca serão úteis.
- Uma classe não pode alterar, adicionar, excluir dinamicamente propriedades e métodos que ela fornece às entidades geradas, ou seja, não pode mudar sua assinatura.
- Em essência, propriedades ou métodos que não estão presentes na classe dos pais (ou na cadeia hierárquica dos pais) não podem estar presentes
- O consumo de memória é proporcional ao número de links na hierarquia de herança (devido às propriedades de cópia)
Em que a organização de protótipos se baseia?
O conceito principal da organização do protótipo é um objeto mutável dinâmico (dmo). O DMO não precisa de uma classe. Ele próprio pode armazenar todas as suas propriedades e métodos.
Ao definir um DMO de uma propriedade, ele verifica a presença dessa propriedade nela. Se houver uma propriedade, ela será simplesmente atribuída; caso contrário, a propriedade será adicionada e inicializada com o valor passado. Os DMOs podem alterar sua assinatura durante o programa quantas vezes quiserem.
Aqui está um exemplo:
Acho que todos os participantes sabem que a sintaxe da classe apareceu no ES6, mas isso não passa de açúcar sintático, ou seja, protótipos sob o capô. O código acima não deve ser considerado uma boa prática de codificação. Isso nada mais é do que uma ilustração, é apresentado desta forma (agora todas as pessoas normais usam aulas de ES6) para não confundir o leitor e enfatizar a diferença de conceitos teóricos.
Se enviarmos o objeto Tom para o console, veremos que o próprio objeto possui apenas o link _proto_, que está sempre presente nele por padrão. A referência aponta para o objeto Person, que é um protótipo do objeto Tom.
Um protótipo é um objeto que serve como um protótipo para outros objetos ou um objeto no qual outro objeto pode desenhar propriedades e métodos, se necessário.
O protótipo de um objeto pode ser qualquer objeto; além disso, um objeto pode reatribuir seu protótipo durante o programa.
Vamos voltar ao nosso Tom:
Tom.name = 'Tom';
Observe que o nome, as propriedades da idade e o método sayHi são as propriedades do objeto tomSon. Ao mesmo tempo, em tomSon sayHi chamamos explicitamente o método protótipo sayHi como se estivesse no objeto Tom, mas, na verdade, ele não está lá e retorna implicitamente do protótipo Person. Também operamos explicitamente no nome da propriedade do protótipo e obtemos implicitamente a propriedade sobrenome, que chamamos de propriedade do objeto tomSon, mas na verdade não está lá. A propriedade sobrenome é puxada implicitamente pelo link
__proto__ do protótipo.
Continuamos o desenvolvimento da história de nosso Tom e seu filho John.
Observe que durante o programa alteramos o protótipo do objeto já criado. Essa é a semelhança entre a
organização Prototype e a
organização de classe dinâmica . É por isso que a resposta "existem classes, existem protótipos" para a pergunta "qual é a diferença entre linguagens clássicas e JavaScript?" não está totalmente correto e indica algum mal-entendido da teoria da OOP e sua implementação em classes e / ou protótipos.
Com a organização Prototype, diferentemente da organização de classe Static, temos a oportunidade de fazer alterações no protótipo depois de criar uma entidade que herda as propriedades desse protótipo, e essas alterações afetarão a entidade já criada.
Ben.hobbies = ['chess', 'badminton'];
Isso é chamado de
modelo de delegação de organização ou
herança de protótipo .
Como é determinada a capacidade de uma entidade implementar determinado comportamento?
Em uma organização de classe estática, essa operação envolve a verificação da entidade para associação a uma classe específica que implementa o comportamento necessário. Na organização do protótipo, existe o conceito de
digitação de
pato . No caso da digitação de pato, verificar a entidade quanto à capacidade de implementar um comportamento específico significará testar diretamente a entidade quanto à capacidade de implementar esse comportamento em um momento específico, ou seja, em diferentes partes do programa, o resultado da verificação pode ser diametralmente oposto.
Quais são as vantagens de uma abordagem de protótipo?
- Mais flexibilidade
- As entidades não possuem propriedades das quais não precisam.
Quais são as desvantagens?
- Menos claro
- Nem sempre é fácil rastrear o que serviu como ponto de partida para o comportamento indesejado da entidade, ou seja, O protótipo é menos previsível que a organização de classe estática
- A comunidade de desenvolvimento de software não está familiarizada com isso, apesar da popularidade e prevalência do JavaScript
Conclusão
Nisso terminaremos hoje. Espero ter conseguido transmitir a ideia de que a diferença entre linguagens clássicas e JavaScript não está relacionada à presença / ausência de classes e à presença / ausência de protótipos, mas à natureza estática / dinâmica da organização.
Obviamente, muito não foi considerado. Como não gostaria de escrever artigos muito longos, discutiremos os recursos do Modelo em cascata na organização de protótipos e nas ferramentas de OOP (polimorfismo, encapsulamento, abstração etc.) nos artigos subsequentes.