
A maioria das linguagens de programação surgiu de um paradigma antigo originado no tempo de Fortran. O guru do JavaScript Douglas Crockford arranca essas raízes secas, permitindo-nos pensar no futuro da programação, passando para um novo nível de entendimento dos requisitos para o Próximo Idioma.
O autor começa com o básico: nomes, números, valores lógicos, caracteres e outras informações básicas. Você aprenderá não apenas sobre os problemas e dificuldades de trabalhar com tipos em JavaScript, mas também como contorná-los. Então você começará a se familiarizar com estruturas e funções de dados para entender os mecanismos subjacentes a elas e aprender a usar funções de ordem superior e um estilo de programação orientado a objetos sem classes.
Trecho
Como o código funciona sem classes
E você pensa que é inteligente fora de todas as classes e gratuito.
John LennonUma das idéias principais no desenvolvimento da programação orientada a objetos era um modelo para a troca de dados entre as partes do programa. O nome do método e seus argumentos devem ser representados na forma de mensagens. Uma chamada de método envia uma mensagem para o objeto. Cada objeto é caracterizado por seu próprio comportamento, que se manifesta ao receber mensagens específicas. O remetente acredita que o destinatário sabe o que fazer com a mensagem.
Um benefício adicional é o polimorfismo. Cada objeto que reconhece uma mensagem específica tem o direito de recebê-la. O que acontece a seguir depende da especialização do objeto. E este é um pensamento muito produtivo.
Infelizmente, começamos a nos distrair com a herança - um esquema muito eficaz para reutilizar o código. Sua importância está associada à capacidade de reduzir custos de mão-de-obra ao desenvolver um programa. A herança é construída em um plano semelhante, com exceção de algumas nuances. Podemos dizer que algum objeto ou classe de objetos é semelhante a outro objeto ou classe de objetos, mas possui algumas diferenças importantes. Em uma situação simples, tudo funciona muito bem. Deve-se lembrar que o OOP moderno começou com o Smalltalk, uma linguagem de programação para crianças. À medida que a situação se torna mais complicada, a herança se torna problemática. Dá origem a uma forte coesão de classes. Alterar uma classe pode causar falhas nas classes que dependem dela. Módulos de classes são apenas inúteis.
Além disso, observamos maior atenção às propriedades, e não aos objetos. É dada atenção especial aos métodos de obter (obter-métodos) e atribuir (definir-métodos) valores para cada propriedade individual e, em projetos ainda menos bem-sucedidos, as propriedades são abertas e podem ser alteradas sem o conhecimento do objeto. É bem possível introduzir um projeto mais bem-sucedido, onde as propriedades estão ocultas e os métodos processam transações, não apenas lidando com alterações de propriedades. Mas essa abordagem nem sempre é aplicada.
Além disso, há muita dependência de tipo. Os tipos se tornaram um recurso do Fortran e de idiomas posteriores, pois eram convenientes para os criadores do compilador. Desde então, a mitologia em torno dos tipos cresceu, tendo adquirido alegações extravagantes de que os tipos protegem o programa de erros. Apesar da devoção aos tipos, os erros não abandonavam a prática cotidiana.
Os tipos são respeitados e elogiados pela detecção precoce de erros de cálculo no estágio de compilação. Quanto mais cedo uma supervisão for descoberta, menor será o custo necessário para eliminá-la. Mas, com o teste adequado do programa, todos esses erros de cálculo são detectados muito rapidamente. Portanto, os erros de identificação do tipo são classificados como de baixo custo.
Os tipos não são os culpados pelo aparecimento de erros difíceis de detectar e caros. Sua falha não ocorre na ocorrência de problemas causados por esses erros e exige alguns truques. Os tipos podem nos forçar a usar métodos de programação obscuros, confusos e duvidosos.
Os tipos são como uma dieta de perda de peso. A dieta não é acusada de retornar e ganhar peso. Ela também não é considerada a causa do sofrimento ou os problemas de saúde que causou. As dietas dão esperança de que o peso retorne a uma norma saudável e continuaremos a comer junk food.
A herança clássica nos permite pensar que criamos programas de alta qualidade, enquanto cometemos mais erros e aplicamos heranças cada vez mais ineficientes. Se você ignorar as manifestações negativas, os tipos parecem ser uma grande vitória. Os benefícios são óbvios. Mas se você observar os tipos com mais atenção, perceberá que os custos excedem os benefícios.
Construtor
No capítulo 13, trabalhamos com fábricas - funções que retornam funções. Agora podemos fazer algo semelhante com construtores - funções que retornam objetos que contêm funções.
Vamos começar criando counter_constructor, semelhante ao gerador de contador. Tem dois métodos, para cima e para baixo:
function counter_constructor() { let counter = 0; function up() { counter += 1; return counter; } function down() { counter -= 1; return counter; } return Object.freeze({ up, down }); }
O objeto retornado está congelado. Não pode ser danificado ou danificado. O objeto tem um estado. O contador de variáveis é uma propriedade privada do objeto. Você pode acessá-lo apenas através de métodos. E não precisamos usar isso.
Esta é uma circunstância muito importante. A interface do objeto é exclusivamente métodos. Ele tem uma casca muito forte. Temos o melhor encapsulamento. Não há acesso direto aos dados. Este é um design modular de alta qualidade.
Um construtor é uma função que retorna um objeto. Os parâmetros e variáveis de construtor tornam-se propriedades particulares do objeto. Não possui propriedades públicas que consistem em dados. Funções internas tornam-se métodos de objeto. Eles transformam propriedades em propriedades fechadas. Os métodos que caem em um objeto congelado estão abertos.
Os métodos devem implementar transações. Suponha, por exemplo, que tenhamos um objeto de pessoa. Pode ser necessário alterar o endereço da pessoa cujos dados estão armazenados. Para fazer isso, você não precisa de um conjunto separado de funções para alterar cada elemento de endereço individual. Precisamos de um método que receba um objeto literal, capaz de descrever todas as partes do endereço que precisam ser alteradas.
Uma das idéias brilhantes em JavaScript é o objeto literal. Essa é uma sintaxe agradável e expressiva para agrupar informações. Ao criar métodos que consomem e criam objetos de dados, você pode reduzir o número de métodos, aumentando assim a integridade do objeto.
Acontece que temos dois tipos de objetos.
- Objetos rígidos contêm apenas métodos. Esses objetos protegem a integridade dos dados contidos no fechamento. Eles nos fornecem polimorfismo e encapsulamento.
- Objetos de dados flexíveis contêm apenas dados. Eles não têm comportamento. Esta é apenas uma coleção útil com a qual as funções podem funcionar.
Acredita-se que o POO tenha começado adicionando procedimentos aos registros na linguagem Kobol, garantindo assim algum tipo de comportamento. Acredito que a combinação de métodos e propriedades de dados foi um passo importante, mas não deve ser o último passo.
Se o objeto rígido precisar ser convertido em uma sequência, o método toJSON deverá estar ativado. Caso contrário, o JSON.stringify o verá como um objeto vazio, ignorando métodos e dados ocultos (consulte o capítulo 22).
Opções do construtor
Uma vez eu criei um construtor que leva dez argumentos. Era muito difícil de usar, pois ninguém conseguia se lembrar da ordem dos argumentos. Mais tarde, percebeu-se que ninguém estava usando o segundo argumento, eu queria removê-lo da lista de parâmetros, mas isso quebraria todo o código já desenvolvido.
Se eu fosse prudente, teria um construtor que aceita um objeto como parâmetro. Geralmente é extraído de um literal de objeto, mas pode vir de outras fontes, por exemplo, do conteúdo JSON.
Isso traria muitos benefícios.
- As linhas principais dão ao código uma aparência documentada. O código é mais fácil de ler porque indica qual é o argumento de cada chamador.
- Os argumentos podem ser organizados em qualquer ordem.
- No futuro, você pode adicionar novos argumentos sem danificar o código existente.
- Parâmetros irrelevantes podem ser ignorados.
Na maioria das vezes, um parâmetro é usado para inicializar uma propriedade privada. Isso é feito da seguinte maneira:
function my_little_constructor(spec) { let { name, mana_cost, colors, type, supertypes, types, subtypes, text, flavor, power, toughness, loyalty, timeshifted, hand, life } = spec;
Esse código cria e inicializa 15 variáveis privadas usando propriedades com os mesmos nomes das especificações. Se spec não tiver uma propriedade correspondente, uma nova variável será inicializada, à qual será atribuído o valor indefinido. Isso permite que você preencha todos os valores ausentes com os valores padrão.
Composição:
A expressividade e eficácia vívidas do JavaScript permitem criar programas no paradigma clássico, embora essa linguagem não se aplique aos clássicos. JavaScript também permite melhorias. Podemos trabalhar com uma composição funcional. Portanto, em vez de adicionar algo como uma exceção, você pode obter um pouco disso e daquilo. O construtor tem a seguinte aparência geral:
function my_little_constructor(spec) { let {} = spec; const _ = other_constructor(spec); const = function () {
Seu construtor pode chamar quantos outros construtores forem necessários para obter acesso ao gerenciamento de estado e ao comportamento que eles fornecem. Você pode até transmitir exatamente o mesmo objeto de especificação. Ao documentar os parâmetros de especificação, listamos as propriedades necessárias para my_little_constructor e as propriedades necessárias para outros construtores.
Às vezes, você pode simplesmente adicionar os métodos resultantes a um objeto congelado. Em outros casos, temos novos métodos que invocam os métodos recebidos. Isso garante que o código seja reutilizado, semelhante à herança, mas sem forte coesão. Uma chamada de função é o esquema de reutilização de código original e nada melhor foi inventado.
Tamanho
Com essa abordagem para construir um objeto, há mais memória envolvida do que quando se usa protótipos, pois cada objeto rígido contém todos os métodos do objeto e o objeto protótipo contém um link para o protótipo que contém os métodos. A diferença no consumo de memória é significativa? Comparando a diferença com as mais recentes conquistas no aumento da quantidade de memória, podemos dizer: não. Estamos acostumados a ler memória em kilobytes. E agora consideramos isso em gigabytes. Neste contexto, a diferença não é sentida.
A diferença pode ser reduzida melhorando a modularidade. A ênfase nas transações, não nas propriedades, permite reduzir o número de métodos e, ao mesmo tempo, melhorar a conectividade.
O modelo clássico é caracterizado pela uniformidade. Cada objeto deve ser uma instância de uma classe. JavaScript remove essas restrições. Nem todos os objetos precisam cumprir essas regras estritas.
Por exemplo, acredito que não faz sentido que os pontos sejam objetos necessariamente rígidos com métodos. Um ponto pode ser um contêiner simples para dois ou três números. Os pontos são passados para funções capazes de projeção, interpolação ou qualquer outra coisa que possa ser feita com pontos. Isso pode ser muito mais produtivo do que os pontos de subclassificação para proporcionar um comportamento especial. Deixe as funções funcionarem.
»Mais informações sobre o livro podem ser encontradas no
site do editor»
Conteúdo»
TrechoCupom de 25% para vendedores ambulantes -
JavaScriptApós o pagamento da versão impressa do livro, um livro eletrônico é enviado por e-mail.