portaldacalheta.pt
  • Principal
  • Noticias Do Mundo
  • Lucratividade E Eficiência
  • Ágil
  • Família
Processo Interno

Como um desenvolvedor JS, isso é o que me mantém acordado à noite



JavaScript é um idioma estranho. Embora inspirado em Smalltalk, ele usa uma sintaxe semelhante a C. Ele combina aspectos de paradigmas de programação procedural, funcional e orientada a objetos (OOP). Tem numeroso , muitas vezes redundantes, abordagens para resolver quase todos os problemas de programação concebíveis e não é fortemente opinativo sobre qual é o preferido. Tem uma tipagem fraca e dinâmica, com uma abordagem labiríntica de coerção de tipo que confunde até desenvolvedores experientes.

JavaScript também tem seus defeitos, armadilhas e recursos questionáveis. Novos programadores lutam com alguns de seus conceitos mais difíceis - pense em assincronicidade, encerramentos e içamento. Os programadores com experiência em outras linguagens presumem razoavelmente que coisas com nomes e aparências semelhantes funcionarão da mesma maneira em JavaScript e muitas vezes estão errados. Arrays não são realmente arrays; qual é o problema this, o que é um protótipo e o que significa new realmente faz?



O problema com as classes ES6

O pior criminoso, de longe, é novo na última versão de lançamento do JavaScript, ECMAScript 6 (ES6): Aulas . Algumas das conversas em torno das aulas são francamente alarmantes e revelam um mal-entendido profundamente enraizado de como a linguagem realmente funciona:



“JavaScript é finalmente um real linguagem orientada a objetos agora que tem classes! ”



Ou:

“As aulas nos livram de pensar sobre o modelo de herança quebrado do JavaScript.”



Ou ainda:

“As aulas são uma abordagem mais segura e fácil para criar tipos em JavaScript.”



Essas declarações não me incomodam porque implicam que há algo errado com herança prototípica ; vamos deixar de lado esses argumentos. Essas declarações me incomodam porque nenhuma delas é verdadeira e demonstram as consequências da abordagem 'tudo para todos' do JavaScript para o design da linguagem: isso prejudica a compreensão do programador da linguagem com mais frequência do que permite. Antes de prosseguir, vamos ilustrar.

JavaScript Pop Quiz nº 1: Qual é a diferença essencial entre esses blocos de código?

function PrototypicalGreeting(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } PrototypicalGreeting.prototype.greet = function() { return `${this.greeting}, ${this.name}!` } const greetProto = new PrototypicalGreeting('Hey', 'folks') console.log(greetProto.greet()) class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World') { this.greeting = greeting this.name = name } greet() { return `${this.greeting}, ${this.name}!` } } const classyGreeting = new ClassicalGreeting('Hey', 'folks') console.log(classyGreeting.greet())

A resposta aqui é não há um . Eles fazem efetivamente a mesma coisa, é apenas uma questão de saber se a sintaxe de classe ES6 foi usada.



modelo de documento de design técnico simples

É verdade que o segundo exemplo é mais expressivo. Só por essa razão, você pode argumentar que class é uma boa adição à linguagem. Infelizmente, o problema é um pouco mais sutil.

JavaScript Pop Quiz # 2: O que o código a seguir faz?

function Proto() { this.name = 'Proto' return this; } Proto.prototype.getName = function() { return this.name } class MyClass extends Proto { constructor() { super() this.name = 'MyClass' } } const instance = new MyClass() console.log(instance.getName()) Proto.prototype.getName = function() { return 'Overridden in Proto' } console.log(instance.getName()) MyClass.prototype.getName = function() { return 'Overridden in MyClass' } console.log(instance.getName()) instance.getName = function() { return 'Overridden in instance' } console.log(instance.getName())

A resposta correta é que ele imprime no console:



> MyClass > Overridden in Proto > Overridden in MyClass > Overridden in instance

