A perspectiva do EcmaScript sobre a teoria geral da OOP

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; //  int age; //  void displayInfo(){ System.out.printf("Name: %s \tAge: %d\n", name, age); } } 

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:

 //        const Person = { name: null, age: null, sayHi() { return `Hi! My name is ${this.name}. I'm ${this.age} years old.` } } const Tom = { //-       } Tom.__proto__ = Person; 

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'; //    Tom.surname = 'Williams'; //    Tom.age = 28;//    Tom.sayHi();//  sayHi,      ,    ,       const tomSon = { name: 'John', age: 5, sayHi() { return this.__proto__.sayHi.call(this) + `My father is ${this.__proto__.name} ${this.surname}`; } } //,     tomSon.__proto__ = Tom; tomSon.sayHi();//  "Hi! My name is John. I'm 5 years old.My father is Tom Williams" 

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.

 // ,    (  ) //  ,   ,    , //      const Ben = { name: 'Ben', surname: 'Silver', age: 42, sayHi() { return `Hello! I'm ${this.name} ${this.surname}. `; } } tomSon.nativeFather = Tom; tomSon.__proto__= Ben; tomSon.sayHi(); //    (),     () //   'Hello! I'm John Silver. My father is Ben Silver' 

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']; // tomSon   ,             ,      tomSon.sayAboutFathersHobies = function () { const reducer = (accumulator, current) => {`${accumulator} and ${current}`} return `My Father play ${this.hobbies.reduce(reducer)}` } tomSon.sayAboutFathersHobies(); //  'My Father play chess and 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.

Source: https://habr.com/ru/post/pt468157/


All Articles