Se você respondeu incorretamente, não entendeu o que class realmente é. Isso não é culpa sua. Muito parecido com Array, class não é um recurso de linguagem, é obscurantismo sintático . Ele tenta ocultar o modelo de herança prototípico e os idiomas desajeitados que o acompanham, e implica que o JavaScript está fazendo algo que não está.

Você deve ter ouvido que class foi apresentado ao JavaScript para tornar os desenvolvedores OOP clássicos vindos de linguagens como Java mais confortáveis ​​com o modelo de herança de classe ES6. Se vocês está um desses desenvolvedores, esse exemplo provavelmente o deixou horrorizado. Deveria. Mostra que JavaScript’s class palavra-chave não vem com nenhuma das garantias que uma classe deve fornecer. Ele também demonstra uma das principais diferenças no modelo de herança de protótipo: os protótipos são instâncias de objeto , não tipos .



Protótipos vs. Classes

A diferença mais importante entre herança baseada em classe e em protótipo é que uma classe define um tipo que pode ser instanciado em tempo de execução, enquanto um protótipo é em si uma instância de objeto.

Um filho de uma classe ES6 é outra tipo definição que estende o pai com novas propriedades e métodos, que por sua vez podem ser instanciados em tempo de execução. Um filho de um protótipo é outro objeto instância que delega ao pai quaisquer propriedades que não sejam implementadas no filho.

Observação lateral: você deve estar se perguntando por que mencionei os métodos de classe, mas não os métodos de protótipo. Isso porque o JavaScript não tem um conceito de métodos. Funções são primeira classe em JavaScript, e eles podem ter propriedades ou ser propriedades de outros objetos.

Um construtor de classe cria uma instância da classe. Um construtor em JavaScript é apenas uma função simples e antiga que retorna um objeto. A única coisa especial sobre um construtor JavaScript é que, quando chamado com new palavra-chave, ele atribui seu protótipo como o protótipo do objeto retornado. Se isso parece um pouco confuso para você, você não está sozinho - é, e é uma grande parte do motivo pelo qual os protótipos são mal compreendidos.

Para colocar um ponto muito bom nisso, um filho de um protótipo não é um cópia de de seu protótipo, nem é um objeto com o mesmo forma como seu protótipo. A criança tem uma referência viva para o protótipo e qualquer propriedade de protótipo que não exista no filho é uma referência unilateral a uma propriedade com o mesmo nome no protótipo.

Considere o seguinte:

let parent = { foo: 'foo' } let child = { } Object.setPrototypeOf(child, parent) console.log(child.foo) // 'foo' child.foo = 'bar' console.log(child.foo) // 'bar' console.log(parent.foo) // 'foo' delete child.foo console.log(child.foo) // 'foo' parent.foo = 'baz' console.log(child.foo) // 'baz' Observação: você quase nunca escreveria um código como este na vida real - é uma prática terrível - mas demonstra o princípio de forma sucinta.

No exemplo anterior, enquanto child.foo era undefined, referenciava parent.foo. Assim que definimos foo em child, child.foo tinha o valor 'bar', mas parent.foo reteve seu valor original. Uma vez que delete child.foo novamente se refere a parent.foo, o que significa que quando alteramos o valor do pai, child.foo refere-se ao novo valor.

Vamos ver o que aconteceu (para fins de ilustração mais clara, vamos fingir que são Strings e não literais de string, a diferença não importa aqui):

Percorrer a cadeia de protótipos para mostrar como as referências ausentes são tratadas no JavaScript.

A maneira como isso funciona nos bastidores e, especialmente, as peculiaridades de new e this, são um assunto para outro dia, mas a Mozilla tem um artigo completo sobre a cadeia de herança de protótipo de JavaScript se você gostaria de ler mais.

A principal conclusão é que os protótipos não definem um type; eles são eles próprios instances e eles são mutáveis ​​em tempo de execução, com tudo o que isso implica e acarreta.

Ainda comigo? Vamos voltar a dissecar as classes de JavaScript.

JavaScript Pop Quiz # 3: Como você implementa a privacidade nas aulas?

Nosso protótipo e propriedades de classe acima não são tanto 'encapsulados', mas 'pendurados precariamente pela janela'. Devíamos consertar isso, mas como?

Nenhum exemplo de código aqui. A resposta é que você não pode.

JavaScript não tem nenhum conceito de privacidade, mas tem encerramentos:

function SecretiveProto() { const secret = 'The Class is a lie!' this.spillTheBeans = function() { console.log(secret) } } const blabbermouth = new SecretiveProto() try { console.log(blabbermouth.secret) } catch(e) { // TypeError: SecretiveClass.secret is not defined } blabbermouth.spillTheBeans() // 'The Class is a lie!'

Você entende o que aconteceu? Se não, você não entende fechamentos. Tudo bem, realmente - eles não são tão intimidantes quanto parecem, são super úteis e você deveria reserve um tempo para aprender sobre eles .

JavaScript Pop Quiz # 4: Qual é o equivalente ao anterior usando o class Palavra-chave?

Desculpe, esta é outra pergunta capciosa. Você pode fazer basicamente a mesma coisa, mas tem a seguinte aparência:

class SecretiveClass { constructor() { const secret = 'I am a lie!' this.spillTheBeans = function() { console.log(secret) } } looseLips() { console.log(secret) } } const liar = new SecretiveClass() try { console.log(liar.secret) } catch(e) { console.log(e) // TypeError: SecretiveClass.secret is not defined } liar.spillTheBeans() // 'I am a lie!'

Deixe-me saber se isso parece mais fácil ou mais claro do que em SecretiveProto. Na minha opinião pessoal, é um pouco pior - quebra o uso idiomático de class declarações em JavaScript e não funciona muito como você esperava vindo de, digamos, Java. Isso ficará claro pelo seguinte:

Questionário pop de JavaScript nº 5: O que significa SecretiveClass::looseLips() Faz?

Vamos descobrir:

try { liar.looseLips() } catch(e) { // ReferenceError: secret is not defined }

Bem ... isso foi estranho.

asp net mvc web api

Questionário pop de JavaScript nº 6: o que os desenvolvedores de JavaScript experientes preferem - protótipos ou classes?

Você adivinhou, essa é outra pergunta capciosa - desenvolvedores de JavaScript experientes tendem a evitar os dois quando podem. Esta é uma boa maneira de fazer o acima com JavaScript idiomático:

function secretFactory() { const secret = 'Favor composition over inheritance, `new` is considered harmful, and the end is near!' const spillTheBeans = () => console.log(secret) return { spillTheBeans } } const leaker = secretFactory() leaker.spillTheBeans()

Não se trata apenas de evitar a feiúra inerente da herança ou impor o encapsulamento. Pense no que mais você pode fazer com secretFactory e leaker que você não poderia fazer facilmente com um protótipo ou uma classe.

Por um lado, você pode desestruturá-lo porque não precisa se preocupar com o contexto de this:

const { spillTheBeans } = secretFactory() spillTheBeans() // Favor composition over inheritance, (...)

Isso é muito bom. Além de evitar new e this tolice, ele nos permite usar nossos objetos de forma intercambiável com os módulos CommonJS e ES6. Também torna a composição um pouco mais fácil:

function spyFactory(infiltrationTarget) { return { exfiltrate: infiltrationTarget.spillTheBeans } } const blackHat = spyFactory(leaker) blackHat.exfiltrate() // Favor composition over inheritance, (...) console.log(blackHat.infiltrationTarget) // undefined (looks like we got away with it)

Clientes de blackHat não precisa se preocupar com onde exfiltrate veio de e spyFactory não tem que mexer com Function::bind malabarismo de contexto ou propriedades profundamente aninhadas. Veja bem, não precisamos nos preocupar muito com this em código procedural síncrono simples, mas causa todos os tipos de problemas em código assíncrono que é melhor evitar.

Com um pouco de pensamento, spyFactory poderia ser desenvolvido em uma ferramenta de espionagem altamente sofisticada que poderia lidar com todos os tipos de alvos de infiltração, ou em outras palavras, um fachada .

É claro que você também pode fazer isso com uma classe, ou melhor, uma variedade de classes, todas herdadas de um abstract class ou interface… exceto que o JavaScript não tem nenhum conceito de resumos ou interfaces.

Vamos voltar ao exemplo de saudação para ver como o implementaríamos com uma fábrica:

function greeterFactory(greeting = 'Hello', name = 'World') { return { greet: () => `${greeting}, ${name}!` } } console.log(greeterFactory('Hey', 'folks').greet()) // Hey, folks!

Você deve ter notado que essas fábricas estão ficando mais concisas à medida que avançamos, mas não se preocupe - elas fazem a mesma coisa. As rodinhas estão saindo, pessoal!

Isso já é menos clichê do que o protótipo ou a versão de classe do mesmo código. Em segundo lugar, ele atinge o encapsulamento de suas propriedades de forma mais eficaz. Além disso, ele tem menos memória e pegada de desempenho em alguns casos (pode não parecer à primeira vista, mas o compilador JIT está trabalhando silenciosamente nos bastidores para reduzir a duplicação e inferir tipos).

Portanto, é mais seguro, geralmente mais rápido e mais fácil de escrever código como este. Por que precisamos de aulas de novo? Oh, claro, capacidade de reutilização. O que acontece se quisermos variantes de saudação infelizes e entusiasmadas? Bem, se estivermos usando o ClassicalGreeting classe, provavelmente saltamos diretamente para o sonho de uma hierarquia de classes. Sabemos que precisaremos parametrizar a pontuação, então faremos uma pequena refatoração e adicionaremos alguns filhos:

// Greeting class class ClassicalGreeting { constructor(greeting = 'Hello', name = 'World', punctuation = '!') { this.greeting = greeting this.name = name this.punctuation = punctuation } greet() { return `${this.greeting}, ${this.name}${this.punctuation}` } } // An unhappy greeting class UnhappyGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, ' :(') } } const classyUnhappyGreeting = new UnhappyGreeting('Hello', 'everyone') console.log(classyUnhappyGreeting.greet()) // Hello, everyone :( // An enthusiastic greeting class EnthusiasticGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, '!!') } greet() { return super.greet().toUpperCase() } } const greetingWithEnthusiasm = new EnthusiasticGreeting() console.log(greetingWithEnthusiasm.greet()) // HELLO, WORLD!!

É uma boa abordagem, até que alguém apareça e peça um recurso que não se encaixe perfeitamente na hierarquia e tudo pare de fazer sentido. Coloque um alfinete nesse pensamento enquanto tentamos escrever a mesma funcionalidade com fábricas:

const greeterFactory = (greeting = 'Hello', name = 'World', punctuation = '!') => ({ greet: () => `${greeting}, ${name}${punctuation}` }) // Makes a greeter unhappy const unhappy = (greeter) => (greeting, name) => greeter(greeting, name, ':(') console.log(unhappy(greeterFactory)('Hello', 'everyone').greet()) // Hello, everyone :( // Makes a greeter enthusiastic const enthusiastic = (greeter) => (greeting, name) => ({ greet: () => greeter(greeting, name, '!!').greet().toUpperCase() }) console.log(enthusiastic(greeterFactory)().greet()) // HELLO, WORLD!!

Não é óbvio que este código seja melhor, embora seja um pouco mais curto. Na verdade, você pode argumentar que é mais difícil de ler e talvez esta seja uma abordagem obtusa. Não poderíamos apenas ter um unhappyGreeterFactory e um enthusiasticGreeterFactory?

Então seu cliente chega e diz: 'Preciso de um novo recepcionista que esteja infeliz e queira que toda a sala saiba disso!'

console.log(enthusiastic(unhappy(greeterFactory))().greet()) // HELLO, WORLD :(

Se precisássemos usar este saudador entusiasticamente infeliz mais de uma vez, poderíamos tornar mais fácil para nós mesmos:

const aggressiveGreeterFactory = enthusiastic(unhappy(greeterFactory)) console.log(aggressiveGreeterFactory('You're late', 'Jim').greet())

Existem abordagens para esse estilo de composição que funcionam com protótipos ou classes. Por exemplo, você pode repensar UnhappyGreeting e EnthusiasticGreeting Como decoradores . Ainda seria necessário mais clichê do que a abordagem de estilo funcional usada acima, mas esse é o preço que você paga pela segurança e encapsulamento de real Aulas.

O problema é que, em JavaScript, você não está obtendo essa segurança automática. Estruturas de JavaScript que enfatizam class o uso faz muita “mágica” para encobrir esses tipos de problemas e forçar as classes a se comportarem. Dê uma olhada no Polymer’s ElementMixin Código fonte algum tempo, eu te desafio. São os níveis de arcana do JavaScript de arquimagos, e quero dizer isso sem ironia ou sarcasmo.

alimentos inteiros são propriedade do walmart

Claro, podemos corrigir alguns dos problemas discutidos acima com Object.freeze ou Object.defineProperties com maior ou menor efeito. Mas por que imitar a forma sem a função, enquanto ignora as ferramentas JavaScript faz fornecer nativamente o que não podemos encontrar em linguagens como Java? Você usaria um martelo com o rótulo “chave de fenda” para apertar um parafuso, quando sua caixa de ferramentas tivesse uma chave de fenda real bem ao lado?

Encontrando as Boas Peças

Os desenvolvedores de JavaScript muitas vezes enfatizam as partes boas da linguagem, tanto coloquialmente quanto em referência a o livro de mesmo nome . Tentamos evitar as armadilhas estabelecidas por suas escolhas de design de linguagem mais questionáveis ​​e nos limitamos às partes que nos permitem escrever código limpo, legível, minimizador de erros e reutilizável.

Existem argumentos razoáveis ​​sobre quais partes do JavaScript se qualificam, mas espero ter convencido você de que class não é um deles. Caso contrário, espero que você entenda que a herança em JavaScript pode ser uma bagunça confusa e que class nem conserta nem poupa você de entender os protótipos. Crédito extra se você aprendeu as dicas de que os padrões de projeto orientados a objetos funcionam bem sem classes ou herança ES6.

Não estou dizendo para você evitar class inteiramente. Às vezes você precisa de herança e class fornece uma sintaxe mais limpa para fazer isso. Em particular, class X extends Y é muito mais agradável do que a abordagem de protótipo antigo. Além disso, muitos frameworks front-end populares encorajam seu uso e você provavelmente deve evitar escrever códigos estranhos fora do padrão apenas por princípio. Eu simplesmente não gosto de para onde isso vai dar.

Em meus pesadelos, toda uma geração de bibliotecas JavaScript é escrita usando class, com a expectativa de que se comporte de maneira semelhante a outras linguagens populares. Novas classes inteiras de bugs (trocadilhos) são descobertas. Os antigos são ressuscitados e poderiam facilmente ter sido deixados no Cemitério de JavaScript malformado se não tivéssemos caído descuidadamente no class armadilha. Desenvolvedores de JavaScript experientes são atormentados por esses monstros, porque o que é popular nem sempre é o que é bom.

Eventualmente, todos nós desistimos de frustração e começamos a reinventar rodas em Rust, Go, Haskell ou quem sabe o que mais, e então compilar para Wasm para a web, e novas bibliotecas e estruturas da web proliferam em um infinito multilíngue.

Isso realmente me mantém acordado à noite.

Compreender o básico

O que é ECMAScript 6?

ES6 é a implementação estável mais recente do ECMAScript, o padrão aberto no qual o JavaScript é baseado. Ele adiciona uma série de novos recursos à linguagem, incluindo um sistema de módulo oficial, variáveis ​​e constantes com escopo de bloco, funções de seta e várias outras novas palavras-chave, sintaxes e objetos integrados.

ES6 é a versão ECMAScript mais recente?

ES6 (ES2015) é o padrão mais recente estável e totalmente implementado (exceto para chamadas finais adequadas e algumas nuances) nas versões mais recentes dos principais navegadores e outros ambientes JS. ES7 (ES2016) e ES8 (ES2017) também são especificações estáveis, mas a implementação é bastante mista.

O ES6 é orientado a objetos?

JavaScript tem forte suporte para programação orientada a objetos, mas usa um modelo de herança diferente (prototípico) em comparação com as linguagens OO mais populares (que usam herança clássica). Ele também oferece suporte a estilos de programação procedural e funcional.

O que são classes ES6?

No ES6, a palavra-chave 'class' e os recursos associados são uma nova abordagem para a criação de construtores de protótipo. Eles não são classes verdadeiras de uma maneira que seria familiar aos usuários da maioria das outras linguagens orientadas a objetos.

Quais palavras-chave podem ser usadas para implementar herança no ES6?

Pode-se implementar herança em JavaScript ES6 por meio das palavras-chave 'class' e 'extends'. Outra abordagem é por meio do idioma da função 'construtor' mais a atribuição de funções e propriedades estáticas ao protótipo do construtor.

O que é herança de protótipo em JavaScript?

Na herança prototípica, os protótipos são instâncias de objetos às quais as instâncias filhas delegam propriedades indefinidas. Em contraste, as classes na herança clássica são definições de tipo, a partir das quais as classes filhas herdam métodos e propriedades durante a instanciação.

Complexo, mas integral: uma visão geral das cachoeiras imobiliárias

Investidores E Financiamento

Complexo, mas integral: uma visão geral das cachoeiras imobiliárias
A lista definitiva dos 50 melhores plug-ins de Sketch

A lista definitiva dos 50 melhores plug-ins de Sketch

Ferramentas E Tutoriais

Publicações Populares
Quem, o quê e por quê - um guia para métodos de teste do usuário
Quem, o quê e por quê - um guia para métodos de teste do usuário
Aflição financeira em uma crise: você não pode prever, você pode se preparar
Aflição financeira em uma crise: você não pode prever, você pode se preparar
Bangladesh condena seis militantes à morte por matar dois ativistas gays
Bangladesh condena seis militantes à morte por matar dois ativistas gays
Mães solteiras na arte de criar filhos sozinhas
Mães solteiras na arte de criar filhos sozinhas
Vender uma empresa para valor máximo em um mercado desafiador de fusões e aquisições
Vender uma empresa para valor máximo em um mercado desafiador de fusões e aquisições
 
Robo-conselheiro Risco de portfólio da indústria: eficiência ou redução de cantos?
Robo-conselheiro Risco de portfólio da indústria: eficiência ou redução de cantos?
EUA: corrida para prefeito de Honolulu segue para segundo turno
EUA: corrida para prefeito de Honolulu segue para segundo turno
13 podcasts que todo designer deve ouvir
13 podcasts que todo designer deve ouvir
Vazamentos de Panama Papers podem dar a Sanders algum poder de fogo contra o rival Clinton
Vazamentos de Panama Papers podem dar a Sanders algum poder de fogo contra o rival Clinton
Com 21 anos e raízes de Kerala, é o mais jovem na lista de MBE do Queen
Com 21 anos e raízes de Kerala, é o mais jovem na lista de MBE do Queen
Publicações Populares
  • converter data para milissegundos javascript
  • linguagens de programação orientadas a objetos não são mais usadas por desenvolvedores de software
  • selecione a opção que representa uma tecnologia vestível.
  • como escrever algoritmo de aprendizado de máquina
  • problemas e soluções de segurança de aplicativos da web
Categorias
  • Noticias Do Mundo
  • Lucratividade E Eficiência
  • Ágil
  • Família
  • © 2022 | Todos Os Direitos Reservados

    portaldacalheta.pt