0% acharam este documento útil (0 voto)
6 visualizações236 páginas

Padrões de JavaScript por Stoyan Stefanov

O livro 'Padrões JavaScript' de Stoyan Stefanov aborda soluções para problemas comuns em JavaScript, focando em práticas recomendadas e padrões que ajudam desenvolvedores a escrever código mais sustentável e robusto. Destinado a programadores experientes, ele explora conceitos como criação de objetos, funções, padrões de projeto e manipulação do DOM. O autor enfatiza a importância de padrões para melhorar a comunicação e a eficiência no desenvolvimento de software.
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
6 visualizações236 páginas

Padrões de JavaScript por Stoyan Stefanov

O livro 'Padrões JavaScript' de Stoyan Stefanov aborda soluções para problemas comuns em JavaScript, focando em práticas recomendadas e padrões que ajudam desenvolvedores a escrever código mais sustentável e robusto. Destinado a programadores experientes, ele explora conceitos como criação de objetos, funções, padrões de projeto e manipulação do DOM. O autor enfatiza a importância de padrões para melhorar a comunicação e a eficiência no desenvolvimento de software.
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Machine Translated by Google [Link]-ebooks.

info
Machine Translated by Google [Link]
Machine Translated by Google [Link]

Padrões JavaScript
Machine Translated by Google [Link]
Machine Translated by Google [Link]

Padrões JavaScript

Stoyan Stefanov

Pequim Cambridge Farnham Colônia Sebastopol Tóquio


Machine Translated by Google [Link]

Padrões JavaScript
por Stoyan Stefanov

Copyright © 2010 Yahoo!, Inc.. Todos os direitos reservados.


Impresso nos Estados Unidos da América.

Publicado pela O'Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472.

Os livros da O'Reilly podem ser adquiridos para uso educacional, comercial ou promocional. Edições online também estão
disponíveis para a maioria dos títulos (http:// [Link]). Para mais informações, entre em contato com
nosso departamento de vendas corporativas/institucionais: (800) 998-9938 ou corporate@[Link].

Editora: Mary Treseler Indexador: Potomac Indexing, LLC


Editora de produção: Teresa Elsey Designer de capa: Karen Montgomery
Editora de texto: ContentWorks, Inc. Designer de interiores: David Futato
Revisora: Teresa Elsey Ilustrador: Robert Romano

Histórico de
impressão: setembro de 2010: primeira edição.

Nutshell Handbook, o logotipo do Nutshell Handbook e o logotipo da O'Reilly são marcas registradas da O'Reilly Media, Inc. Padrões
JavaScript, a imagem de uma perdiz europeia e a imagem comercial relacionada são marcas registradas da O'Reilly Media, Inc.

Muitas das designações usadas por fabricantes e vendedores para distinguir seus produtos são reivindicadas como marcas registradas.
Nos casos em que essas designações aparecem neste livro, e a O'Reilly Media, Inc. tinha conhecimento de uma reivindicação de marca
registrada, as designações foram impressas em letras maiúsculas ou com as iniciais maiúsculas.

Embora todas as precauções tenham sido tomadas na preparação deste livro, a editora e o autor não assumem nenhuma
responsabilidade por erros ou omissões, ou por danos resultantes do uso das informações aqui contidas.

ISBN: 978-0-596-80675-0

[SB]

1284038177
Machine Translated by Google [Link]

Para minhas meninas: Eva, Zlatina e Nathalie


Machine Translated by Google [Link]
Machine Translated by Google [Link]

Índice

Prefácio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii

1. Introdução . ... 1
Padrões 1
JavaScript: Conceitos 3
Orientado a Objetos 3
Sem aulas 4
Protótipos 4
Ambiente 5
ECMAScript 5 5
JSLint 6
O Console 6

2. Essenciais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Escrevendo código sustentável 9
Minimizando Globais 10
O problema com os globais 11
Efeitos colaterais ao esquecer var 12
Acesso ao Objeto Global 13
Padrão de variável única 13
Içamento: Um problema com vars dispersos e 14
loops for-in 15
17
(Não) Aumentando Protótipos Integrados 19
Padrão de troca 20
Evitando a conversão implícita de tipos 21
Evitando eval() 21
Conversões de números com parseInt() 23
Convenções de codificação 23
Recuo 24
Aparelhos Curly 24

vii
Machine Translated by Google [Link]

Localização da chave de abertura 25

Espaço em branco 26

Convenções de nomenclatura 28

Construtores de Capitalização 28

Separando Palavras 28

Outros padrões de nomenclatura 29

Escrevendo Comentários 30

Escrevendo documentos de API 30

Exemplo de YUIDoc 31

Escrevendo para ser lido 34


Revisões por pares 35

Minificar…Resumo do JSLint 36
em execução 37
de produção 37

3. Literais e Construtores . ...


Objeto Literal 39

A Sintaxe Literal do Objeto 40


Objetos de um Construtor 41

Construtor de Objeto Catch 41


Funções de construtor personalizadas 42
Valores de retorno do construtor 43

Padrões para a aplicação de novos 44

Convenção de Nomenclatura 45

Usando isso 45

Construtor de auto-invocação 46

Literal de matriz 46

Sintaxe literal de matriz 47


Curiosidade sobre o construtor de array 47
Verifique a existência de matriz 48
JSON 49

Trabalhando com JSON 49

Expressão regular literal 50

Sintaxe Literal de Expressão Regular 51

Envoltórios primitivos 52
Objetos de erro 53

Resumo 54

4. Funções . ...
Fundo 57
Desambiguação da Terminologia 58

Declarações versus expressões: nomes e içamento 59

Nome da função Propriedade 60

viii | Índice
Machine Translated by Google [Link]

Função de elevação 61
Padrão de retorno de chamada 62
Um exemplo de retorno de chamada 63
Retornos de chamada e escopo 64
Ouvintes de eventos assíncronos 66
Tempos limite 66
Retornos de chamada em bibliotecas 67
Retornando funções 67
Funções autodefinidas 68
Funções Imediatas 69
Parâmetros de uma Função Imediata 70
Valores retornados de funções imediatas 71
Benefícios e uso 72
Inicialização Imediata de Objetos 73
Ramificação em tempo de inicialização 74
Propriedades de Função — Um Padrão de Memorização 76
Objetos de configuração 77
Caril 79
Aplicação de Função 79
Aplicação parcial 80
Currying 81
Quando usar currying 83
Resumo 84

5. Padrões de Criação de Objetos . ...


Padrão de namespace 87
Função de namespace de uso geral 89
Declarando Dependências 90
Propriedades e Métodos Privados 92
Membros Privados 92
Métodos Privilegiados 93
Falhas de privacidade 93
Literais de Objetos e Privacidade 94
Protótipos e Privacidade 95
Revelando funções privadas como métodos públicos 96
Padrão de módulo 97
Revelando o Padrão do Módulo 99
Módulos que criam construtores 100
Importando Globais para um Módulo 101
Padrão Sandbox 101
Um construtor global 101
Adicionando Módulos 103
Implementando o Construtor 104

Índice | ix
Machine Translated by Google [Link]

Membros estáticos 105


Membros Estáticos Públicos 105
Membros Estáticos Privados 107
Constantes de Objeto 109
Padrão de encadeamento 110
Prós e contras do método Chaining Pattern 111
method() 112
Resumo 113

6. Padrões de reutilização de código . ...


Padrões de herança clássicos versus modernos 115
Resultado esperado ao usar herança clássica 116
Padrão Clássico #1 — O Padrão Padrão 117
Seguindo a Cadeia de Protótipos 117
Desvantagens ao usar o padrão nº 1 119
Padrão Clássico #2 — Aluguel de Construtor 120
A Cadeia de Protótipos 121
Herança Múltipla por Empréstimo de Construtores 122
Prós e contras do padrão de construtor de empréstimo 123
Padrão Clássico #3 — Protótipo de Aluguel e Configuração 123
Padrão Clássico #4 — Compartilhe o Protótipo 124
Padrão Clássico #5 — Um Construtor Temporário 125
Armazenando a Superclasse 126
Redefinindo o ponteiro do construtor 127
Classe 128
Herança Prototípica 130
Discussão 132
Adição ao ECMAScript 5 132
Herança por Cópia de Propriedades 133
Exemplo 135
de métodos de empréstimo 136
de mix-ins: Emprestar de array 137
Emprestar e vincular 137
[Link]() 138
Resumo 139

7. Padrões de projeto . ... 141


Solteiro 141
Usando novo 142
Instância em uma propriedade estática 143
Instância em um fechamento 144
Fábrica 146
Fábrica de Objetos Integrada 148

x | Índice
Machine Translated by Google [Link]

Iterador 149
Decorador 151
Uso 151
Implementação 151
Implementação usando uma lista 154
Estratégia 155
Exemplo de validação de dados 156
Fachada 158
Procuração 159
Um exemplo 160
Proxy como um cache 167
Mediador 167
Exemplo de Mediador 168
Observador 171
Exemplo nº 1: Assinaturas de revistas 171
Exemplo nº 2: O jogo do Keypress 175
Resumo 178

8. DOM e padrões de navegador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181


Separação de Preocupações 181
Scripting DOM 183
Acesso DOM 183
Manipulação DOM 184
Eventos 185
Tratamento de Eventos 186
Delegação de Eventos 188
Roteiros de longa duração 189
setTimeout() 189
Trabalhadores da Web 190

Scripting Remoto 190


XMLHttpRequest 191
JSONP 192
Molduras e Beacons de Imagem 195
Implementando JavaScript 196
Combinando scripts 196
Minificando e compactando o 197
cabeçalho Expires 197
Usando uma 197
CDN Estratégias de 198
carregamento O lugar do elemento <script> 199
Fragmentação HTTP 200
Elemento <script> dinâmico para downloads não bloqueados 201
Carregamento lento 203

Índice | xi
Machine Translated by Google [Link]

Carregamento sob demanda 203


Pré-carregamento de JavaScript 205
Resumo 206

Índice . ...

xii | Índice
Machine Translated by Google [Link]

Prefácio

Padrões são soluções para problemas comuns. Um passo adiante, padrões são modelos para resolver
categorias de problemas.

Os padrões ajudam você a dividir um problema em blocos semelhantes aos de Lego e a focar nas partes
específicas do problema, ao mesmo tempo em que abstrai muitos detalhes do tipo "já passei por isso, fiz
isso, tenho a camiseta".

Os padrões também nos ajudam a nos comunicar melhor, simplesmente fornecendo um vocabulário comum.

Portanto, é importante identificar e estudar padrões.

Público-alvo
Este livro não é para iniciantes; ele é direcionado a desenvolvedores e programadores profissionais que
desejam levar suas habilidades em JavaScript para o próximo nível.

Alguns conceitos básicos (como laços, condicionais e fechamentos) não são abordados. Se você precisar
revisar alguns desses tópicos, consulte a lista de leituras sugeridas.

Ao mesmo tempo, alguns tópicos (como criação ou elevação de objetos) podem parecer básicos demais
para estar neste livro, mas são discutidos a partir de uma perspectiva de padrões e, na minha opinião, são
essenciais para aproveitar o poder da linguagem.

Se você está procurando práticas recomendadas e padrões poderosos para ajudá-lo a escrever um código
JavaScript melhor, mais sustentável e robusto, este livro é para você.

Convenções usadas neste livro


As seguintes convenções tipográficas são usadas neste livro:

Itálico
indica novos termos, URLs, endereços de e-mail, nomes de arquivos e extensões de arquivo.

xiii
Machine Translated by Google [Link]

Largura constante

Usada para listagens de programas, bem como dentro de parágrafos para se referir a elementos do programa, como
nomes de variáveis ou funções, bancos de dados, tipos de dados, variáveis de ambiente, instruções e palavras-chave.

Largura constante em

negrito Mostra comandos ou outro texto que deve ser digitado literalmente pelo usuário.

Itálico de largura constante

Mostra o texto que deve ser substituído por valores fornecidos pelo usuário ou por valores determinados
pelo contexto.

Este ícone significa uma dica, sugestão ou observação geral.

Este ícone significa um aviso ou cuidado.

Usando exemplos de código


Este livro está aqui para ajudá-lo a realizar seu trabalho. Em geral, você pode usar o código deste livro em
seus programas e documentação. Não é necessário entrar em contato conosco para obter permissão, a
menos que esteja reproduzindo uma parte significativa do código. Por exemplo, escrever um programa que
use vários trechos de código deste livro não requer permissão. Vender ou distribuir um CD-ROM com exemplos
dos livros da O'Reilly exige permissão. Responder a uma pergunta citando este livro e citando o código de
exemplo não requer permissão. Incorporar uma quantidade significativa de código de exemplo deste livro na
documentação do seu produto exige permissão.

Agradecemos, mas não exigimos, a atribuição. Uma atribuição geralmente inclui o título, autor, editora e ISBN.
Por exemplo: “Padrões JavaScript, por Stoyan Stefanov (O'Reilly). Copyright 2010 Yahoo!, Inc., 9780596806750.”

Se você acha que o uso de exemplos de código não se enquadra no uso justo ou na permissão concedida aqui, sinta-se à
vontade para entrar em contato conosco pelo e-mail permissions@[Link].

Safari® Livros Online

O Safari Books Online é uma biblioteca digital sob demanda que permite que você pesquise
facilmente mais de 7.500 livros e vídeos de referência sobre tecnologia e criatividade para
encontrar as respostas que precisa rapidamente.

xiv | Prefácio
Machine Translated by Google [Link]

Com uma assinatura, você pode ler qualquer página e assistir a qualquer vídeo da nossa biblioteca on-line.
Leia livros no seu celular e dispositivos móveis. Acesse novos títulos antes que eles sejam disponibilizados
para impressão, tenha acesso exclusivo aos manuscritos em desenvolvimento e publique feedback para
os autores. Copie e cole exemplos de código, organize seus favoritos, baixe capítulos, marque seções
importantes, crie notas, imprima páginas e aproveite vários outros recursos que economizam tempo.

A O'Reilly Media disponibilizou este livro no serviço Safari Books Online. Para ter acesso digital completo
a este e outros livros sobre temas semelhantes da O'Reilly e de outras editoras, inscreva-se gratuitamente
em http:// [Link].

Como entrar em contato conosco

Por favor, envie comentários e perguntas sobre este livro à editora: O'Reilly Media, Inc.

1005 Gravenstein Highway North


Sebastopol, CA 95472
800-998-9938 (nos Estados Unidos ou Canadá)
707-829-0515 (internacional ou local)
707-829-0104 (fax)

Temos uma página web para este livro, onde listamos erratas, exemplos e qualquer informação adicional.
Você pode acessá-la em:

http:// [Link]/ catalog/ 9780596806750 Para

comentar ou fazer perguntas técnicas sobre este livro, envie um e-mail para:

bookquestions@[Link]

Para obter mais informações sobre nossos livros, conferências, Centros de Recursos e
O'Reilly Network, visite nosso site em:

http:// [Link]

Agradecimentos
Sou eternamente grato aos incríveis revisores que compartilharam sua energia e conhecimento
para criar um livro muito melhor para o bem da comunidade. Seus blogs e feeds do Twitter são uma
fonte constante de admiração, observações perspicazes, ótimas ideias e padrões.

• Dmitry Soshnikov (http:// [Link], @DmitrySoshnikov) • Andrea Giammarchi

(http:// [Link], @WebReflection) • Asen Bozhilov (http:// [Link],

@abozhilov) • Juriy Zaytsev (http:// [Link], @kangax)

Prefácio | xv
Machine Translated by Google [Link]

• Ryan Grove (http:// [Link], @yaypie) • Nicholas


Zakas (http:// [Link], @slicknet) • Remy Sharp (http://
[Link], @rem) • Iliyan Peychev

Créditos
Alguns dos padrões do livro foram identificados pelo autor, com base em sua experiência e em estudos
de bibliotecas JavaScript populares, como jQuery e YUI. Mas a maioria dos padrões é identificada e
descrita pela comunidade JavaScript; portanto, este livro é resultado do trabalho coletivo de muitos
desenvolvedores. Para não interromper a narrativa com a história e os créditos, uma lista de referências
e sugestões de leitura adicional estão disponíveis no site que acompanha o livro, em http://
[Link]/ book/ reading/.

Se eu esqueci de algum artigo bom e original na lista de referências, por favor, aceite minhas desculpas
e entre em contato comigo para que eu possa adicioná-lo à lista on-line em http:// [Link].

Leitura
Este não é um livro para iniciantes e alguns tópicos básicos, como laços e condições, foram ignorados.
Se você precisa aprender mais sobre a linguagem, aqui estão algumas sugestões de títulos:

• JavaScript orientado a objetos por mim (Packt Publishing) • JavaScript: o


guia definitivo por David Flanagan (O'Reilly) • JavaScript: as partes boas por
Douglas Crockford (O'Reilly) • Padrões de design Pro JavaScript por Ross
Hermes e Dustin Diaz (Apress) • JavaScript de alto desempenho por Nicholas Zakas
(O'Reilly) • JavaScript profissional para desenvolvedores web por Nicholas
Zakas (Wrox)

xvi | Prefácio
Machine Translated by Google [Link]

CAPÍTULO 1

Introdução

JavaScript é a linguagem da web. Começou como uma forma de manipular alguns tipos selecionados
de elementos em uma página web (como imagens e campos de formulário), mas cresceu enormemente.
Além da criação de scripts para navegadores do lado do cliente, hoje em dia é possível usar JavaScript
para programar para uma variedade cada vez maior de plataformas. Você pode escrever código do
lado do servidor (usando .NET ou [Link]), aplicativos desktop (que funcionam em todos os sistemas
operacionais) e extensões de aplicativos (por exemplo, para Firefox ou Photoshop), aplicativos mobile
e scripts de linha de comando.

JavaScript também é uma linguagem incomum. Ela não possui classes, e funções são objetos de
primeira classe usados para diversas tarefas. Inicialmente, a linguagem foi considerada deficiente por
muitos desenvolvedores, mas nos últimos anos essa percepção mudou. Curiosamente, linguagens
como Java e PHP começaram a adicionar recursos como closures e funções anônimas, que os
desenvolvedores JavaScript já apreciavam e consideravam garantidos há algum tempo.

JavaScript é dinâmico o suficiente para que você possa fazer com que ele se pareça com outra
linguagem com a qual já esteja familiarizado. Mas a melhor abordagem é aceitar suas diferenças e
estudar seus padrões específicos.

Padrões
Um padrão no sentido mais amplo da palavra é um “tema de eventos ou objetos recorrentes… pode
ser um modelo ou modelo que pode ser usado para gerar coisas” (http:// [Link] .org/ wiki/ Pattern).

No desenvolvimento de software, um padrão é uma solução para um problema comum. Um padrão


não é necessariamente uma solução de código pronta para copiar e colar, mas sim uma prática
recomendada, uma abstração útil e um modelo para resolver categorias de problemas.

1
Machine Translated by Google [Link]

É importante identificar padrões porque:

• Eles nos ajudam a escrever códigos melhores usando práticas comprovadas e não reinventar a roda. • Eles

fornecem um nível de abstração — o cérebro pode conter apenas uma certa quantidade de informações em
um determinado momento, então, quando você pensa em um problema mais complexo, ajuda se você
não se preocupar com os detalhes de baixo nível, mas contabilizá-los com blocos de construção
autocontidos (padrões).

• Eles melhoram a comunicação entre desenvolvedores e equipes, que muitas vezes estão em locais remotos
e não se comunicam pessoalmente. Simplesmente rotular alguma técnica ou abordagem de codificação
torna mais fácil garantir que estamos falando da mesma coisa. Por exemplo, é mais fácil dizer (e pensar)
"função imediata" do que "essa coisa em que você envolve a função entre parênteses e, no final, coloca
outro parênteses para invocar a função exatamente onde você a definiu".

multou-o.”

Este livro discute os seguintes tipos de padrões:

• Padrões de design •

Padrões de codificação

• Antipadrões

Padrões de projeto são aqueles inicialmente definidos pelo livro "Gang of Four" (nome dado em homenagem
aos seus quatro autores), publicado originalmente em 1994 sob o título Design Patterns: Elements of Reusable
Object-Oriented Software. Exemplos de padrões de projeto são singleton, factory, decorator, observer e assim
por diante. A questão dos padrões de projeto em relação ao JavaScript é que, embora independentes de
linguagem, os padrões de projeto foram estudados principalmente sob a perspectiva de linguagens fortemente
tipadas, como C++ e Java.
Às vezes, não faz necessariamente sentido aplicá-los literalmente em uma linguagem dinâmica de tipagem
flexível, como JavaScript. Às vezes, esses padrões são soluções alternativas que lidam com a natureza
fortemente tipada das linguagens e a herança baseada em classes.
Em JavaScript, pode haver alternativas mais simples. Este livro discute implementações em JavaScript de
vários padrões de design no Capítulo 7.

Os padrões de codificação são muito mais interessantes; são padrões específicos de JavaScript e boas
práticas relacionadas aos recursos exclusivos da linguagem, como os diversos usos de funções. Os padrões
de codificação JavaScript são o tema principal do livro.

Você pode encontrar um antipadrão ocasionalmente no livro. Antipadrões têm um som um pouco negativo ou
até mesmo ofensivo em seu nome, mas não precisa ser assim. Um antipadrão não é o mesmo que um bug ou
um erro de codificação; é apenas uma abordagem comum que causa mais problemas do que resolve. Os
antipadrões são claramente identificados com um comentário no código.

2 | Capítulo 1: Introdução
Machine Translated by Google [Link]

JavaScript: Conceitos
Vamos revisar rapidamente alguns conceitos importantes que fornecem um contexto para os próximos
capítulos.

JavaScript Orientado

a Objetos é uma linguagem orientada a objetos, o que frequentemente surpreende desenvolvedores


que já a examinaram e a descartaram. Qualquer coisa que você veja em um trecho de código
JavaScript tem uma boa chance de ser um objeto. Apenas cinco tipos primitivos não são objetos:
número, string, booleano, nulo e indefinido, e os três primeiros têm representação de objeto
correspondente na forma de wrappers primitivos (discutidos no próximo capítulo). Valores primitivos
de número, string e booleano são facilmente convertidos em objetos pelo programador ou, às vezes,
em segundo plano, pelo interpretador JavaScript.

Funções também são objetos. Elas podem ter propriedades e métodos.

A coisa mais simples que se faz em qualquer linguagem é definir uma variável. Bem, em JavaScript,
ao definir uma variável, você já está lidando com objetos. Primeiro, a variável se torna automaticamente
uma propriedade de um objeto interno conhecido como Objeto de Ativação (ou uma propriedade do
objeto global, se for uma variável global). Segundo, essa variável também é, na verdade, semelhante
a um objeto, pois possui suas próprias propriedades (chamadas atributos), que determinam se a
variável pode ser alterada, excluída ou enumerada em um loop for-in . Esses atributos não são
expostos diretamente no ECMAScript 3, mas a edição 5 oferece métodos de descrição especiais
para manipulá-los.

Então, o que são esses objetos? Como eles fazem tantas coisas, devem ser bem especiais.
Na verdade, são extremamente simples. Um objeto é apenas uma coleção de propriedades nomeadas,
uma lista de pares chave-valor (quase idêntico a um array associativo em outras linguagens).
Algumas das propriedades podem ser funções (objetos de função), nesse caso as chamamos de
métodos.

Outra coisa sobre os objetos que você cria é que você pode modificá-los a qualquer momento.
(Embora o ECMAScript 5 introduza APIs para evitar mutações.) Você pode pegar um objeto e
adicionar, remover e atualizar seus membros. Se você se preocupa com privacidade e acesso,
veremos padrões para isso também.

E uma última coisa a ter em mente é que existem dois tipos principais de objetos:

Nativo
Descrito no padrão ECMAScript
Hospedar

Definido pelo ambiente do host (por exemplo, o ambiente do navegador)

Os objetos nativos podem ser categorizados como integrados (por exemplo, Array, Date) ou definidos
pelo usuário (var o = {};).

JavaScript: Conceitos | 3
Machine Translated by Google [Link]

Objetos host são, por exemplo, window e todos os objetos DOM. Se você está se perguntando se
está usando objetos host, tente executar seu código em um ambiente diferente, sem navegador.
Se funcionar bem, você provavelmente está usando apenas objetos nativos.

Sem aulas
Você verá esta afirmação repetida em diversas ocasiões ao longo do livro: Não há classes em
JavaScript. Este é um conceito novo para programadores experientes em outras linguagens, e são
necessárias mais do que algumas repetições e um esforço considerável para "desaprender" classes
e aceitar que JavaScript lida apenas com objetos.

Não ter classes torna seus programas mais curtos — você não precisa ter uma classe para criar
um objeto. Considere esta criação de objeto semelhante à de Java:
// Criação de objetos Java
OláOO olá_oo = new OláOO();

Repetir a mesma coisa três vezes parece um peso extra quando se trata de criar objetos simples.
E, na maioria das vezes, queremos manter nossos objetos simples.

Em JavaScript, você cria um objeto em branco quando precisa de um e começa a adicionar


membros interessantes a ele. Você compõe objetos adicionando primitivos, funções ou outros
objetos a eles como suas propriedades. Um objeto "em branco" não é totalmente em branco; ele já
vem com algumas propriedades internas, mas não possui propriedades "próprias". Falaremos mais
sobre isso no próximo capítulo.

Uma das regras gerais do livro da Gangue dos Quatro diz: "Prefira a composição de objetos à
herança de classes". Isso significa que, se você puder criar objetos a partir de partes disponíveis
que você tem por aí, essa é uma abordagem muito melhor do que criar longas cadeias e
classificações de herança pai-filho. Em JavaScript, é fácil seguir esse conselho — simplesmente
porque não existem classes e a composição de objetos é o que você faz de qualquer maneira.

Protótipos

JavaScript possui herança, embora esta seja apenas uma maneira de reutilizar código. (E há um
capítulo inteiro sobre reutilização de código.) A herança pode ser realizada de várias maneiras, que
geralmente fazem uso de protótipos. Um protótipo é um objeto (sem surpresas) e cada função que
você cria recebe automaticamente uma propriedade de protótipo que aponta para um novo objeto
em branco. Este objeto é quase idêntico a um objeto criado com um literal de objeto ou construtor
Object() , exceto que sua propriedade de construtor aponta para a função que você cria e não para
o Object() interno . Você pode adicionar membros a este objeto em branco e, posteriormente, fazer
com que outros objetos herdem deste objeto e usem suas propriedades como se fossem suas.

Discutiremos herança em detalhes, mas por enquanto tenha em mente que o protótipo é um objeto
(não uma classe ou algo especial) e cada função tem uma propriedade de protótipo .

4 | Capítulo 1: Introdução
Machine Translated by Google [Link]

Ambiente
Programas JavaScript precisam de um ambiente para rodar. O habitat natural de um programa JavaScript é
o navegador, mas este não é o único ambiente. Os padrões neste livro estão principalmente relacionados
ao núcleo do JavaScript (ECMAScript), portanto, são independentes de ambiente. As exceções são:

• Capítulo 8, que trata especificamente de padrões de navegador • Alguns

outros exemplos que ilustram aplicações práticas de um padrão

Os ambientes podem fornecer seus próprios objetos de host, que não são definidos no padrão ECMA-
Script e podem ter comportamento não especificado e inesperado.

ECMAScript 5
A linguagem de programação principal do JavaScript (excluindo DOM, BOM e objetos extras do host) é
baseada no padrão ECMAScript , ou ES, para abreviar. A versão 3 do padrão foi aceita oficialmente em
1999 e é a atualmente implementada em todos os navegadores.
A versão 4 foi abandonada e a versão 5 foi aprovada em dezembro de 2009, 10 anos após a anterior.

A versão 5 adiciona alguns novos objetos, métodos e propriedades integrados à linguagem, mas sua adição
mais importante é o chamado modo estrito, que, na verdade, remove recursos da linguagem, tornando os
programas mais simples e menos propensos a erros. Por exemplo, o uso da instrução with tem sido
contestado ao longo dos anos. Agora, no modo estrito do ES5, ele gera um erro, embora não haja problema
se encontrado no modo não estrito. O modo estrito é acionado por uma string comum, que implementações
mais antigas da linguagem simplesmente ignoram. Isso significa que o uso do modo estrito é compatível
com versões anteriores, pois não gerará erros em navegadores mais antigos que não o compreendem.

Uma vez por escopo (escopo de função, escopo global ou no início de uma string passada para eval()),
você pode usar a seguinte string:

function my()
{ "use strict"; //
resto da função...
}

Isso significa que o código na função é executado no subconjunto estrito da linguagem. Em navegadores
mais antigos, trata-se apenas de uma string não atribuída a nenhuma variável, portanto, não é usada, mas
ainda assim não é um erro.

O plano para a linguagem é que no futuro o modo estrito seja o único permitido.
Nesse sentido, o ES5 é uma versão de transição — os desenvolvedores são encorajados, mas não
forçados, a escrever código que funcione no modo estrito.

ECMAScript 5 |5
Machine Translated by Google [Link]

O livro não explora padrões relacionados às adições específicas do ES5, pois, no momento em que este livro
foi escrito, não havia nenhum navegador que implementasse o ES5. Mas os exemplos neste livro promovem
uma transição para o novo padrão por meio de:

• Garantir que os exemplos de código oferecidos não gerarão erros no modo estrito • Evitar e

apontar construções obsoletas, como [Link] • Chamar padrões ES3 que têm equivalentes

integrados ES5, como [Link]()

JSLint
JavaScript é uma linguagem interpretada sem verificações estáticas em tempo de compilação. Portanto, é
possível implementar um programa quebrado com um simples erro de digitação sem perceber. É aqui que o
JSLint ajuda.

JSLint (http:// [Link]) é uma ferramenta de qualidade de código JavaScript criada por Douglas Crockford que
inspeciona seu código e alerta sobre possíveis problemas. É altamente recomendável que você execute seu
código com JSLint. A ferramenta "vai machucar seus sentimentos", como seu criador alerta, mas apenas no
começo. Você pode aprender rapidamente com seus erros e adotar os hábitos essenciais de um programador
JavaScript profissional. Não ter nenhum erro de JSLint em seu código também ajuda você a ter mais confiança
nele, sabendo que não cometeu uma simples omissão ou erro de sintaxe às pressas.

A partir do próximo capítulo, você verá o JSLint sendo bastante mencionado. Todo o código do livro passa com
sucesso na verificação do JSLint (com as configurações padrão, vigentes no momento da escrita), exceto em
algumas ocasiões claramente marcadas como antipadrões.

Em suas configurações padrão, o JSLint espera que seu código seja compatível com o modo estrito.

O Console
O objeto console é usado ao longo do livro. Este objeto não faz parte da linguagem, mas sim do ambiente, e
está presente na maioria dos navegadores atuais. No Firefox, por exemplo, ele vem com a extensão Firebug. O
console do Firebug possui uma interface de usuário que permite digitar e testar rapidamente pequenos trechos
de código JavaScript, além de interagir com a página carregada no momento (veja a Figura 1-1). Também é
altamente recomendado como uma ferramenta de aprendizado e exploração. Funcionalidades semelhantes
estão disponíveis nos navegadores WebKit (Safari e Chrome) como parte do Web Inspector e no IE a partir da
versão 8 como parte das Ferramentas do Desenvolvedor.

A maioria dos exemplos de código no livro usa o objeto console em vez de solicitar alert()s ou atualizar a página
atual, porque é uma maneira fácil e discreta de imprimir alguma saída.

6 | Capítulo 1: Introdução
Machine Translated by Google [Link]

Figura 1-1. Usando o console do Firebug

Frequentemente usamos o método log(), que imprime todos os parâmetros passados a ele, e às
vezes dir(), que enumera o objeto passado a ele e imprime todas as propriedades. Aqui está um
exemplo de uso:
[Link]("test", 1, {}, [1,2,3]);
[Link]({one: 1, two: {three: 3}});

Ao digitar no console, você não precisa usar [Link](); você pode simplesmente omiti-lo. Para
evitar confusão, alguns trechos de código também o ignoram e assumem que você está testando
o código no console:
[Link] === window['nome']; // verdadeiro

É como se usássemos o seguinte:


[Link]([Link] === window['nome']);

e imprimiu true no console.

O Console | 7
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 2

Essenciais

Este capítulo discute as melhores práticas, padrões e hábitos essenciais para escrever código
JavaScript de alta qualidade, como evitar globais, usar declarações de variáveis únicas, pré-
armazenar em cache o comprimento em loops, seguir convenções de codificação e muito mais. O
capítulo também inclui alguns hábitos não necessariamente relacionados ao código em si, mas
mais sobre o processo geral de criação de código, incluindo escrever documentação de API,
conduzir revisões por pares e executar JSLint. Esses hábitos e melhores práticas podem ajudá-lo
a escrever um código melhor, mais compreensível e sustentável — um código do qual se orgulhar
(e ser capaz de decifrar) ao revisitá-lo meses e anos depois.

Escrevendo código sustentável


Corrigir bugs de software é caro. E o custo aumenta com o tempo, especialmente se os bugs se
infiltrarem no produto lançado publicamente. É melhor corrigir um bug imediatamente, assim que
o encontrar; isso ocorre quando o problema que seu código resolve ainda está fresco na sua cabeça.
Caso contrário, você passa para outras tarefas e esquece completamente daquele código
específico. Revisar o código depois de algum tempo requer:

• Tempo para reaprender e entender o problema •


Tempo para entender o código que deve resolver o problema

Outro problema, específico de projetos ou empresas maiores, é que a pessoa que eventualmente
corrige o bug não é a mesma que o criou (e também não é a mesma que o encontrou). Portanto,
é fundamental reduzir o tempo necessário para entender o código, seja ele escrito por você há
algum tempo ou por outro desenvolvedor da equipe. Isso é crucial tanto para o resultado final
(receita da empresa) quanto para a satisfação do desenvolvedor, porque todos nós preferimos
desenvolver algo novo e empolgante em vez de gastar horas e dias mantendo código legado.

Outro fato da vida relacionado ao desenvolvimento de software em geral é que geralmente se


gasta mais tempo lendo código do que escrevendo . Em momentos em que você está focado e
imerso em um problema, pode se sentar e criar uma quantidade considerável de código em uma tarde.

9
Machine Translated by Google [Link]

O código provavelmente funcionará naquele momento, mas, à medida que o aplicativo amadurece, muitas
outras coisas acontecem e exigem que seu código seja revisado, revisado e ajustado. Por exemplo:

• Bugs são descobertos. •

Novos recursos são adicionados ao aplicativo. • O

aplicativo precisa funcionar em novos ambientes (por exemplo, novos navegadores aparecem no
mercado). • O código é

reaproveitado. • O código é

completamente reescrito do zero ou portado para outra arquitetura ou até mesmo outra linguagem.

Como resultado das mudanças, as poucas horas-homem gastas inicialmente escrevendo o código se
transformam em semanas-homem gastas lendo-o. É por isso que criar um código sustentável é fundamental
para o sucesso de uma aplicação.
Código sustentável significa código que:

• É legível

• É consistente

• É previsível • Parece

que foi escrito pela mesma pessoa • Está documentado

O restante deste capítulo aborda esses pontos quando se trata de escrever JavaScript.

Minimizando Globais
JavaScript usa funções para gerenciar o escopo. Uma variável declarada dentro de uma função é local para
essa função e não está disponível fora dela. Por outro lado, variáveis globais são aquelas declaradas fora
de qualquer função ou simplesmente usadas sem serem declaradas.

Todo ambiente JavaScript possui um objeto global acessível quando você usa this fora de qualquer função.
Cada variável global que você cria se torna uma propriedade do objeto global. Em navegadores, por
conveniência, há uma propriedade adicional do objeto global chamada window que (geralmente) aponta
para o próprio objeto global. O trecho de código a seguir mostra como criar e acessar uma variável global
em um ambiente de navegador:
myglobal = "olá"; // antipadrão
[Link](myglobal); // "olá"
[Link]([Link]); // "olá"
[Link](window["myglobal"]); // "olá"
[Link]([Link]); // "olá"

10 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

O problema com os globais


O problema com variáveis globais é que elas são compartilhadas entre todo o código do seu aplicativo
JavaScript ou página web. Elas residem no mesmo namespace global e sempre há a possibilidade
de conflitos de nomenclatura — quando duas partes distintas de um aplicativo definem variáveis
globais com o mesmo nome, mas com finalidades diferentes.

Também é comum que páginas da web incluam código não escrito pelos desenvolvedores da página,
por exemplo: • Uma

biblioteca JavaScript de terceiros •


Scripts de um parceiro de publicidade •
Código de um script de rastreamento e análise de usuários de terceiros
• Diferentes tipos de widgets, emblemas e botões

Digamos que um dos scripts de terceiros defina uma variável global, chamada, por exemplo, result.
Posteriormente, em uma de suas funções, você define outra variável global chamada result. O
resultado disso é que a última variável result substitui as anteriores, e o script de terceiros pode
simplesmente parar de funcionar.

Portanto, é importante ser um bom vizinho para os outros scripts que podem estar na mesma página
e usar o mínimo possível de variáveis globais. Mais adiante neste livro, você aprenderá sobre
estratégias para minimizar o número de variáveis globais, como o padrão de namespace ou as
funções imediatas autoexecutáveis. No entanto, o padrão mais importante para ter menos variáveis
globais é sempre usar var para declarar variáveis.

É surpreendentemente fácil criar globais involuntariamente por causa de dois recursos do JavaScript.
Primeiro, você pode usar variáveis sem sequer declará-las. E segundo, JavaScript tem a noção de
globais implícitas, o que significa que qualquer variável que você não declarar se torna uma
propriedade do objeto global (e é acessível como uma variável global devidamente declarada).
Considere o seguinte exemplo:
função soma(x, y) { //
antipadrão: resultado global
implícito = x +
y; retornar resultado;
}

Neste código, result é usado sem ser declarado. O código funciona bem, mas depois de chamar a
função, você acaba com mais uma variável result no namespace global, o que pode ser uma fonte
de problemas.

A regra geral é sempre declarar variáveis com var, como demonstrado na versão aprimorada da função
sum() : function sum(x, y) { var result = x +
y; return result;

Minimizando Globais | 11
Machine Translated by Google [Link]

Outro antipadrão que cria globais implícitos é encadear atribuições como parte de uma declaração var . No
trecho a seguir, a é local, mas b se torna global, o que provavelmente não é o que você pretendia fazer:

// antipadrão, não use a


função foo() { var
a = b = 0;

// ...
}

Se você está se perguntando por que isso acontece, é por causa da avaliação da direita para a esquerda.
Primeiro, a expressão b = 0 é avaliada e, neste caso, b não é declarado. O valor de retorno dessa expressão
é 0 e é atribuído à nova variável local declarada com var a. Em outras palavras, é como se você tivesse
digitado:
var a = (b = 0);

Se você já declarou as variáveis, encadear atribuições é adequado e não cria variáveis globais inesperadas.
Exemplo: function foo() { var a, b; // ...
a = b = 0; // ambas
locais

Outro motivo para evitar globais é a portabilidade. Se você quer que seu código
rode em diferentes ambientes (hosts), é perigoso usar globais, pois você pode
acidentalmente sobrescrever um objeto host que não existe no seu ambiente
original (então você achou que o nome era seguro), mas que existe em alguns
dos outros.

Efeitos colaterais ao esquecer var


Há uma pequena diferença entre variáveis globais implícitas e definidas explicitamente: a diferença está na
capacidade de indefinir essas variáveis usando o operador delete :

• Globais criados com var (aqueles criados no programa fora de qualquer função) não podem ser
excluídos.

• Globais implícitos criados sem var (independentemente de serem criados dentro de funções) podem ser
excluído.

Isso mostra que variáveis globais implícitas não são tecnicamente variáveis reais, mas são propriedades
do objeto global. Propriedades podem ser excluídas com o operador delete, enquanto variáveis não podem:

// define três globais var


global_var = 1;
global_novar = 2; // antipadrão
(function () {

12 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

global_fromfunc = 3; // antipadrão }());

// tentativa de excluir
delete global_var; // falso delete
global_novar; // verdadeiro
delete global_fromfunc; // verdadeiro

// testa a exclusão
typeof global_var; // "número"
typeof global_novar; // "indefinido" typeof
global_fromfunc; // "indefinido"

No modo estrito do ES5, atribuições a variáveis não declaradas (como os dois antipadrões no
snippet anterior) gerarão um erro.

Acesso ao Objeto Global. Nos

navegadores, o objeto global é acessível de qualquer parte do código por meio da propriedade
window (a menos que você tenha feito algo especial e inesperado, como declarar uma variável
local chamada window). Mas em outros ambientes, essa propriedade de conveniência pode ter
outro nome (ou até mesmo não estar disponível para o programador). Se você precisar acessar o
objeto global sem codificar o identificador window, poderá fazer o seguinte a partir de qualquer
nível do escopo da função aninhada:
var global = (função () { retornar
isto; }());

Dessa forma, você sempre pode obter o objeto global, porque dentro de funções que foram
invocadas como funções (ou seja, não como constritores com new), this deve sempre apontar
para o objeto global. Na verdade, isso não é mais o caso no ECMAScript 5 em modo estrito,
então você precisa adotar um padrão diferente quando seu código estiver em modo estrito. Por
exemplo, se você estiver desenvolvendo uma biblioteca, pode encapsular o código da biblioteca
em uma função imediata (discutida no Capítulo 4) e, a partir do escopo global, passar uma
referência a this como parâmetro para sua função imediata.

Padrão de variável

única: usar uma única instrução var no topo das suas funções é um padrão útil a ser adotado. Ele
tem os seguintes benefícios:

• Fornece um único local para procurar todas as variáveis locais necessárias para a função •
Evita erros lógicos quando uma variável é usada antes de ser definida (consulte “Içamento: Uma
Problema com vars dispersos” na página 14)
• Ajuda você a lembrar de declarar variáveis e, portanto, minimizar globais • É menos
código (para digitar e transferir pela rede)

Minimizando Globais | 13
Machine Translated by Google [Link]

O padrão de variável única se parece com isso:

função func() { var a =


1, b = 2,
soma
= a + b,
meuobjeto = {}, i, j;

// corpo da função...

Use uma instrução var e declare várias variáveis delimitadas por vírgulas. É uma boa prática também
inicializar a variável com um valor inicial no momento da declaração. Isso pode evitar erros lógicos (todas as
variáveis não inicializadas e declaradas são inicializadas com o valor indefinido) e também melhorar a
legibilidade do código. Ao analisar o código posteriormente, você pode ter uma ideia do uso pretendido de
uma variável com base em seu valor inicial — por exemplo, ela deveria ser um objeto ou um inteiro?

Você também pode realizar algum trabalho real no momento da declaração, como no caso de sum = a + b
no código anterior. Outro exemplo é ao trabalhar com referências DOM (Document Object Model). Você
pode atribuir referências DOM a variáveis locais juntamente com a declaração única, como demonstra o
código a seguir:

função updateElement() { var el =


[Link]("resultado"), style = [Link];

// faça algo com el e estilo...

Elevação: Um Problema com Variáveis Dispersas.

JavaScript permite que você tenha várias instruções var em qualquer lugar de uma função, e todas elas agem
como se as variáveis estivessem declaradas no topo da função. Esse comportamento é conhecido como
elevação. Isso pode levar a erros lógicos quando você usa uma variável e a declara posteriormente na
função. Em JavaScript, desde que uma variável esteja no mesmo escopo (mesma função), ela é considerada
declarada, mesmo quando usada antes da declaração var . Veja este exemplo:

// antipadrão
meunome = "global"; // variável global function
func() { alert(meunome); //
"indefinido" var meunome = "local";
alerta(meunome); // "locais"

} função();

14 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

Neste exemplo, você pode esperar que o primeiro alert() indique "global" e o segundo "local".
É uma expectativa razoável porque, no momento do primeiro alerta, myname não foi
declarado e, portanto, a função provavelmente deveria "ver" o myname global. Mas não é
assim que funciona. O primeiro alerta dirá "undefined" porque myname é considerado
declarado como uma variável local para a função. (Embora a declaração venha depois.)
Todas as declarações de variáveis são elevadas ao topo da função. Portanto, para evitar
esse tipo de confusão, é melhor declarar antecipadamente todas as variáveis que você
pretende usar.
O trecho de código anterior se comportará como se fosse implementado assim:
meunome = "global"; // variável global
function func()
{ var meunome; // igual a -> var meunome =
indefinido; alerta(meunome); //
"indefinido"
meunome = "local"; alerta(meunome); // "locais"

} função();

Para completar, vale mencionar que, na verdade, no nível de implementação, as


coisas são um pouco mais complexas. Há duas etapas de manipulação de código,
onde variáveis, declarações de função e parâmetros formais são criados na primeira
etapa, que é a de análise sintática e inserção no contexto. Na segunda etapa, a de
execução do código em tempo de execução, expressões de função e identificadores
não qualificados (variáveis não declaradas) são criados. Mas, para fins práticos,
podemos adotar o conceito de " hoisting", que, na verdade, não é definido pelo
padrão ECMAScript, mas é comumente usado para descrever o comportamento.

Laços for
Nos laços for, você itera sobre arrays ou objetos semelhantes a arrays, como argumentos e
objetos HTMLCollection . O padrão usual do laço for se parece com o seguinte:
// loop subótimo para
(var i = 0; i < [Link]; i++) { // faça
algo com myarray[i]
}

Um problema com esse padrão é que o comprimento do array é acessado a cada iteração do
loop. Isso pode tornar seu código lento, especialmente quando myarray não é um array, mas
um objeto HTMLCollection .
HTMLCollections são objetos retornados por métodos DOM, como:

• [Link]()
• [Link]()
• [Link]()

para Laços | 15
Machine Translated by Google [Link]

Há também uma série de outras HTMLCollections, que foram introduzidas antes do padrão DOM e ainda são
usadas hoje. Entre elas, estão (entre outras):

[Link]
Todos os elementos IMG na página
[Link]
Todos os elementos A

[Link] Todos

os formulários

[Link][0].elements Todos
os campos do primeiro formulário na página

O problema com coleções é que elas são consultas ativas no documento subjacente (a página HTML). Isso
significa que toda vez que você acessa o comprimento de qualquer coleção, você está consultando o DOM
ativo, e as operações no DOM são caras em geral.

É por isso que um padrão melhor para loops for é armazenar em cache o comprimento do array (ou coleção)
sobre o qual você está iterando, conforme mostrado no exemplo a seguir:

for (var i = 0, max = [Link]; i < max; i++) { // faça algo


com myarray[i]
}

Dessa forma, você recupera o valor do comprimento apenas uma vez e o utiliza durante todo o loop.

O armazenamento em cache do comprimento ao iterar sobre HTMLCollections é mais rápido em todos os


navegadores — entre duas vezes mais rápido (Safari 3) e 190 vezes (IE7). (Para mais detalhes, consulte
JavaScript de Alto Desempenho por Nicholas Zakas [O'Reilly].)

Observe que quando você pretende modificar explicitamente a coleção no loop (por exemplo, adicionando
mais elementos DOM), você provavelmente gostaria que o comprimento fosse atualizado e não
constante.

Seguindo o padrão de variável única, você também pode tirar a variável do loop e fazer o loop como: function looper() { var i = 0, max,

minhamatriz = [];

// ...

para (i = 0, max = [Link]; i < max; i++) { //


faça algo com myarray[i]
}
}

Esse padrão tem o benefício da consistência porque você segue o padrão de variável única .
Uma desvantagem é que isso torna um pouco mais difícil copiar e colar loops inteiros durante a refatoração
do código. Por exemplo, se você estiver copiando o loop de uma função para outra,

16 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

você tem que ter certeza de também transferir i e max para a nova função (e provavelmente excluí-los da
função original se eles não forem mais necessários lá).

Um último ajuste no loop seria substituir i++ por uma destas expressões:

i=i+1i
+= 1

O JSLint solicita que você faça isso; o motivo é que ++ e -- promovem "truques excessivos". Se você
discordar disso, pode definir a opção plusplus do JSLint como false. (É true por padrão.) Mais adiante no
livro, o último padrão é usado: i += 1.

Duas variações do padrão for introduzem algumas micro-otimizações porque:

• Use uma variável a menos (sem

máximo) • Faça a contagem regressiva até 0, que geralmente é mais rápido porque é mais eficiente comparar
com 0 do que com o comprimento da matriz ou com qualquer coisa diferente de 0

O primeiro padrão modificado é:

var i, minhamatriz = [];

para (i = [Link]; i--;) {


// faça algo com myarray[i]
}

E o segundo usa um loop while : var


myarray = [], i =
[Link];

enquanto (i--)
{ // faça algo com myarray[i]
}

Essas são microotimizações e só serão percebidas em operações críticas de desempenho. Além disso, o
JSLint reclamará do uso de i--.

Loops for-in
Laços for-in devem ser usados para iterar sobre objetos que não sejam matrizes. Laços com for-in também
são chamados de enumeração.

Tecnicamente, você também pode usar for-in para fazer um loop sobre arrays (porque em JavaScript arrays
são objetos), mas isso não é recomendado. Isso pode levar a erros lógicos se o objeto array já tiver sido
complementado com funcionalidades personalizadas. Além disso, a ordem (a sequência) de listagem das
propriedades não é garantida em um for-in. Portanto, é preferível usar loops for normais com arrays e loops
for-in para objetos.

É importante usar o método hasOwnProperty() ao iterar sobre propriedades de objetos para filtrar
propriedades que descem a cadeia de protótipos.

Loops for-in | 17
Machine Translated by Google [Link]

Considere o seguinte exemplo: // o objeto var man

= { mãos: 2,
pernas: 2,
cabeças: 1

};

// em algum outro lugar no código // um


método foi adicionado a todos os objetos if (typeof
[Link] === "undefined") { [Link] = function ()
{};
}

Neste exemplo, temos um objeto simples chamado man definido com um literal de objeto. Em
algum momento, antes ou depois da definição de man , o protótipo do objeto foi complementado
com um método útil chamado clone(). A cadeia de protótipos está ativa, o que significa que todos
os objetos têm acesso automático ao novo método. Para evitar que o método clone() apareça ao
enumerar man, você precisa chamar hasOwnProperty() para filtrar as propriedades do protótipo.
A falha na filtragem pode resultar no aparecimento da função clone() , o que é um comportamento
indesejado na maioria dos cenários:
// 1. //
laço for-in para (var
i em man) { se
([Link](i)) { // filtro [Link](i, ":",
man[i]);
}

}/
* resultado no console mãos :
2 pernas : 2
cabeças :
1 */

// 2. //
antipadrão: // loop for-
in sem verificar hasOwnProperty() for (var i in man) { [Link](i, ":",
man[i]);

} /*
resultado no console mãos: 2
pernas: 2
cabeças: 1

clone: função() */

18 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

Outro padrão para usar hasOwnProperty() é chamar esse método do [Link], assim:

para (var i em homem)


{ se ([Link](homem, i)) { // filtrar
[Link](i, ":", homem[i]);
}
}

A vantagem é que você pode evitar colisões de nomenclatura caso o objeto principal tenha redefinido
hasOwnProperty. Além disso, para evitar longas pesquisas de propriedades até o objeto, você pode usar
uma variável local para "armazená-la em cache":

var i,
hasOwn = [Link];
para (i em homem)
{ se ([Link](homem, i)) { // filtrar
[Link](i, ":", homem[i]);
}
}

A rigor, não usar hasOwnProperty() não é um erro. Dependendo da tarefa e da confiança


que você tem no código, você pode ignorá-la e acelerar um pouco os loops. Mas quando
não tiver certeza sobre o conteúdo do objeto (e sua cadeia de protótipos), é mais seguro
simplesmente adicionar a verificação hasOwnProperty() .

Uma variação de formatação (que não passa por JSLint) ignora uma chave e coloca o if na mesma linha. A
vantagem é que a instrução do loop se parece mais com um pensamento completo ("para cada elemento
que tem uma propriedade X, faça algo com X"). Além disso, há menos recuo antes de chegar ao objetivo
principal do loop:

// Aviso: não passa JSLint var i,

hasOwn = [Link]; for (i


in man) if ([Link](man, i)) { // filtro [Link](i, ":",
man[i]);
}

(Não) Aumentando Protótipos Integrados


Aumentar a propriedade de protótipo das funções construtoras é uma maneira poderosa de adicionar
funcionalidade, mas às vezes pode ser poderosa demais.

É tentador aumentar protótipos de construtores integrados, como Object(), Array() ou Function(), mas isso
pode prejudicar seriamente a manutenibilidade, pois tornará seu código menos previsível. Outros
desenvolvedores que usam seu código provavelmente esperarão que os métodos JavaScript integrados
funcionem de forma consistente e não contarão com suas adições.

(Não) Aumentando Protótipos Integrados | 19


Machine Translated by Google [Link]

Além disso, as propriedades que você adiciona ao protótipo podem aparecer em loops que não
usam hasOwnProperty(), o que pode criar confusão.

Portanto, é melhor não aumentar protótipos integrados. Você pode abrir uma exceção à regra
somente quando todas estas condições forem atendidas:

1. Espera-se que futuras versões do ECMAScript ou implementações de JavaScript implementem


essa funcionalidade como um método integrado de forma consistente. Por exemplo, você
pode adicionar métodos descritos no ECMAScript 5 enquanto espera que os navegadores os
atualizem. Nesse caso, você está apenas definindo os métodos úteis com antecedência.
2. Verifique se sua propriedade ou método personalizado ainda não existe — talvez já esteja
implementado em algum outro lugar no código ou já faça parte do mecanismo JavaScript de
um dos navegadores que você suporta.
3. Você documenta e comunica claramente a mudança à equipe.

Se essas três condições forem atendidas, você poderá prosseguir com a adição personalizada ao
protótipo, seguindo este padrão: if
(typeof [Link] !== "function")
{ [Link] = function ()
{ // implementação...
};
}

Padrão de troca
Você pode melhorar a legibilidade e a robustez de suas instruções switch seguindo este padrão:

var inspect_me = 0,
resultado = '';

alternar (inspecione-me) {
caso 0:
resultado = "zero"; quebra;
caso 1:

resultado = "um";
quebra;
padrão:
resultado = "desconhecido";
}

As convenções de estilo seguidas neste exemplo simples são:

• Alinhar cada caso com switch (uma exceção à regra de recuo de chaves). • Recuar o código
dentro de cada caso. • Terminar cada caso
com um break claro;.

20 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

• Evitar falhas (quando você omite a interrupção intencionalmente). Se você estiver absolutamente convencido
de que uma falha é a melhor abordagem, certifique-se de documentar esses casos, pois eles podem parecer
erros para os leitores do seu código. • Finalizar a troca com um padrão: para garantir que sempre

haja um resultado sensato, mesmo


se nenhum dos casos correspondesse.

Evitando a conversão implícita de tipos


JavaScript converte implicitamente variáveis quando você as compara. É por isso que comparações como false == 0
ou ""
== 0 retorna verdadeiro.

Para evitar confusão causada pela conversão de tipos implícita, sempre use os operadores === e !== que verificam
os valores e o tipo das expressões que você compara:

var zero = 0; if
(zero === false) { // não
está sendo executado porque zero é 0, não falso
}

// antipadrão if
(zero == false) { // este
bloco é executado...
}

Há outra corrente de pensamento que defende a opinião de que é redundante usar === quando == é suficiente. Por
exemplo, ao usar typeof , você sabe que ele retorna uma string, então não há razão para usar igualdade estrita. No
entanto, JSLint requer igualdade estrita; isso faz com que o código pareça consistente e reduz o esforço mental ao
lê-lo. ("Este == é intencional ou uma omissão?")

Evitando eval()
Se você identificar o uso de eval() no seu código, lembre-se do mantra "eval() é maligno". Esta função recebe uma
string arbitrária e a executa como código JavaScript. Quando o código em questão é conhecido de antemão (não
determinado em tempo de execução), não há motivo para usar eval(). Se o código for gerado dinamicamente em
tempo de execução, geralmente há uma maneira melhor de atingir o objetivo sem eval(). Por exemplo, usar apenas
a notação de colchetes para acessar propriedades dinâmicas é melhor e mais simples:

// antipadrão var
propriedade = "nome";
alerta(eval("obj." + propriedade));

// propriedade
var preferida = "nome";
alerta(obj[propriedade]);

Usar eval() também tem implicações de segurança, pois você pode estar executando código (por exemplo, vindo
da rede) que foi adulterado. Este é um problema comum.

Evitando a conversão implícita de tipos | 21


Machine Translated by Google [Link]

antipadrão ao lidar com uma resposta JSON de uma requisição Ajax. Nesses casos, é melhor
usar os métodos integrados do navegador para analisar a resposta JSON e garantir que ela
seja segura e válida. Para navegadores que não suportam [Link]() nativamente, você
pode usar uma biblioteca do [Link].

Também é importante lembrar que passar strings para setInterval(), setTimeout() e o construtor
Function() é, em grande parte, semelhante a usar eval() e, portanto, deve ser evitado. Nos
bastidores, o JavaScript ainda precisa avaliar e executar a string que você passa como código
de programação:
// antipadrões
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferido
setTimeout(myFunc, 1000);
setTimeout(function ()
{ myFunc(1, 2,
3); }, 1000);

O uso do construtor new Function() é semelhante ao eval() e deve ser abordado com cautela.
Pode ser um construtor poderoso, mas frequentemente é mal utilizado. Se você realmente
precisar usar eval(), considere usar new Function() . Há um pequeno benefício potencial, pois
o código avaliado em new Function() será executado em um escopo de função local, portanto,
quaisquer variáveis definidas com var no código que está sendo avaliado não se tornarão
globais automaticamente. Outra maneira de evitar globais automáticas é encapsular a chamada
eval() em uma função imediata (mais sobre funções imediatas no Capítulo 4).

Considere o exemplo a seguir. Aqui, apenas un permanece como variável global, poluindo o namespace:
[Link](typeof
un); // "indefinido" [Link](typeof deux); //
"indefinido" [Link](typeof trois); //
"indefinido"

var jsstring = "var un = 1; [Link](un);";


avaliação(jsstring); // registra "1"

jsstring = "var deux = 2; [Link](deux);"; nova


Função(jsstring)(); // registra "2"

jsstring = "var trois = 3; [Link](trois);"; (função()


{ eval(jsstring); }
()); // registra "3"

[Link](typeof un); // "número"


[Link](typeof dois); // "indefinido"
[Link](typeof três); // "indefinido"

Outra diferença entre eval() e o construtor Function é que eval() pode interferir na cadeia de
escopo, enquanto Function é muito mais restrita. Não importa onde você execute Function, ele
vê apenas o escopo global. Portanto, ele pode lidar com menos variáveis locais.

22 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

poluição. No exemplo a seguir, eval() pode acessar e modificar uma variável em seu escopo externo,
enquanto Function não pode (observe também que usar Function ou new Function é idêntico):

(function () { var
local = 1;
eval("local = 3; [Link](local)"); // logs 3
[Link](local); // logs 3 }());

(function () { var
local = 1;
Function("[Link](typeof local);")(); // logs indefinidos }());

Conversões de números com parseInt()

Usando parseInt(), você pode obter um valor numérico de uma string. A função aceita um segundo
parâmetro radix, que frequentemente é omitido, mas não deveria ser. Os problemas ocorrem quando a
string a ser analisada começa com 0: por exemplo, parte de uma data inserida em um campo de
formulário. Strings que começam com 0 são tratadas como números octais (base 8) no ECMAScript 3;
no entanto, isso mudou no ES5. Para evitar inconsistências e resultados inesperados, sempre
especifique o parâmetro radix:

var mês = "06",


ano = "09";
mês = parseInt(mês, 10); ano =
parseInt(ano, 10);

Neste exemplo, se você omitir o parâmetro radix como parseInt(year), o valor retornado será 0, porque
“09” assume um número octal (como se você fizesse parseInt(year, 8)) e 09 não é um dígito válido na
base 8.

Formas alternativas de converter uma string em um número incluem:


+"08" // o resultado é 8
Número("08") // 8

Geralmente, elas são mais rápidas que parseInt(), porque parseInt(), como o nome sugere, analisa e
não apenas converte. Mas se você espera uma entrada como "08 hello", parseInt() retornará um
número, enquanto as outras falharão com NaN.

Convenções de codificação

É importante estabelecer e seguir convenções de codificação — elas tornam seu código consistente,
previsível e muito mais fácil de ler e entender. Um novo desenvolvedor que se junta à equipe pode ler
as convenções e se tornar produtivo muito mais rápido, entendendo o código escrito por qualquer
outro membro da equipe.

Convenções de codificação | 23
Machine Translated by Google [Link]

Muitas discussões têm sido travadas em reuniões e listas de discussão sobre aspectos
específicos de certas convenções de codificação (por exemplo, a indentação do código —
tabulações ou espaços?). Portanto, se você está sugerindo a adoção de convenções em sua
organização, esteja preparado para enfrentar resistência e ouvir opiniões diferentes, mas
igualmente fortes. Lembre-se de que é muito mais importante estabelecer e seguir
consistentemente uma convenção, qualquer convenção, do que os detalhes exatos dessa convenção.

Recuo
Código sem indentação é impossível de ler. Pior ainda é código com indentação inconsistente, pois parece seguir uma
convenção, mas pode trazer surpresas confusas ao longo do caminho. É importante padronizar o uso da indentação.

Alguns desenvolvedores preferem recuo com tabulações, pois qualquer um pode ajustar seu
editor para exibir as tabulações com o número de espaços desejado individualmente. Alguns
preferem espaços — geralmente quatro. Não importa, desde que todos na equipe sigam a
mesma convenção. Este livro, por exemplo, usa recuo de quatro espaços, que também é o
padrão no JSLint.

E o que você deve recuar? A regra é simples — qualquer coisa entre chaves. Isso significa os corpos de funções, laços (do, while,
for, for-in), ifs, switches e propriedades de objetos na notação literal de objeto. O código a seguir mostra alguns exemplos de uso de
recuo: function outer(a, b) { var c = 1, d = 2, inner; if (a > b) { inner = function () { return { r: c - d

};

}; } senão
{ interno = função () { retornar
{ r: c + d

};
};

} retornar interno;
}

Chaves: As
chaves devem ser sempre usadas, mesmo nos casos em que são opcionais. Tecnicamente, se
você tiver apenas uma instrução em um if ou for, as chaves não são necessárias, mas você

24 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

De qualquer forma, você deve sempre usá-los. Isso torna o código mais consistente e fácil de
atualizar.

Imagine que você tem um loop for com apenas uma instrução. Você poderia omitir as chaves e
não haveria erro de sintaxe: //
má prática for
(var i = 0; i < 10; i += 1) alert(i);

Mas e se, mais tarde, você adicionar outra linha no corpo do loop?
// má prática para
(var i = 0; i < 10; i += 1) alert(i); alert(i +
" "
é + (i % 2 ? "ímpar" : "par"));

O segundo alerta está fora do loop, embora a indentação possa te enganar. A melhor coisa a
fazer a longo prazo é sempre usar as chaves, mesmo para blocos de uma linha:
// melhor
para (var i = 0; i < 10; i += 1) { alert(i);

Da mesma forma para condições if : //

ruim if
(true)
alert(1); else

alert(2);

// melhor
se
(verdadeiro)
{ alert(1); }
senão { alert(2);
}

Localização das chaves de

abertura Os desenvolvedores também tendem a ter preferências sobre onde a chave de abertura deve ficar: na
mesma linha ou na linha seguinte? if (true) { alert("É

VERDADEIRO!");
}

Ou:

se (verdadeiro)
{
alert("É VERDADE!");
}

Convenções de codificação | 25
Machine Translated by Google [Link]

Neste exemplo específico, é uma questão de preferência, mas há casos em que o programa pode se
comportar de forma diferente dependendo de onde a chave está. Isso se deve ao mecanismo de inserção
de ponto e vírgula — o JavaScript não é exigente quando você opta por não terminar suas linhas
corretamente com um ponto e vírgula e o adiciona para você. Esse comportamento pode causar
problemas quando uma função retorna um literal de objeto e a chave de abertura está na linha seguinte:

// aviso: valor de retorno inesperado function func()


{ return {

nome: "Batman"
};
}

Se você espera que esta função retorne um objeto com uma propriedade de nome , você ficará surpreso.
Devido aos pontos e vírgulas implícitos, a função retorna indefinido. O código anterior é equivalente a
este: // aviso: valor de retorno
inesperado function func() {

return undefined; //
código inalcançável segue... {

nome: "Batman"
};
}

Concluindo, use sempre chaves e sempre coloque a primeira na mesma linha da instrução anterior: function func() { return { name:
"Batman"

};
}

Uma observação sobre ponto e vírgula: assim como com as chaves, você deve sempre
usar ponto e vírgula, mesmo quando eles são implícitos pelos analisadores de JavaScript.
Isso não apenas promove disciplina e uma abordagem mais rigorosa ao código, mas
também ajuda a resolver ambiguidades, como mostrou o exemplo anterior.

Espaço em

Branco: O uso de espaços em branco também pode contribuir para melhorar a legibilidade e a
consistência do código. Em frases escritas em inglês, use intervalos após vírgulas e pontos. Em
JavaScript, siga a mesma lógica e adicione intervalos após expressões do tipo lista (equivalente a
vírgulas) e no final de declarações (equivalente a completar um "pensamento").

26 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

Bons lugares para usar um espaço em branco incluem:

• Após o ponto e vírgula que separa as partes de um loop for : por exemplo, for (var i
= 0; i < 10; i += 1) {...}
• Inicializando múltiplas variáveis (i e max) em um loop for : for (var i = 0, max = 10; i < max; i += 1) {...}

• Após as vírgulas que delimitam itens de array: var a = [1, 2, 3]; • Após vírgulas em

propriedades de objeto e depois de dois pontos que dividem nomes de propriedades e seus valores: var o = {a:
1, b: 2}; • Delimitando argumentos de função:

myFunc(a, b, c) • Antes das chaves em declarações de função:

function myFunc() {} • Após function em expressões de função anônima: var myFunc = function

() {};

Outro bom uso para espaços em branco é separar todos os operadores e seus operandos com espaços,
o que basicamente significa usar um espaço antes e depois de +, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=
e assim por diante: //
espaçamento generoso e consistente //
torna o código mais fácil de ler //
permitindo que ele "respire" var
d = 0, a =
b + 1; if (a
&& b && c) { d = a %
c; a += d;

// antipadrão //
espaços ausentes ou inconsistentes //
torna o código confuso var d=
0, a =b+1;
if (a&&
b&&c) { d=a %c;
a+= d;

E uma observação final sobre o espaço em branco: espaçamento entre chaves. É bom usar um espaço:

• Antes de abrir chaves ({) em funções, casos if-else , loops e literais de objeto • Entre a chave de fechamento

(}) e else ou while

Um argumento contra o uso liberal de espaços em branco pode ser que ele aumentaria o tamanho do arquivo,
mas a minimização (discutida mais adiante no capítulo) resolve esse problema.

Um aspecto frequentemente negligenciado da legibilidade do código é o uso de espaços em branco


verticais. Você pode usar linhas em branco para separar unidades de código, assim como parágrafos
são usados na literatura para separar ideias.

Convenções de codificação | 27
Machine Translated by Google [Link]

Convenções de nomenclatura

Outra maneira de tornar seu código mais previsível e sustentável é adotar convenções de nomenclatura. Isso
significa escolher nomes para suas variáveis e funções de forma consistente.

Abaixo estão algumas sugestões de convenções de nomenclatura que você pode adotar como estão
ou ajustar conforme sua preferência. Novamente, ter uma convenção e segui-la consistentemente é
muito mais importante do que a convenção propriamente dita.

Construtores de Capitalização

JavaScript não tem classes, mas tem funções construtoras invocadas com new:
var adam = nova Pessoa();

Como construtores ainda são apenas funções, ajuda se você puder dizer, apenas olhando para o
nome da função, se ela deveria se comportar como um construtor ou como uma função normal.

Nomear construtores com a primeira letra maiúscula fornece essa dica. Usar letras minúsculas para
funções e métodos indica que eles não devem ser chamados com new:
função MyConstructor() {...}
função myFunction() {...}

No próximo capítulo, há alguns padrões que permitem que você force programaticamente seus
construtores a se comportarem como construtores, mas simplesmente seguir a convenção de
nomenclatura já é útil por si só, pelo menos para programadores que leem o código-fonte.

Separando Palavras

Quando você tem várias palavras em uma variável ou nome de função, é uma boa ideia seguir uma
convenção sobre como elas serão separadas. Uma convenção comum é usar o chamado " camel
case". Seguindo a convenção, você digita as palavras em letras minúsculas, colocando apenas a
primeira letra de cada palavra em maiúscula.

Para seus construtores, você pode usar letras maiúsculas, como em MyConstructor(), e para nomes
de funções e métodos, você pode usar letras minúsculas, como em myFunction(), calculateArea() e
getFirstName().

E quanto às variáveis que não são funções? Desenvolvedores costumam usar letras minúsculas para
nomes de variáveis, mas outra boa ideia é usar todas as palavras em minúsculas delimitadas por um
sublinhado: por exemplo, first_name, favorite_bands e old_company_name. Essa notação ajuda a
distinguir visualmente entre funções e todos os outros identificadores — primitivos e objetos.

28 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

O ECMAScript usa camel case para métodos e propriedades, embora os nomes de propriedades com várias
palavras sejam raros ( propriedades lastIndex e ignoreCase de objetos de expressão regular).

Outros padrões de nomenclatura

Às vezes, os desenvolvedores usam uma convenção de nomenclatura para criar ou substituir recursos de linguagem.

Por exemplo, não há como definir constantes em JavaScript (embora existam algumas integradas, como
Number.MAX_VALUE), então os desenvolvedores adotaram a convenção de usar todas as letras maiúsculas
para nomear variáveis que não devem mudar de valor durante a vida do programa, como:

// constantes preciosas, por favor, não toquem var


PI = 3.14,
MAX_WIDTH = 800;

Há outra convenção que concorre para o uso de letras maiúsculas: usar letras maiúsculas para nomes de
variáveis globais. Nomear variáveis globais com todas as letras maiúsculas pode reforçar a prática de
minimizar seu número e torná-las facilmente distinguíveis.

Outro caso de uso de uma convenção para imitar a funcionalidade é a convenção de membros privados.
Embora seja possível implementar privacidade verdadeira em JavaScript, às vezes os desenvolvedores acham
mais fácil usar apenas um prefixo de sublinhado para denotar um método ou propriedade privada.
Considere o seguinte exemplo:

var pessoa =
{ getNome: função () ' '
{ return this._getFirst() + + this._getLast();
},
_getFirst: função () { // ...

},
_getLast: função () { // ...

}
};

Neste exemplo, getName() deve ser um método público, parte da API estável, enquanto _getFirst() e _getLast()
devem ser privados. Eles ainda são métodos públicos normais, mas o uso do prefixo sublinhado avisa os
usuários do objeto pessoa de que não há garantia de que esses métodos funcionem na próxima versão e não
devem ser usados diretamente. Observe que o JSLint reclamará sobre os prefixos sublinhados, a menos que
você defina a opção nomen: false.

A seguir estão algumas variedades da convenção _private :

• Usando um sublinhado final para significar privado, como em name_ e getElements_() • Usando

um prefixo de sublinhado para propriedades _protected e dois para __private


propriedades

Convenções de nomenclatura | 29
Machine Translated by Google [Link]

• No Firefox, algumas propriedades internas que tecnicamente não fazem parte da linguagem estão
disponíveis e são nomeadas com um prefixo de dois sublinhados e um sufixo de dois sublinhados,
como __proto__ e __parent__

Escrevendo Comentários

Você precisa comentar seu código, mesmo que seja improvável que alguém além de você o toque.
Muitas vezes, quando você está imerso em um problema, acha que é óbvio o que o código faz, mas
quando volta a ele depois de uma semana, tem dificuldade em lembrar exatamente como ele funcionava.

Você não deve exagerar comentando o óbvio: cada variável ou cada linha. Mas geralmente é necessário
documentar todas as funções, seus argumentos e valores de retorno, e também qualquer algoritmo ou
técnica interessante ou incomum. Pense nos comentários como dicas para os futuros leitores do
código; os leitores precisam entender o que seu código faz sem ler muito mais do que apenas os
comentários e os nomes das funções e propriedades. Quando você tem, por exemplo, cinco ou seis
linhas de código executando uma tarefa específica, o leitor pode pular os detalhes do código se você
fornecer uma descrição de uma linha descrevendo a finalidade do código e por que ele está lá. Não há
uma regra rígida ou proporção de comentários para código; alguns trechos de código (pense em
expressões regulares) podem, na verdade, exigir mais comentários do que código.

O hábito mais importante, mas também o mais difícil de seguir, é manter os


comentários atualizados, porque comentários desatualizados podem enganar e
ser muito piores do que nenhum comentário.

E como você verá na próxima seção, os comentários podem ajudar você a gerar documentação
automaticamente.

Escrevendo documentos de API

A maioria dos desenvolvedores considera escrever documentação uma tarefa tediosa e pouco
recompensadora. Mas isso não precisa ser verdade. A documentação da API pode ser gerada
automaticamente a partir de comentários no código. Dessa forma, você pode ter a documentação
escrita sem precisar escrevê-la. A maioria dos programadores acha essa ideia fascinante, porque gerar
automaticamente uma referência legível a partir de palavras-chave específicas e "comandos"
especialmente formatados se parece muito com programação real.

Tradicionalmente, a documentação de APIs vem do mundo Java, onde um utilitário chamado javadoc
é distribuído junto com o Java SDK (Software Developer Kit). Mas a mesma ideia foi adaptada para
muitas outras linguagens. E para JavaScript, existem duas ferramentas excelentes, gratuitas e de
código aberto: o JSDoc Toolkit (http:// [Link]/ p/ jsdoc-toolkit/). e YUIDoc (http:// [Link]/
projects/ yuidoc).

30 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

O processo de geração de documentação de API inclui:

• Escrever blocos de código especialmente formatados

• Executar uma ferramenta para analisar o código e os comentários •

Publicar os resultados da ferramenta, que geralmente são páginas HTML

A sintaxe especial que você precisa aprender consiste em cerca de uma dúzia de tags, que se parecem com isto:

/**
*
@valor da tag
*/

Por exemplo, digamos que você tenha uma função chamada reverse() que inverte uma string. Ela
recebe um parâmetro string e, no final, retorna outra string. A documentação poderia ser assim:

/**
* Inverter uma string
*
*
@param {String} String de entrada para reverter
* @return {String} A string invertida */ var

reverse = function (input) { // ... return


output;

};

Você pode ver que @param é a tag para parâmetros de entrada e @return é a tag usada para documentar
valores de retorno. A ferramenta de documentação analisa essas tags e produz um conjunto de documentos
HTML bem formatados no final.

Exemplo de YUIDoc: O

YUIDoc foi criado originalmente com o objetivo de documentar a biblioteca YUI (Yahoo! User Interface), mas pode
ser usado em qualquer projeto. Ele possui algumas convenções que você deve seguir para aproveitar ao máximo
a ferramenta, como, por exemplo, a noção de módulos e classes. (Embora não existam classes em JavaScript.)

Vamos ver um exemplo completo de geração de documentação usando o YUIDoc.

A Figura 2-1 mostra uma prévia da documentação bem formatada que você receberá no final.
Na verdade, você pode ajustar o modelo HTML para torná-lo ainda mais bonito e personalizado de acordo com as
necessidades, a aparência e o comportamento do seu projeto.

Para uma demonstração ao vivo do exemplo, visite http:// [Link]/ book/ 2/.

Neste exemplo, todo o aplicativo consiste em apenas um arquivo ([Link]) com apenas um módulo (myapp) . Você
aprenderá mais sobre módulos nos próximos capítulos, mas, por enquanto, pense no módulo como uma tag de
comentário necessária para que o YUIDoc funcione.

Escrevendo Documentação de API | 31


Machine Translated by Google [Link]

Figura 2-1. Documentação gerada pelo YUIDoc

O conteúdo de [Link] começa assim:


/**
*
Minha aplicação JavaScript
*
* @module
myapp */

Em seguida, você define um objeto em branco para usar como namespace:

var MEUAPP = {};

E então você define um objeto math_stuff que tem dois métodos: sum() e multi():
/**
* Um utilitário de matemática
*
@namespace
MYAPP * @class

math_stuff */ MYAPP.math_stuff = {

/**
* Soma dois números

32 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

*
* @method
*
sum @param {Number} a Primeiro
*
número @param {Number} b O segundo
número * @return {Number} A soma das duas

entradas */ sum: function


(a, b) { return a + b;
},

/**
* Multiplica dois números
*
* @método multi
*
@param {Número} a Primeiro número @param
*
{Número} b O segundo número * @return {Número} As
duas entradas multiplicadas */ multi: function (a, b) { return a

* b;
}
};
E isso completa a primeira “classe”. Observe as tags destacadas:

@namespace
A referência global que contém seu objeto.
@class
Um nome impróprio (sem classes em JavaScript) que é usado para significar um objeto ou uma
função construtora.

@method
Define um método em um objeto e especifica o nome do método. @param
Lista os
argumentos que uma função aceita. Os tipos dos parâmetros estão entre chaves, seguidos do nome
do parâmetro e sua descrição.
@return
Como @param, só que descreve o valor retornado pelo método e não tem nome.

Para a segunda “classe”, vamos usar uma função construtora e adicionar um método ao seu protótipo,
apenas para ter uma ideia de como o sistema de documentação funciona com as diferentes maneiras de
criar objetos:

/**
* Constrói objetos Pessoa * @class
Pessoa *
@constructor
*
@namespace
*
MEUAPP @param {String} primeiro Nome
*
@param {String} último Sobrenome */

[Link] = função (primeiro, último) {

Escrevendo Documentação de API | 33


Machine Translated by Google [Link]

/**
* Nome da pessoa
*
@property first_name
*
@type String
*/
this.first_name = first; /** *

Sobrenome (família) da pessoa @property


*
last_name @type
*
String */

this.last_name = last;

};

/**
* Retorna o nome do objeto pessoa
*
* @method getName
* @return {String} O nome da pessoa */

[Link]' =' function ()


{ return this.first_name + + this.last_name;
};
Na Figura 2-1, você pode ver a aparência da documentação gerada para este construtor Person . As partes
destacadas aqui são:

• @constructor indica que esta "classe" é, na verdade, uma função construtora. • @property

e @type descrevem propriedades de um objeto. O sistema YUIDoc é

independente de linguagem e analisa apenas os blocos de comentários, não o código JavaScript. A


desvantagem é que você precisa declarar os nomes das propriedades, parâmetros e métodos nos
comentários, por exemplo, @property first_name. A vantagem é que, quando você se sentir confortável com
ele, poderá usar o mesmo sistema para documentar código em qualquer linguagem.

Escrevendo para ser lido


Escrever comentários para os blocos de documentação da API não é apenas uma maneira preguiçosa de
fornecer documentação de referência, mas também serve a outro propósito: melhorar a qualidade do código,
fazendo com que você o revisite.

Qualquer escritor ou editor dirá que a edição é importante: provavelmente o passo mais importante na
produção de um bom livro ou artigo. Colocar tudo no papel é apenas o primeiro passo, o primeiro rascunho. O
rascunho comunica algumas informações ao leitor, mas provavelmente não da maneira mais clara, estruturada
ou fácil de seguir.

O mesmo se aplica à escrita de código. Quando você se senta e resolve um problema, essa solução é apenas
um primeiro rascunho. Ela produz o resultado desejado, mas será que o faz da melhor maneira possível?
É fácil de ler, entender, manter e atualizar? Ao revisitar seu código, o ideal é

34 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

Depois de algum tempo, você certamente verá pontos a serem melhorados — maneiras de tornar o código mais fácil
de acompanhar, remover algumas ineficiências e assim por diante. Isso é essencialmente edição e pode ajudar
enormemente a atingir seu objetivo de criar código de alta qualidade. Mas, na maioria das vezes, trabalhamos com
prazos apertados ("aqui está o problema, a solução é para ontem") e não há tempo para edição. É por isso que
escrever a documentação da API é uma oportunidade de edição.

Muitas vezes, ao escrever blocos de comentários em documentos, você revisita o problema. Às vezes, revisitar deixa
claro que, por exemplo, este terceiro parâmetro de um método é, na verdade, mais necessário do que o segundo, e o
segundo quase sempre será definido como verdadeiro por padrão, então faz sentido ajustar a interface do método e
trocá-los.

Escrever para ser lido significa escrever código, ou mesmo apenas a API, com a ideia de que outra pessoa o lerá. Só
esse fato já fará você editar e pensar em maneiras melhores de resolver o problema que você tem em mãos.

Falando em primeiros rascunhos, há também a ideia de "planejar jogar um fora". Pode parecer um pouco extremo no
início, mas faz muito sentido, especialmente quando você tem um projeto de missão crítica em mãos (e vidas
humanas dependem disso). A ideia é que a primeira solução que você encontrar deve ser descartada e então você
começa do zero. A primeira solução pode ser uma solução funcional, mas nada mais é do que um rascunho, um
exemplo de como resolver um problema. A segunda solução sempre será melhor, porque agora você tem uma
compreensão muito mais profunda do problema. Na segunda solução, você também não tem permissão para copiar
e colar da primeira, o que ajuda a evitar que você pegue atalhos ou se contente com a solução imperfeita.

Revisões por pares

Outra maneira de melhorar seu código é revisá-lo por pares. As revisões por pares podem ser formais e padronizadas,
até mesmo com o auxílio de ferramentas especializadas, e essa é uma ótima maneira de tornar as revisões uma parte
simplificada do processo de desenvolvimento. Mas não ter tempo para pesquisar e adotar ferramentas de revisão não
deve ser um obstáculo. Você pode simplesmente pedir ao desenvolvedor ao seu lado para dar uma olhada no seu
código ou pode orientá-lo.

Novamente, assim como acontece ao escrever documentos de API ou qualquer tipo de documentação, as revisões
por pares ajudam você a escrever um código mais claro, simplesmente porque você sabe que outra pessoa precisará
ler e entender o que você está fazendo.

As revisões por pares são uma boa prática não apenas porque o código resultante é melhor, mas também porque
tanto o revisor quanto o criador compartilham e trocam conhecimento e aprendem com a experiência e as abordagens
individuais de cada um.

Se você tem um escritório individual e não tem colegas para revisar seu código, isso também não deve ser um
obstáculo. Você sempre pode tornar público pelo menos parte do código ou simplesmente publicar um blog sobre um
trecho interessante dele e, assim, ter o mundo como seu revisor.

Avaliações por pares | 35


Machine Translated by Google [Link]

Outra boa prática é fazer com que seu sistema de controle de código-fonte (CVS, Subversion, Git)
envie uma notificação por e-mail à equipe sempre que alguém fizer check-in do código. A maioria
desses e-mails não será lida, mas de vez em quando pode ocorrer uma revisão espontânea por
pares quando alguém decide dar uma pausa no trabalho e dar uma olhada no código que você
acabou de fazer check-in.

Minificar…Em produção
Minificação é o processo de eliminar espaços em branco, comentários e outras partes não
essenciais do código JavaScript para diminuir o tamanho dos arquivos JavaScript que precisam
ser transferidos do servidor para o navegador. Isso geralmente é feito por uma ferramenta (um
minificador), como o Yahoo! YUICompressor ou o Closure Compiler do Google, e ajuda a acelerar
o tempo de carregamento da página. É importante minificar os scripts prontos para produção, pois
isso resulta em economias significativas, muitas vezes reduzindo o tamanho pela metade.

Veja como é um exemplo de código minificado (isso faz parte do utilitário Event da biblioteca YUI2):

[Link]=function(D,C,B,A){[Link]=D;[Link]=C||window;[Link]
=B;[Link]=A||[Link];[Link]=[];if(![Link]) {}var
E="_YUICEOnSubscribe";if(D!==E){[Link]=new
[Link](E,this,true);}...

Além de remover espaços em branco, novas linhas e comentários, os minificadores também


renomeiam variáveis com nomes mais curtos (mas somente quando for seguro), como os
parâmetros D, C, B e A no código anterior. Os minificadores podem renomear apenas variáveis
locais, pois renomear variáveis globais pode quebrar o código. Por isso, é uma boa prática usar
variáveis locais sempre que possível. Se você usar uma variável global, como uma referência
DOM, mais de uma ou duas vezes em uma função, é uma boa prática atribuí-la a uma variável local.
Isso acelerará as pesquisas ao resolver um nome de variável e, portanto, o código será mais
rápido durante o tempo de execução, além de ser melhor minimizado e baixado mais rapidamente.

Como observação lateral, o Google Closure Compiler também tentará minimizar variáveis globais
(no modo “avançado”), o que é arriscado e requer alguma atenção e disciplina da sua parte para
aproveitar essa minimização extra.

Minificar seu código de produção é importante porque melhora o desempenho da página, mas
você deve deixar essa tarefa para o minificador. É um erro tentar escrever código pré-minificado.
Você deve sempre usar nomes de variáveis descritivos, espaços em branco e recuos consistentes,
comentários e assim por diante. O código que você escrever será lido (por humanos), então facilite
a compreensão rápida para o mantenedor e deixe o minificador (a máquina) cuidar da redução do
tamanho dos arquivos.

36 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

Executar JSLint

O JSLint já foi apresentado no capítulo anterior e também é mencionado em diversas ocasiões


neste capítulo. A esta altura, você provavelmente já está convencido de que executar o JSLint no
seu código é um bom padrão de programação.

O que o JSLint procura? Ele procura violações de alguns dos padrões discutidos neste capítulo
( padrão de variável única, base para parseInt(), sempre usando chaves) e muitas outras violações,
incluindo:
• Código inacessível

• Usar variáveis antes de serem definidas •


Caracteres UTF inseguros

• Usando void, with ou eval •


Caracteres de escape incorretos em expressões regulares

O JSLint é escrito em JavaScript (e provavelmente passaria por uma verificação de JSLint); a boa
notícia é que ele está disponível como uma ferramenta web e como código para download para
diversas plataformas e interpretadores de JavaScript. Você pode baixá-lo e executá-lo localmente
em qualquer plataforma usando WSH (Windows Scripting Host, parte de todos os sistemas
Windows), JSC (JavaScriptCore, parte do Mac OSX) ou Rhino (interpretador de JavaScript da Mozilla).

É uma ótima ideia baixar o JSLint e integrá-lo ao seu editor de texto para que você crie o hábito de
executá-lo sempre que salvar um arquivo. (Criar um atalho de teclado para ele também pode
ajudar.)

Resumo
Neste capítulo, analisamos o que significa escrever código sustentável — um tópico importante
não apenas para o sucesso de um projeto de software, mas também para a sanidade e o bem-
estar dos desenvolvedores e de todos ao seu redor. Em seguida, abordamos algumas das
melhores práticas e padrões essenciais, incluindo:

• Reduzir o número de globais, idealmente para um por aplicação • Usar


uma única variável por função, o que ajuda a manter um olho em todas as variáveis em um único
local e evita surpresas causadas pelo comportamento de elevação de variáveis
• loops for , loops for-in , switches, “eval() é mau”, não aumentando o built-in
protótipos
• Seguindo convenções de codificação (espaço em branco consistente, recuo, uso de chaves e
ponto e vírgula, mesmo quando são opcionais) e convenções de nomenclatura (para
construtores, funções e variáveis)

Resumo | 37
Machine Translated by Google [Link]

O capítulo também discutiu algumas práticas adicionais, não relacionadas ao código,


mas ao processo de programação em geral: escrever comentários, criar documentação
de API gerada, conduzir revisões por pares, não tentar escrever código minimizado em
detrimento da legibilidade e sempre verificar seu código com JSLint.

38 | Capítulo 2: Essenciais
Machine Translated by Google [Link]

CAPÍTULO 3

Literais e Construtores

Os padrões de notação literal disponíveis em JavaScript permitem definições de objetos mais concisas,
expressivas e menos propensas a erros. Este capítulo discute literais como objetos, arrays e expressões
regulares, e por que eles são preferíveis ao uso de funções construtoras integradas equivalentes, como
Object() e Array(). O formato JSON é apresentado para demonstrar como literais de arrays e objetos
são usados para definir um formato de transferência de dados. O capítulo também discute construtores
personalizados e maneiras de impor o new para garantir que os construtores se comportem conforme o
esperado.

Para estender a mensagem principal do capítulo (que é evitar construtores e usar literais), há uma
discussão sobre os construtores wrapper integrados Number(), String() e Boolean() e como eles se
comparam aos valores primitivos number, string e boolean. Por fim, há uma breve observação sobre o
uso dos diferentes construtores integrados Error() .

Objeto Literal
Ao pensar em objetos em JavaScript, pense simplesmente em tabelas hash de pares chave-valor
(semelhantes ao que chamamos de "matrizes associativas" em outras linguagens). Os valores podem
ser primitivos ou outros objetos; em ambos os casos, são chamados de propriedades. Os valores
também podem ser funções, e nesse caso são chamados de métodos.

Os objetos personalizados que você cria em JavaScript (ou seja, os objetos nativos definidos pelo
usuário ) são mutáveis a qualquer momento. Muitas das propriedades dos objetos nativos integrados
também são mutáveis. Você pode começar com um objeto em branco e adicionar funcionalidades a ele
à medida que avança. A notação literal de objeto é ideal para esse tipo de criação de objetos sob demanda.

Considere o seguinte exemplo: //


comece com um objeto vazio var
dog = {};

// adicione uma
propriedade [Link] = "Benji";

39
Machine Translated by Google [Link]

// agora adicione um
método [Link] = function
() { return [Link];
};

No exemplo anterior, você começa com uma tela em branco — um objeto em branco. Em seguida, você
adiciona uma propriedade e um método a ele. A qualquer momento na vida do programa, você pode:

• Alterar os valores das propriedades e métodos, por exemplo:

[Link] = function () { //
redefine o método para retornar //
um valor codificado
return "Fido";
};
• Remover propriedades/métodos completamente:

excluir [Link];
• Adicione mais propriedades e métodos:

[Link] = function ()
{ return "Au!";
};
[Link] = verdadeiro;

Não é necessário começar com um objeto vazio. O padrão literal de objeto permite adicionar
funcionalidade ao objeto no momento da criação, como demonstra o próximo exemplo.

var dog =
{ nome: "Benji",
getName: function ()
{ return [Link];
}
};

Você verá "objeto em branco" e "objeto vazio" em alguns lugares ao longo


deste livro. É importante entender que isso é para simplificar e que não existe
objeto vazio em JavaScript. Mesmo o objeto {} mais simples já possui
propriedades e métodos herdados de [Link]. Por "vazio",
entenderemos um objeto que não possui propriedades próprias além das
herdadas.

A Sintaxe do Literal de Objeto. Se

você não está acostumado com a notação literal de objeto, ela pode parecer um pouco estranha no começo,
mas quanto mais você a usar, mais vai gostar. Em essência, as regras de sintaxe são:

• Coloque o objeto entre chaves ({ e }). • Separe as

propriedades e métodos dentro do objeto por vírgula. Uma vírgula final após o par nome-valor é permitida,
mas causa erros no IE, portanto, não a utilize.

40 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

• Separe os nomes e valores das propriedades com dois pontos. • Ao


atribuir o objeto a uma variável, não se esqueça do ponto e vírgula após o
fechando }.

Objetos de um construtor Não há

classes em JavaScript e isso permite grande flexibilidade, porque você não precisa saber nada sobre
seu objeto com antecedência; você não precisa de um "modelo" de classe.
Mas JavaScript também tem funções construtoras, que usam sintaxe semelhante à criação de objetos
baseada em classes em Java ou outras linguagens.

Você pode criar objetos usando suas próprias funções de construtor ou usando alguns dos
construtores integrados, como Object(), Date(), String() e assim por diante.

Aqui está um exemplo mostrando duas maneiras equivalentes de criar dois objetos idênticos:

// uma maneira -- usando um


literal var car = {goes: "far"};

// outra maneira -- usando um construtor interno //


aviso: este é um antipadrão var car
= new Object(); [Link]
= "far";

Como você pode ver neste exemplo, um benefício óbvio da notação literal é que ela é mais curta de
digitar. Outro motivo pelo qual o literal é o padrão preferido para a criação de objetos é que ele enfatiza
que os objetos são simplesmente hashes mutáveis e não algo que precisa ser criado a partir de uma
"receita" (de uma classe).

Outro motivo para usar literais em vez do construtor Object é que não há resolução de escopo. Como é
possível que você tenha criado um construtor local com o mesmo nome, o interpretador precisa
pesquisar a cadeia de escopo desde o local onde você está chamando Object() até encontrar o
construtor global Object .

Construtor de Objetos: Captura:

Você não tem motivo para usar o construtor new Object() quando pode usar um literal de objeto, mas
pode estar herdando código legado escrito por outros, portanto, você deve estar ciente de um "recurso"
deste construtor (ou ainda outro motivo para não usá-lo). O recurso em questão é que o construtor
Object() aceita um parâmetro e, dependendo do valor passado, pode decidir delegar a criação do objeto
a outro construtor interno e retornar um objeto diferente do esperado.

A seguir estão alguns exemplos de como passar um número, uma string e um valor booleano para new
Object(); o resultado é que você obtém objetos criados com um construtor diferente:

// Aviso: antipadrões à frente

// um objeto vazio
var o = new Object();

Objeto Literal | 41
Machine Translated by Google [Link]

[Link]([Link] === Objeto); // verdadeiro

// um objeto numérico
var o = new Object(1);
[Link]([Link] === Number); // true
[Link]([Link](2)); // "1.00"

// um objeto string
var o = new Object("Eu sou uma string");
[Link]([Link] === String); // true // objetos
normais não têm um método substring(), // mas
objetos string têm [Link](typeof
[Link]); // "função"

// um objeto booleano
var o = new Object(true);
[Link]([Link] === Boolean); // true

Esse comportamento do construtor Object() pode levar a resultados inesperados quando o valor
passado a ele é dinâmico e não é conhecido até o tempo de execução. Novamente, para concluir,
não use new Object(); em vez disso, use o literal de objeto mais simples e confiável.

Funções de construtor personalizadas

Além do padrão literal de objeto e das funções construtoras integradas, você pode criar objetos
usando suas próprias funções construtoras personalizadas, como demonstra o exemplo a seguir:

var adão = new Pessoa("Adão");


adã[Link](); // "Eu sou Adão"

Este novo padrão se parece muito com a criação de um objeto em Java usando uma classe chamada
Person. A sintaxe é semelhante, mas, na verdade, em JavaScript não há classes e Person é apenas
uma função.
Veja como a função construtora Person pode ser definida.

var Pessoa = função (nome) { [Link] =


nome; [Link] =
função () { return "Eu sou +
"
[Link];
};
};

Quando você invoca a função construtora com new, o seguinte acontece dentro da função:

• Um objeto vazio é criado e referenciado por esta variável, herdando o protótipo


da função.

• Propriedades e métodos são adicionados ao objeto referenciado por this. • O


objeto recém-criado referenciado por this é retornado no final implicitamente (se nenhum outro
objeto foi retornado explicitamente).

42 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

É como se algo assim acontecesse nos bastidores:


var Pessoa = função (nome) {

// cria um novo objeto //


usando o literal de objeto // var
this = {};

// adicione propriedades e métodos


[Link] = nome;
[Link] = função () { return "Eu
"
sou + [Link];
};

// retornar isto;
};

Para simplificar, neste exemplo, o método say() foi adicionado . O resultado é que sempre
que você chama new Person(), uma nova função é criada na memória. Isso é obviamente
ineficiente, pois o método say() não muda de uma instância para a outra.
A melhor opção é adicionar o método ao protótipo de Person:
[Link]ó[Link] = função () { return "Eu sou
"
+ [Link];
};

Falaremos mais sobre protótipos e herança nos próximos capítulos, mas lembre-se de que
membros reutilizáveis, como métodos, devem ir para o protótipo.
Há mais uma coisa que ficará clara mais adiante no livro, mas vale a pena mencionar aqui
para garantir a completude. Dissemos que dentro do construtor algo assim acontece nos
bastidores:
// var isto = {};

Isso não é toda a verdade, porque o objeto "vazio" não está realmente vazio; ele herdou do
protótipo da Pessoa . Então é mais como:
// var this = [Link]([Link]);

Discutiremos o que [Link]() significa mais adiante no livro.

Valores de retorno do construtor

Quando invocada com new, uma função construtora sempre retorna um objeto; por padrão, é
o objeto referenciado por this. Se você não adicionar nenhuma propriedade a this dentro do
seu construtor, um objeto "vazio" será retornado ("vazio", exceto por herdar do protótipo do
construtor).
Construtores retornam implicitamente this, mesmo quando você não tem uma instrução return
na função. Mas você pode retornar qualquer outro objeto de sua escolha. No próximo
exemplo, um novo objeto referenciado por that é criado e retornado.

Funções de Construtor Personalizadas | 43


Machine Translated by Google [Link]

var Objectmaker = função () {

// esta propriedade `name` será ignorada //


porque o construtor // decide
retornar outro objeto [Link] = "This is it";

// criando e retornando um novo objeto var


that = {};
[Link] = "E é isso"; return that;

};

// teste
var o = new Objectmaker();
[Link]([Link]); // "E é isso"

Como você pode ver, você tem a liberdade de retornar qualquer objeto em seus construtores,
desde que seja um objeto. Tentar retornar algo que não seja um objeto (como uma string ou
um booleano "false", por exemplo) não causará erro, mas será simplesmente ignorado, e o
objeto referenciado por "this" será retornado em seu lugar.

Padrões para a aplicação de novos


Como já mencionado, construtores ainda são apenas funções, mas invocados com new. O
que acontece se você esquecer new ao invocar um construtor? Isso não causará erros de
sintaxe ou de tempo de execução, mas pode levar a erros lógicos e comportamentos inesperados.
Isso ocorre porque, quando você esquece new, this dentro do construtor apontará para o objeto
global. (Em navegadores, this apontará para window.)

Quando seu construtor tem algo como [Link] e você o invoca sem new, você está, na
verdade, criando uma nova propriedade do objeto global chamada member , acessível por
meio de [Link] ou simplesmente member. Esse comportamento é altamente
indesejável, pois você sabe que deve sempre se esforçar para manter o namespace global
limpo.
// função
construtora Waffle()
{ [Link] = "yummy";
}

// um novo objeto
var bom_morning = new Waffle();
[Link](typeof bom_morning); // "objeto"
[Link](bom_morning.stastes); // "gostoso"

// antipadrão: //
esquecido `novo`
var bom_morning = Waffle();
[Link](typeof bom_morning); // "indefinido"
[Link]([Link]); // "gostoso"

44 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

Esse comportamento indesejado é corrigido no ECMAScript 5 e, no modo estrito, não apontará mais para o
objeto global. Se o ES5 não estiver disponível, ainda há algo que você pode fazer para garantir que uma
função construtora sempre se comporte como tal, mesmo se chamada sem new.

Convenção de nomenclatura

A alternativa mais simples é usar uma convenção de nomenclatura, conforme discutido no capítulo anterior,
onde você coloca a primeira letra em maiúscula nos nomes dos construtores (MyConstructor) e em minúscula
em funções e métodos “normais” (myFunction).

Usar that

Seguir uma convenção de nomenclatura pode certamente ajudar, mas apenas sugere e não impõe o
comportamento correto. Aqui está um padrão que ajuda você a garantir que seu construtor sempre se
comporte como um construtor. Em vez de adicionar todos os membros a this, você os adiciona a that e então
retorna that.

função Waffle() { var


that = {};
[Link] = "delícia";
return that;
}

Para objetos mais simples, você nem precisa de uma variável local como essa; você pode simplesmente
retornar um objeto de um literal como este:
function Waffle()
{ return
{ tastes: "yummy"
};
}

Usando qualquer uma das implementações acima, Waffle() sempre retorna um objeto, independentemente
de como ele é chamado:

var first = new Waffle(),


second = Waffle();
[Link]([Link]); // "delicioso"
[Link]([Link]); // "delicioso"

O problema com esse padrão é que o link para o protótipo é perdido, então quaisquer membros que você
adicionar ao protótipo Waffle() não estarão disponíveis para os objetos.

Observe que o nome da variável é apenas uma convenção; não faz parte da linguagem. Você pode
usar qualquer nome, enquanto outros nomes de variáveis comuns incluem self e me.

Padrões para Aplicação de Novos | 45


Machine Translated by Google [Link]

Construtor de Autoinvocação.
Para resolver a desvantagem do padrão anterior e ter propriedades de protótipo disponíveis
para os objetos de instância, considere a seguinte abordagem. No construtor, você verifica se
esta é uma instância do seu construtor e, caso contrário, o construtor se autoinvoca novamente,
desta vez corretamente com new:
função Waffle() {

se (!(esta instância de Waffle))


{ retornar novo Waffle();
}

[Link] = "delicioso";

}
[Link] = verdadeiro;

// testando invocações var


first = new Waffle(), second =
Waffle();

[Link]([Link]); // "delícia"
[Link]([Link]); // "delícia"

[Link]([Link]); // verdadeiro
[Link]([Link]); // verdadeiro

Outra maneira geral de verificar a instância é comparar com [Link] em vez de


codificar o nome do construtor.
if (!(esta instância de [Link])) { retornar
novos [Link]();
}

Este padrão aproveita o fato de que, dentro de cada função, um objeto chamado arguments é
criado, contendo todos os parâmetros passados à função quando ela foi invocada. E arguments
tem uma propriedade chamada callee, que aponta para a função que foi chamada. Esteja ciente
de que [Link] não é permitido no modo estrito do ES5, portanto, é melhor limitar seu
uso futuro e também remover quaisquer instâncias que você encontrar no código existente.

Literal de matriz
Arrays em JavaScript, como a maioria das outras coisas na linguagem, são objetos. Eles podem
ser criados com a função construtora interna Array(), mas também possuem uma notação
literal e, assim como a literal de objeto, a notação literal de array é mais simples e preferível.

Veja como você pode criar dois arrays com os mesmos elementos de duas maneiras diferentes:
usando o construtor Array() e usando o padrão literal.

46 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

// matriz de três elementos //


aviso: antipadrão var a =
new Array("itsy", "bitsy", "spider");

// exatamente o mesmo
array var a = ["itsy", "bitsy", "spider"];

[Link](typeof a); // "objeto", porque arrays são objetos


[Link]([Link] === Array); // verdadeiro

Sintaxe de Literal de
Matriz : A notação literal de matriz não é muito extensa: é apenas uma lista de elementos
delimitada por vírgulas, e toda a lista é delimitada por colchetes. Você pode atribuir qualquer
tipo de valor aos elementos da matriz, incluindo objetos ou outras matrizes.
A sintaxe literal de array é simples, direta e elegante. Afinal, um array é apenas uma lista de
valores indexados a zero. Não há necessidade de complicar as coisas (e escrever mais código)
incluindo um construtor e usando o operador new .

Curiosidade sobre o construtor de


array Mais um motivo para ficar longe de new Array() é evitar uma possível armadilha que
esse construtor tem guardado para você.

Quando você passa um único número para o construtor Array() , ele não se torna o valor do
primeiro elemento do array. Em vez disso, ele define o comprimento do array. Isso significa
que new Array(3) cria um array com comprimento 3, mas sem elementos reais. Se você tentar
acessar qualquer um dos elementos, obterá o valor undefined porque os elementos não
existem. O exemplo de código a seguir mostra o comportamento diferente quando você usa o
literal e o construtor com um único valor.
// uma matriz de um elemento
var a = [3];
[Link]([Link]); // 1
[Link](a[0]); // 3

// uma matriz de três elementos


var a = new Array(3);
[Link]([Link]); // 3
[Link](typeof a[0]); // "indefinido"

Embora esse comportamento possa ser um pouco inesperado, ele piora quando você passa
um número de ponto flutuante para new Array() em vez de um inteiro. Isso resulta em um erro
porque o ponto flutuante não é um valor válido para o comprimento do array:
// usando literal de array
var a = [3.14];
[Link](a[0]); // 3.14

Matriz Literal | 47
Machine Translated by Google [Link]

var a = new Array(3.14); // RangeError: comprimento de array inválido


[Link](typeof a); // "indefinido"

Para evitar possíveis erros ao criar matrizes dinâmicas em tempo de execução, é muito mais
seguro usar a notação literal de matriz.

Existem alguns usos inteligentes para o construtor Array() , por exemplo,


para repetir strings. O trecho a seguir retorna uma string com 255 espaços
em branco (por que não 256? Deixo o leitor curioso pensar):

var branco = novo Array(256).join(' ');

Verifique se há array-
ness. Usar o operador typeof com operandos de array retorna “objeto”.
[Link](typeof [1, 2]); // "objeto"

Embora esse comportamento faça sentido (matrizes são objetos), não é muito útil. Muitas vezes, você precisa saber se um valor é
realmente uma matriz. Às vezes, você pode ver código verificando a presença da propriedade length ou de algum método de matriz,
como slice(), para determinar a "qualidade de uma matriz". Mas essas verificações não são robustas, pois não há razão para que
um objeto que não seja uma matriz não tenha propriedades e métodos com os mesmos nomes. Além disso, às vezes, as pessoas
usam a matriz instanceof, mas essa verificação funciona incorretamente quando usada entre quadros em algumas versões do IE.

O ECMAScript 5 define um novo método [Link](), que retorna verdadeiro se o


argumento for um array. Por
exemplo: [Link]([]); // verdadeiro

// tentando enganar o cheque //


com um objeto do tipo array

[Link]({ length: 1, "0":


1, slice: function () {} }); // false

Se este novo método não estiver disponível em seu ambiente, você pode fazer a verificação
chamando o método [Link]() . Se você invocar o método call() de toString
no contexto de um array, ele deverá retornar a string "[object Array]". Se o contexto for um
objeto, ele deverá retornar a string "[object Object]". Então, você pode fazer algo assim: if
(typeof
[Link] === "undefined") { [Link]
= function (arg) {
retornar [Link](arg) === "[matriz de objetos]";
};
}

48 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

JSON
Agora que você está familiarizado com os literais de array e objeto discutidos anteriormente, vamos
dar uma olhada em JSON, que significa JavaScript Object Notation e é um formato de transferência
de dados. É leve e prático para trabalhar em diversas linguagens, especialmente em JavaScript.

Na verdade, não há nada de novo para aprender sobre JSON. É apenas uma combinação da notação
de array e literal de objeto. Aqui está um exemplo de uma string JSON:
{"nome": "valor", "algum": [1, 2, 3]}

A única diferença de sintaxe entre JSON e o literal de objeto é que os nomes de propriedades
precisam estar entre aspas para serem JSON válidos. Em literais de objeto, as aspas são necessárias
apenas quando os nomes de propriedades não são identificadores válidos; por exemplo, quando
contêm espaços {"first name": "Dave"}.

Em strings JSON você não pode usar funções ou literais de expressões regulares.

Trabalhando com JSON

Como mencionado no capítulo anterior, não é recomendado avaliar cegamente qualquer string JSON com eval() devido
às implicações de segurança. É melhor usar o método [Link]() , que faz parte da linguagem desde o ES5 e é
fornecido nativamente pelos mecanismos JavaScript em navegadores modernos. Para mecanismos JavaScript mais
antigos, você pode usar a biblioteca [Link] (http:// [Link]/ [Link]). para obter acesso ao objeto JSON e seus
métodos.

// uma string JSON de


entrada var jstr = '{"mykey": "my value"}';

// antipadrão
var data = eval('(' + jstr + ')');

// var data
preferencial = [Link](jstr);

[Link]([Link]); // "meu valor"

Se você já usa uma biblioteca JavaScript, é provável que ela venha com um utilitário para analisar
JSON, então você pode não precisar da biblioteca [Link] adicional. Por exemplo, usando o YUI3,
você pode fazer:
// uma string JSON de
entrada var jstr = '{"mykey": "my value"}';

// analisa a string e a transforma em um objeto //


usando uma instância
YUI YUI().use('json-parse', function (Y)
{ var data = [Link](jstr);
[Link]([Link]); // "meu valor" });

JSON | 49
Machine Translated by Google [Link]

No jQuery, existe o método parseJSON() :


// uma string JSON de
entrada var jstr = '{"mykey": "my value"}';

var data = [Link](jstr);


[Link]([Link]); // "meu valor"

O oposto do método [Link]() é [Link](). Ele recebe qualquer objeto ou array (ou um
primitivo) e o serializa em uma string JSON.
var dog =
{ nome: "Fido",
data de nascimento:
nova data(), pernas: [1, 2, 3, 4]
};

var jsonstr = [Link](cachorro);

// jsonstr agora é: //
{"name":"Fido","dob":"2010-04-11T22:36:22.436Z","legs":[1,2,3,4]}

Expressão regular literal


Expressões regulares em JavaScript também são objetos, e você tem duas opções para criá-las:

• Usando o novo construtor RegExp() •


Usando o literal de expressão regular

O código de exemplo a seguir demonstra duas maneiras de criar uma expressão regular que
corresponde a uma barra invertida:

// literal de expressão regular var


re = /\\/gm;

// construtor var
re = new RegExp("\\\\", "gm");

Como você pode ver, a notação literal da expressão regular é mais curta e não força você a pensar
em termos de construtores semelhantes a classes. Portanto, é preferível usar o literal.

Além disso, ao usar o construtor RegExp() , você também precisa escapar aspas e, frequentemente,
precisa escapar duas vezes as barras invertidas, como mostrado no trecho anterior, onde
precisamos de quatro barras invertidas para corresponder a uma única. Isso torna seus padrões de
expressões regulares mais longos e difíceis de ler e modificar. Expressões regulares já são bastante
complexas para começar, e qualquer chance de simplificá-las é bem-vinda, então é melhor usar a
notação literal.

50 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

Sintaxe Literal de Expressão Regular


A notação literal de expressão regular usa barras para envolver o padrão de expressão
regular usado para correspondência. Após a segunda barra, você pode colocar os
modificadores de padrão na forma de letras sem aspas:

• g—Correspondência global •
m—Multilinha

• i—Correspondência sem distinção entre maiúsculas e minúsculas

Os modificadores de padrão podem aparecer em qualquer ordem ou combinação:

var re = /padrão/ gmi;

Usar o literal de expressão regular ajuda a escrever código mais conciso ao chamar métodos como
[Link]() que aceitam objetos de expressão regular como parâmetros.

var no_letters = "abc123XYZ".replace(/[az]/gi, "");


[Link](no_letters); // 123

Um motivo para usar new RegExp() é que o padrão não é conhecido antecipadamente, mas é criado como uma
string em tempo de execução.

Outra distinção entre o literal de expressão regular e o construtor é que o literal cria um objeto apenas uma vez
durante o tempo de análise. Se você criar a mesma expressão regular em um loop, o objeto criado anteriormente
será retornado com todas as suas propriedades (como lastIndex) já definidas desde a primeira vez. Considere o
exemplo a seguir como uma ilustração de como o mesmo objeto é retornado duas vezes.

função getRE() { var


re = /[az]/; [Link] =
"bar"; return re;

var reg = getRE(), re2


= getRE();

[Link](reg === re2); // verdadeiro


[Link] = "baz";
[Link]([Link]); // "baz"

Esse comportamento mudou no ES5 e o literal também cria novos objetos. O


comportamento também foi corrigido em muitos ambientes de navegador,
portanto, não é confiável.

E uma última observação: chamar RegExp() sem new (como uma função, não como um construtor) se comporta
da mesma forma que com new.

Expressão Regular Literal | 51


Machine Translated by Google [Link]

Envoltórios primitivos
JavaScript possui cinco tipos de valores primitivos: number, string, boolean, null e undefined.
Com exceção de null e undefined, os outros três possuem os chamados objetos wrapper
primitivos. Os objetos wrapper podem ser criados usando os construtores internos Number(),
String() e Boolean().

Para ilustrar a diferença entre um número primitivo e um objeto numérico, considere o seguinte
exemplo:
// um número primitivo var
n = 100;
[Link](typeof n); // "número"

// um objeto Number
var nobj = new Number(100);
[Link](typeof nobj); // "objeto"

Os objetos wrapper têm algumas propriedades e métodos úteis — por exemplo, objetos numéricos têm
métodos como toFixed() e toExponential(). Objetos string têm os métodos substring(), charAt() e
toLowerCase() (entre outros) e uma propriedade de comprimento . Esses métodos são convenientes
e podem ser um bom motivo para decidir criar um objeto, em vez de usar um primitivo. Mas os métodos
também funcionam com primitivos — assim que você invoca um método, o primitivo é temporariamente
convertido em um objeto em segundo plano e se comporta como se fosse um objeto. // uma string
primitiva pode ser usada como um objeto var s =
"hello"; [Link]([Link]()); // "HELLO"

// o valor em si pode atuar como um objeto


"monkey".slice(3, 6); // "key"

// o mesmo para números


(22 / 7).toPrecision(3); // "3.14"

Como os primitivos podem atuar como objetos assim que você precisar deles, muitas vezes
não há motivo para usar os construtores wrapper mais verbosos. Por exemplo, você não
precisa escrever new String("hi"); quando pode simplesmente usar "hi":
// evite estes: var s
= new String("minha string"); var n = new
Number(101); var b = new
Boolean(true);

// melhor e mais simples: var


s = "minha string"; var n =
101; var b =
true;

Um motivo para usar objetos wrapper é quando você deseja aumentar o valor e persistir o
estado. Como primitivos não são objetos, eles não podem ser aumentados com propriedades.

52 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

// string primitiva var


greet = "Olá";

// o primitivo é convertido em um objeto //


para usar o método split() [Link](' ')
[0]; // "Olá"

// tentar aumentar um primitivo não é um erro [Link]


= true;

// mas na verdade não funciona


typeof [Link]; // "indefinido"

No trecho anterior, greet foi convertido temporariamente em um objeto para que o acesso à
propriedade/método funcionasse sem erros. Por outro lado, se greet fosse definido como um objeto
usando new String(), a propriedade smile aumentada teria funcionado conforme o esperado.
Aumentar uma string, um número ou um valor booleano raramente é usado e, a menos que seja
isso que você precisa, provavelmente não precisa dos construtores wrapper.

Quando usados sem new, os construtores wrapper convertem o argumento passado a eles em um
valor primitivo: typeof
Number(1); // "número" typeof
Number("1"); // "número" typeof
Number(new Number()); // "número" typeof
String(1); // "string" typeof
Boolean(1); // "boolean"

Objetos de erro

JavaScript possui diversos construtores de erro integrados, como Error(), SyntaxError(), TypeError() e outros, que são
usados com a instrução throw . Os objetos de erro criados por esses construtores têm as seguintes propriedades:

nome

A propriedade de nome da função construtora que criou o objeto; pode ser o “Error” geral ou
um construtor mais especializado, como “RangeError”
mensagem
A string passada ao construtor ao criar o objeto

Os objetos de erro têm outras propriedades, como o número da linha e o nome do arquivo onde o
erro ocorreu, mas essas propriedades extras são extensões do navegador implementadas de forma
inconsistente entre navegadores e, portanto, não são confiáveis.

Por outro lado, throw funciona com qualquer objeto, não necessariamente um objeto criado com
um dos construtores de erro, então você pode optar por lançar seus próprios objetos. Esses objetos
de erro podem ter as propriedades "nome", "mensagem" e qualquer outro tipo de informação que
você queira passar para ser tratada pela instrução catch . Você pode ser criativo com seus objetos
de erro personalizados e usá-los para restaurar o estado normal do aplicativo.

Objetos de erro | 53
Machine Translated by Google [Link]

try
{ // algo ruim aconteceu, lance um erro throw
{ name:
"MyErrorType", // tipo de erro personalizado
message: "oops",
extra: "Isso foi bastante embaraçoso",
remedy: genericErrorHandler // quem deve lidar com isso

}; } catch (e)
{ // informar o usuário
alert([Link]); // "oops"

// manipula o erro com elegância


[Link](); // chama genericErrorHandler()
}

Os construtores de erro invocados como funções (sem new) se comportam da mesma forma que os
con-construtores (com new) e retornam os mesmos objetos de erro.

Resumo
Neste capítulo, você aprendeu sobre diferentes padrões literais, que são alternativas mais simples ao
uso de funções construtoras. O capítulo abordou:

• Notação literal de objeto — Uma maneira elegante de criar objetos como pares de chave-valor
delimitados por vírgulas, envoltos em chaves.
• Funções de construtor — Construtores integrados (que quase sempre têm uma notação literal
melhor e mais curta) e construtores personalizados

• Maneiras de garantir que construtores personalizados sempre se comportem como se


fossem invocados com new • Notação literal de array — listas de valores delimitadas por
vírgulas dentro de colchetes • JSON — um formato de dados que
consiste em literais de objeto e
array • Literais de expressão regular • Outros construtores internos a serem evitados: String(), Number(), Boolean() e o
diferentes construtores Error()

Em geral, com exceção do construtor Date() , raramente há necessidade de usar os outros construtores
integrados. A tabela a seguir resume esses construtores e seus padrões literais correspondentes e
preferenciais.

54 | Capítulo 3: Literais e Construtores


Machine Translated by Google [Link]

Construtores integrados (evitar) Literais e primitivos (preferencialmente)

var o = novo Objeto(); var o = {};


var a = new Array(); var a = [];
var re = new RegExp( var re = /[az]/g;
"[az]",
"g"
);
vars = new String(); var var s = "";
n = novo Número(); var n = 0;
var b = novo Booleano(); var b = falso;
lançar novo Erro("uh-oh"); lançar {
nome: "Erro",
mensagem: "uh-oh"
};
... ou

lançar erro("uh-oh");

Resumo | 55
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 4

Funções

Dominar funções é uma habilidade essencial para o programador JavaScript, pois a linguagem oferece
muitas utilidades para elas. Elas realizam uma variedade de tarefas para as quais outras linguagens
podem ter sintaxe especial.

Neste capítulo, você aprenderá sobre as diferentes maneiras de definir uma função em JavaScript,
sobre expressões e declarações de função e verá como funcionam o escopo local e o içamento de
variáveis. Em seguida, você aprenderá sobre diversos padrões que auxiliam suas APIs (fornecendo
interfaces melhores para suas funções), inicializações de código (com menos variáveis globais) e
desempenho (em outras palavras, evitando trabalho).

Vamos nos aprofundar nas funções, começando pela revisão e esclarecimento dos conceitos básicos importantes.

Fundo
Há duas características principais das funções em JavaScript que as tornam especiais: a primeira é
que as funções são objetos de primeira classe e a segunda é que elas fornecem escopo.

Funções são objetos que:

• Podem ser criados dinamicamente em tempo de execução, durante a execução do programa.


• Podem ser atribuídos a variáveis, podem ter suas referências copiadas para outras variáveis,
podem ser aumentados e, exceto em alguns casos especiais, podem ser excluídos.
• Podem ser passados como argumentos para outras funções e também podem ser retornados por outras
funções

• Podem ter suas próprias propriedades e métodos

Portanto, pode acontecer que uma função A, sendo um objeto, tenha propriedades e métodos, um dos
quais seja outra função B. Então, B pode aceitar uma função C como argumento e, quando executada,
pode retornar outra função D. À primeira vista, são muitas funções para acompanhar. Mas quando
você se sente confortável com as várias aplicações das funções, passa a apreciar o poder, a
flexibilidade e a expressividade.

57
Machine Translated by Google [Link]

que as funções podem oferecer. Em geral, quando você pensa em uma função em JavaScript, pense em
um objeto, cuja única característica especial é que esse objeto é invocável, ou seja, pode ser executado.

O fato de que funções são objetos se torna óbvio quando você vê o construtor new Function() em ação:

// antipadrão //
apenas para fins de
demonstração var add = new Function('a, b', 'return
a + b'); add(1, 2); // retorna 3

Neste código, não há dúvida de que add() é um objeto; afinal, ele foi criado por um construtor. Usar o
construtor Function() não é uma boa ideia (é tão ruim quanto eval()) , pois o código é passado como uma
string e avaliado. Também é inconveniente de escrever (e ler), pois você precisa escapar aspas e tomar
cuidado extra se quiser recuar o código dentro da função corretamente para facilitar a leitura.

A segunda característica importante é que as funções fornecem escopo. Em JavaScript, não há escopo
local para chaves; em outras palavras, blocos não criam escopo. Há apenas escopo para funções.
Qualquer variável definida com var dentro de uma função é uma variável local, invisível fora da função.
Dizer que chaves não fornecem escopo local significa que, se você definir uma variável com var dentro
de uma condição if ou dentro de um loop for ou while , isso não significa que a variável seja local para
aquele if ou for. Ela é local apenas para a função de encapsulamento e, se não houver função de
encapsulamento, ela se torna uma variável global.
Conforme discutido no Capítulo 2, minimizar o número de globais é um bom hábito, portanto, funções
são indispensáveis quando se trata de manter o escopo da variável sob controle.

Desambiguação da terminologia Vamos

dedicar um momento para discutir a terminologia que envolve o código usado para definir uma função,
porque usar nomes precisos e acordados é tão importante quanto o código quando falamos sobre
padrões.

Considere o seguinte trecho: //


expressão de função nomeada
var add = function add(a, b) { return
a + b;
};

O código anterior mostra uma função que usa uma expressão de função nomeada.

Se você pular o nome (a segunda adição no exemplo) na expressão de função, obterá uma expressão
de função sem nome , também conhecida simplesmente como expressão de função ou, mais comumente,
como função anônima. Um exemplo é:
// expressão de função, também conhecida como função
anônima var add = function (a,
b) { return a + b;
};

58 | Capítulo 4: Funções
Machine Translated by Google [Link]

Portanto, o termo mais amplo é “expressão de função” e a “expressão de função nomeada” é um caso
específico de uma expressão de função, que define o nome opcional.

Ao omitir o segundo add e terminar com uma expressão de função sem nome, isso não afetará a definição
e as invocações consecutivas da função. A única diferença é que a propriedade name do objeto de função
será uma string em branco. A propriedade name é uma extensão da linguagem (não faz parte do padrão
ECMA), mas está amplamente disponível em muitos ambientes. Se você mantiver o segundo add, a
propriedade [Link] conterá a string "add". A propriedade name é útil ao usar depuradores, como o
Firebug, ou ao chamar a mesma função recursivamente a partir dela mesma; caso contrário, você pode
simplesmente ignorá-la.

Por fim, temos as declarações de função. Elas se parecem mais com as funções usadas em outras
linguagens: function
foo() { // o corpo
da função vai aqui
}

Em termos de sintaxe, expressões de função nomeadas e declarações de função parecem semelhantes,


especialmente se você não atribuir o resultado da expressão de função a uma variável (como veremos no
padrão de retorno de chamada mais adiante neste capítulo). Às vezes, não há outra maneira de diferenciar
uma declaração de função de uma expressão de função nomeada a não ser observando o contexto em que
a função ocorre, como você verá no próximo capítulo.
seção.

Há uma diferença de sintaxe entre os dois no ponto e vírgula final. O ponto e vírgula não é necessário em
declarações de funções, mas é obrigatório em expressões de funções, e você deve sempre usá-lo, mesmo
que o mecanismo automático de inserção de ponto e vírgula possa fazer isso por você.

O termo literal de função também é comumente usado. Pode significar tanto


uma expressão de função quanto uma expressão de função nomeada. Devido
a essa ambiguidade, provavelmente é melhor não usá-lo.

Declarações versus Expressões: Nomes e Hoisting. Então, o que você

deve usar — declarações de função ou expressões de função? Nos casos em que sintaticamente não é
possível usar uma declaração, esse dilema está resolvido. Exemplos incluem passar um objeto de função
como parâmetro ou definir métodos em literais de objeto:

// esta é uma expressão de função, //


passada como um argumento para a função `callMe`
callMe(function () {
// Eu sou uma expressão de função sem nome //
também conhecida como uma função anônima });

Contexto | 59
Machine Translated by Google [Link]

// esta é uma expressão de função nomeada


callMe(function me() {
// Eu sou uma expressão de função
nomeada // e meu nome
é "eu" });

// outra expressão de função var


myobject = { say:
function () {
// Eu sou uma expressão de função
}
};

Declarações de função só podem aparecer em "código de programa", ou seja, dentro dos corpos
de outras funções ou no espaço global. Suas definições não podem ser atribuídas a variáveis
ou propriedades, nem aparecer em invocações de função como parâmetros. Aqui está um
exemplo do uso permitido de declarações de função, onde todas as funções foo(), bar() e local()
são definidas usando o padrão de declaração de função: // escopo
global função
foo() {}

função local() { //
escopo local
função bar() {}
return bar;
}

Propriedade "nome" da função:


Outro fator a ser considerado ao escolher um padrão de definição de função é a disponibilidade
da propriedade " nome" somente leitura. Novamente, essa propriedade não é padrão, mas está
disponível em muitos ambientes. Em declarações de função e expressões de função nomeada,
a propriedade "nome" é definida. Em expressões de função anônimas, ela depende da
implementação; pode ser indefinida (IE) ou definida com uma string vazia (Firefox, WebKit):

função foo() {} // declaração var bar =


function () {}; // expressão var baz = function
baz() {}; // expressão nomeada

[Link]; // "foo"
[Link]; // ""
[Link]; // "baz"

A propriedade name é útil ao depurar código no Firebug ou em outros depuradores. Quando o


depurador precisa mostrar um erro em uma função, ele pode verificar a presença da propriedade
name e usá-la como um indicador. A propriedade name também é usada para chamar a mesma
função recursivamente de dentro dela mesma. Se você não estiver interessado nesses dois
casos, uma expressão de função sem nome seria mais fácil e menos prolixa.

60 | Capítulo 4: Funções
Machine Translated by Google [Link]

O argumento contra declarações de função e a razão para preferir expressões de função é que
as expressões destacam que funções são objetos como todos os outros objetos e não alguma
construção de linguagem especial.

É tecnicamente possível usar uma expressão de função nomeada e atribuí-la a


uma variável com um nome diferente, por exemplo:
var foo = função bar() {};

Entretanto, o comportamento desse uso não é implementado corretamente em


alguns navegadores (IE), por isso não é recomendado usar esse padrão.

Elevação de Funções:

Da discussão anterior, você pode concluir que o comportamento das declarações de funções é
praticamente equivalente ao de uma expressão de função nomeada . Isso não é exatamente
verdade, e a diferença reside no comportamento de elevação.

O termo içamento não é definido no ECMAScript, mas é comum e uma boa


maneira de descrever o comportamento.

Como você sabe, todas as variáveis, independentemente de onde sejam declaradas no corpo da
função, são içadas para o topo da função nos bastidores. O mesmo se aplica às funções, pois
são apenas objetos atribuídos a variáveis. O único problema é que, ao usar uma declaração de
função, a definição da função também é içada, não apenas sua declaração. Considere este
trecho: // antipattern // apenas para
ilustração

// funções globais
function foo()
{ alert('global foo');

} função bar()
{ alert('barra global');
}

função hoistMe() {

[Link](typeof foo); // "função"


[Link](typeof bar); // "indefinido"

foo(); // "local foo"


bar(); // TypeError: bar não é uma função

// declaração de função: //
a variável 'foo' e sua implementação são içadas

Contexto | 61
Machine Translated by Google [Link]

função foo()
{ alerta('local foo');
}

// expressão de função: //
somente a variável 'bar' é içada // não a
implementação var bar =
function () { alert('local
bar');
};

} hoistMe();

Neste exemplo, você vê que, assim como acontece com variáveis normais, a mera presença de foo e
bar em qualquer lugar da função hoistMe() as move para o topo, sobrescrevendo as funções globais
foo e bar. A diferença é que a definição local de foo() é elevada para o topo e funciona bem; embora
seja definida posteriormente. A definição de bar() não é elevada, apenas sua declaração. É por isso
que, até que a execução do código alcance a definição de bar() , ela permanece indefinida e não pode
ser usada como função (embora ainda impeça que a função global bar() seja "vista" na cadeia de
escopo).

Agora que já abordamos o contexto e a terminologia necessários em torno das funções, vamos ver
alguns dos bons padrões relacionados a funções que o JavaScript oferece, começando pelo padrão
de retorno de chamada. Novamente, é importante lembrar as duas características especiais das
funções em JavaScript:

• Eles são objetos. •


Eles fornecem escopo local.

Padrão de retorno de chamada

Funções são objetos, o que significa que podem ser passadas como argumentos para outras funções.
Quando você passa a função introduceBugs() como parâmetro para a função writeCode(), em algum
momento é provável que writeCode() execute (ou chame) introduceBugs(). Nesse caso, introduceBugs()
é chamada de função de retorno de chamada ou simplesmente uma função de retorno de chamada.
ligar de volta:

function writeCode(callback) { //
faça algo...
callback(); // ...

função introduceBugs() { // ...


criar bugs
}

escreverCódigo(introduzirBugs);

62 | Capítulo 4: Funções
Machine Translated by Google [Link]

Observe como introduceBugs() é passado como argumento para writeCode() sem os parênteses.
Os parênteses executam uma função, enquanto neste caso queremos passar apenas uma
referência à função e deixar que writeCode() a execute (em outras palavras, a chame de volta)
quando apropriado.

Um Exemplo de

Callback Vamos pegar um exemplo e começar sem um callback primeiro e, em seguida,


refatorar. Imagine que você tem uma função de propósito geral que realiza um trabalho complexo
e retorna um grande conjunto de dados como resultado. Essa função genérica poderia ser
chamada, por exemplo, findNodes(), e sua tarefa seria rastrear a árvore DOM de uma página e
retornar um array de elementos da página que
sejam do seu interesse: var
findNodes = function() { var i = 100000, // loop grande e pesado
nós = [], // armazena o resultado
encontrado; // o próximo nó
encontrado
while
(i) { i -= 1; // lógica complexa
aqui... nó[Link](found);

} retornar nós;
};

É uma boa ideia manter esta função genérica e fazer com que ela simplesmente retorne um
array de nós DOM, sem fazer nada com os elementos em si. A lógica de modificação de nós
poderia estar em uma função diferente, por exemplo, uma função chamada hide() que, como o
nome sugere, oculta os nós da página:
var hide = função (nós) {
var i = 0, max = nó[Link]; para (;
i < max; i += 1)
{ nós[i].[Link] = "nenhum";
}
};

// executando as funções
hide(findNodes());

Esta implementação é ineficiente, pois hide() precisa percorrer novamente o array de nós
retornado por findNodes(). Seria mais eficiente se você pudesse evitar esse loop e ocultar os
nós assim que os selecionasse em findNodes(). Mas se você implementar a lógica de ocultação
em findNodes(), ela deixará de ser uma função genérica devido ao acoplamento da lógica de
recuperação e modificação. Entre no padrão de retorno de chamada — você passa a lógica de
ocultação de nós como uma função de retorno de chamada e delega sua execução:
// findNodes() refatorado para aceitar um retorno de
chamada var findNodes = function (callback)
{ var i = 100000,
nodes = [],
found;

Padrão de retorno de chamada | 63


Machine Translated by Google [Link]

// verifique se o retorno de chamada


pode ser chamado if (typeof callback !==
"function") { callback = false;
}

enquanto (i)
{ i -= 1;

// lógica complexa aqui...

// agora retorno de chamada:

if (retorno de chamada)

{ retorno de chamada(encontrado);

nó[Link](encontrado);

} retornar nós;
};

A implementação é simples; a única tarefa adicional que findNodes() executa é verificar se um retorno
de chamada opcional foi fornecido e, em caso afirmativo, executá-lo. O retorno de chamada é opcional,
portanto, o findNodes() refatorado ainda pode ser usado como antes e não danificará o código antigo
que depende da API antiga.

A implementação hide() será muito mais simples agora porque não precisa fazer um loop pelos nós: //
uma função de
retorno de chamada var
hide = function (node) {
[Link] = "nenhum";
};

// encontre os nós e oculte-os conforme você


avança findNodes(hide);

O retorno de chamada pode ser uma função existente, como mostrado no código anterior, ou pode ser
uma função anônima, que você cria ao chamar a função principal. Por exemplo, veja como você pode
mostrar nós usando a mesma função genérica findNodes() : // passando um retorno de
chamada anônimo findNodes(function
(node) { [Link] =
"block"; });

Retornos de chamada e escopo

Nos exemplos anteriores, a parte onde o retorno de chamada é executado era assim:

callback(parâmetros);

Embora isso seja simples e seja bom o suficiente em muitos casos, geralmente há cenários em que o
retorno de chamada não é uma função anônima única ou uma função global, mas é uma

64 | Capítulo 4: Funções
Machine Translated by Google [Link]

método de um objeto. Se o método de retorno de chamada usar this para se referir ao objeto ao qual pertence,
isso pode causar um comportamento inesperado.

Imagine que o retorno de chamada é a função paint(), que é um método do objeto chamado
meuaplicativo:

var myapp = {};


[Link] = "verde";
[Link] = função (nó) {
[Link] = [Link];
};

A função findNodes() faz algo assim: var findNodes = function


(callback) { // ... if (typeof callback ===

"function") { callback(found);

} // ...
};

Se você chamar findNodes([Link]), não funcionará como esperado, pois [Link] não estará definido.
O objeto this se referirá ao objeto global, pois findNodes() é uma função global. Se findNodes() fosse um
método de um objeto chamado dom (como [Link]()), então this dentro do retorno de chamada se
referiria a dom em vez do myapp esperado.

A solução para esse problema é passar a função de retorno de chamada e, além disso, passar o objeto ao
qual esse retorno de chamada pertence:

findNodes([Link], meuaplicativo);

Então você também precisa modificar findNodes() para vincular o objeto que você passou:

var findNodes = função (retorno de chamada, callback_obj) {


//...
se (tipo de retorno de chamada === "função") {
[Link](callback_obj, encontrado);

} // ...
};

Haverá mais sobre os tópicos de vinculação e uso de call() e apply() nos próximos capítulos.

Outra opção para passar um objeto e um método para ser usado como retorno de chamada é passar o método
como uma string, para não repetir o objeto duas vezes. Em outras palavras: findNodes([Link],
myapp);

pode se tornar:

findNodes("pintura", meuaplicativo);

Então findNodes() faria algo parecido com isto:

Padrão de retorno de chamada | 65


Machine Translated by Google [Link]

var findNodes = função (retorno de chamada, callback_obj) {

se (tipo de retorno de chamada === "string") {


retorno de chamada = callback_obj[retorno de chamada];
}

//...
se (tipo de retorno de chamada === "função") {
[Link](callback_obj, encontrado);

} // ...
};

Ouvintes de Eventos Assíncronos O padrão

de retorno de chamada tem muitos usos cotidianos; por exemplo, ao anexar um ouvinte de eventos a um elemento
em uma página, você está, na verdade, fornecendo um ponteiro para uma função de retorno de chamada que
será chamada quando o evento ocorrer. Aqui está um exemplo simples de como [Link]() é passado como
um retorno de chamada ao ouvir o evento de clique do documento:

[Link]("clique", [Link], false);

A maior parte da programação do navegador do lado do cliente é orientada a eventos. Quando a página termina
de carregar, ela dispara um evento de carregamento . Em seguida, o usuário interage com a página e aciona
vários eventos, como clique, pressionamento de tecla, passagem do mouse, movimentação do mouse e assim
por diante. JavaScript é especialmente adequado para programação orientada a eventos, devido ao padrão de
retorno de chamada, que permite que seus programas funcionem de forma assíncrona, ou seja, fora de ordem.

"Não nos ligue, nós ligamos para você" é uma frase famosa em Hollywood, onde muitos candidatos fazem testes
para o mesmo papel em um filme. Seria impossível para a equipe de elenco atender ligações de todos os
candidatos o tempo todo. No JavaScript assíncrono orientado a eventos, ocorre um fenômeno semelhante. Só
que, em vez de fornecer seu número de telefone, você fornece uma função de retorno de chamada para ser
chamada quando for a hora certa. Você pode até fornecer mais retornos de chamada do que o necessário, pois
certos eventos podem nunca acontecer.
Por exemplo, se o usuário nunca clicar em “Comprar agora!”, sua função que valida o formato do número do
cartão de crédito nunca será chamada de volta.

Tempos limite

Outro exemplo do padrão de retorno de chamada em ação é quando você usa os métodos de tempo limite
fornecidos pelo objeto de janela do navegador: setTimeout() e setInterval(). Esses métodos também aceitam e
executam retornos de chamada: var thePlotThickens =

function () { [Link]('500ms later...');

};
setTimeout(thePlotThickens, 500);

Observe novamente como a função thePlotThickens é passada como uma variável, sem parênteses, porque
você não quer que ela seja executada imediatamente, mas simplesmente quer apontar para ela

66 | Capítulo 4: Funções
Machine Translated by Google [Link]

para uso posterior por setTimeout(). Passar a string "thePlotThickens()" em vez de um ponteiro de função
é um antipadrão comum, semelhante a eval().

Retornos de chamada em bibliotecas

O retorno de chamada é um padrão simples e poderoso, que pode ser útil ao projetar uma biblioteca. O
código que compõe uma biblioteca de software deve ser o mais genérico e reutilizável possível, e os
retornos de chamada podem ajudar nessa generalização. Você não precisa prever e implementar todos
os recursos que imaginar, pois isso sobrecarregará a biblioteca e a maioria dos usuários nunca precisará
de uma grande parte desses recursos. Em vez disso, concentre-se na funcionalidade principal e forneça
"ganchos" na forma de retornos de chamada, o que permitirá que os métodos da biblioteca sejam
facilmente desenvolvidos, estendidos e personalizados.

Retornando funções
Funções são objetos, portanto, podem ser usadas como valores de retorno. Isso significa que uma função
não precisa retornar nenhum tipo de valor de dado ou matriz de dados como resultado de sua execução.
Uma função pode retornar outra função mais especializada ou pode criar outra função sob demanda,
dependendo de algumas entradas.

Aqui está um exemplo simples: uma função realiza algum trabalho, provavelmente uma inicialização
única, e então trabalha em seu valor de retorno. O valor retornado é outra função, que também pode ser
executada:

var setup = função ()


{ alerta(1);
retornar função ()
{ alerta(2);
};
};

// usando a função de
configuração var my = setup(); //
alertas 1 my(); // alertas 2

Como setup() encapsula a função retornada, ele cria um fechamento, e você pode usar esse fechamento
para armazenar alguns dados privados, que são acessíveis pela função retornada, mas não pelo código
externo. Um exemplo seria um contador que fornece um valor incrementado toda vez que você o chama:

var setup = função () { var


contagem = 0;
retornar função ()
{ retornar (contagem += 1);
};
};

// uso
var next = setup();
next(); // retorna 1

Retornando Funções | 67
Machine Translated by Google [Link]

próximo(); // 2
próximo(); // 3

Funções autodefinidas
Funções podem ser definidas dinamicamente e atribuídas a variáveis. Se você criar uma nova
função e atribuí-la à mesma variável que já contém outra função, estará sobrescrevendo a função
antiga pela nova. De certa forma, você estará reciclando o ponteiro de função antigo para apontar
para uma nova função. E tudo isso pode acontecer dentro do corpo da função antiga. Nesse caso,
a função se sobrescreve e se redefine com uma nova implementação. Isso provavelmente parece
mais complicado do que é; vejamos um exemplo simples: var scareMe = function () { alert("Boo!");
scareMe = function
() { alert("Double boo!");

};
};

// usando a função autodefinida


scareMe(); // Buu!
scareMe(); // Buu duplo!

Este padrão é útil quando sua função tem algum trabalho preparatório inicial a ser feito e precisa
fazê-lo apenas uma vez. Como não há motivo para repetir o trabalho quando ele pode ser evitado,
uma parte da função pode não ser mais necessária. Nesses casos, a função autodefinida pode
atualizar sua própria implementação.

Usar esse padrão pode obviamente ajudar no desempenho do seu aplicativo, porque sua função
redefinida simplesmente faz menos trabalho.

Outro nome para esse padrão é “definição de função preguiçosa”, porque a


função não é definida corretamente até a primeira vez que é usada e fica
preguiçosa depois disso, fazendo menos trabalho.

Uma desvantagem do padrão é que quaisquer propriedades adicionadas anteriormente à função


original serão perdidas quando ela for redefinida. Além disso, se a função for usada com um nome
diferente, por exemplo, atribuída a uma variável diferente ou usada como método de um objeto, a
parte de redefinição nunca ocorrerá e o corpo da função original será executado.

Vamos ver um exemplo em que a função scareMe() é usada da mesma forma que um objeto de
primeira classe seria usado:

1. Uma nova propriedade é adicionada.

2. O objeto de função é atribuído a uma nova variável.

68 | Capítulo 4: Funções
Machine Translated by Google [Link]

3. A função também é usada como um método.

Considere o seguinte trecho: // 1.


adicionando uma nova
propriedade [Link] = "properly";

// 2. atribuindo um nome diferente var


prank = scareMe;

// 3. usando como método var


spooky = { boo:
scareMe
};

// chamando com um novo


nome prank(); //
"Boo!" prank(); //
"Boo!" [Link]([Link]); // "corretamente"

// chamando como um
método [Link](); //
"Boo!" [Link](); //
"Boo!" [Link]([Link]); // "corretamente"

// usando a função autodefinida


scareMe(); // Buuuuuu!
scareMe(); // Buuuuuu!
[Link]([Link]); // indefinido

Como você pode ver, a autodefinição não ocorreu como você provavelmente esperava quando a
função foi atribuída a uma nova variável. Toda vez que prank() era chamado, ele emitia o alerta "Boo!".
Ao mesmo tempo, ele sobrescreveu a função global scareMe() , mas a própria função prank()
continuou visualizando a definição antiga, incluindo a propriedade property. O mesmo aconteceu
quando a função foi usada como o método boo() do objeto assustador . Todas essas invocações
continuaram reescrevendo o ponteiro global scareMe(), de modo que, quando finalmente chamado,
ele tinha o corpo atualizado alertando "Double boo" desde a primeira vez. Também não era mais
possível visualizar [Link].

Funções Imediatas
O padrão de função imediata é uma sintaxe que permite executar uma função assim que ela é
definida. Veja um exemplo:
(function ()
{ alert('cuidado!'); }());

Este padrão é, em essência, apenas uma expressão de função (nomeada ou anônima), que é
executada logo após sua criação. O termo " função imediata" não é definido no padrão ECMAScript,
mas é curto e ajuda a descrever e discutir o padrão.

Funções Imediatas | 69
Machine Translated by Google [Link]

O padrão consiste nas seguintes partes:

• Você define uma função usando uma expressão de função. (Uma declaração de função não
trabalhar.)

• Você adiciona um conjunto de parênteses no final, o que faz com que a função seja executada
imediatamente.
• Você envolve toda a função entre parênteses (obrigatório somente se você não atribuir a função
a uma variável).

A seguinte sintaxe alternativa também é comum (observe o posicionamento dos parênteses de fechamento), mas o JSLint
prefere a primeira: (function () { alert('watch out!'); })();

Esse padrão é útil porque fornece uma área restrita de escopo para seu código de inicialização.
Pense no seguinte cenário comum: seu código precisa executar algumas tarefas de configuração quando a página
carrega, como anexar manipuladores de eventos, criar objetos e assim por diante. Todo esse trabalho precisa ser
feito apenas uma vez, então não há motivo para criar uma função nomeada reutilizável. Mas o código também
requer algumas variáveis temporárias, que você não precisará após a conclusão da fase de inicialização. Seria uma
má ideia criar todas essas variáveis como globais. É por isso que você precisa de uma função imediata — para
encapsular todo o seu código em seu escopo local e não vazar nenhuma variável no escopo global: (function () {

var dias = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sábado'],


hoje = nova data(), +
'
dias[[Link]()] + ', ' + [Link](); msg = 'Hoje é

alerta(msg);

}()); // "Hoje é sexta-feira, 13"

Se esse código não estivesse encapsulado em uma função imediata, as variáveis days, today e msg
seriam todas variáveis globais, sobras do código de inicialização.

Parâmetros de uma Função Imediata


Você também pode passar argumentos para funções imediatas, como demonstra o exemplo a seguir:

// imprime:
// Conheci Joe Black na sexta-feira, 13 de agosto de 2010, às 23h26min59s GMT-0800 (PST)

(função (quem, quando) {

" " "


[Link]("Eu conheci + quem + sobre
+ quando);

}("Joe Black", nova Data()));

70 | Capítulo 4: Funções
Machine Translated by Google [Link]

Normalmente, o objeto global é passado como um argumento para a função imediata para que ele
seja acessível dentro da função sem precisar usar window: dessa forma, o código fica mais
interoperável em ambientes fora do navegador:
(função (global) {

// acesse o objeto global via `global`

}(esse));

Observe que, em geral, você não deve passar muitos parâmetros para uma função imediata, porque
pode se tornar um fardo ficar rolando constantemente para o topo e para o final da função para
entender como ela funciona.

Valores retornados de funções imediatas


Assim como qualquer outra função, uma função imediata pode retornar valores e esses valores de retorno podem ser
atribuídos a variáveis: var result = (function ()

{ return 2 + 2; }());

Outra maneira de conseguir o mesmo é omitir os parênteses que envolvem a função, pois eles não
são necessários ao atribuir o valor de retorno de uma função imediata a uma variável. Omitir o
primeiro conjunto de parênteses resulta no seguinte:
var resultado = função () { return 2
+ 2; }();

Esta sintaxe é mais simples, mas pode parecer um pouco enganosa. Se não notar o () no final da
função, alguém que esteja lendo o código pode pensar que "result" aponta para uma função. Na
verdade, "result" aponta para o valor retornado pela função imediata, neste caso, o número 4.

Outra sintaxe que alcança os mesmos resultados é:


var resultado = (função () {retornar
2 + 2; })();

Os exemplos anteriores retornaram um valor inteiro primitivo como resultado da execução da função
imediata. Mas, em vez de um valor primitivo, uma função imediata pode retornar qualquer tipo de
valor, incluindo outra função. Você pode então usar o escopo da função im-mediate para armazenar
privadamente alguns dados, específicos da função interna retornada.

No próximo exemplo, o valor retornado pela função imediata é uma função, que será atribuída à
variável getResult e simplesmente retornará o valor de res, um valor que foi pré-calculado e
armazenado no fechamento da função imediata:
var getResult = (função () { var res = 2
+ 2;

Funções Imediatas | 71
Machine Translated by Google [Link]

retornar função ()
{ retornar res;

}; }());

Funções imediatas também podem ser usadas ao definir propriedades de objetos. Imagine que você
precisa definir uma propriedade que provavelmente nunca mudará durante a vida útil do objeto, mas
antes de defini-la, é preciso realizar um trabalho para descobrir o valor correto.
Você pode usar uma função imediata para encapsular esse trabalho, e o valor retornado pela função
imediata se tornará o valor da propriedade. O código a seguir mostra um exemplo:

var o =
{ mensagem: (função () { var
quem = "eu", o
que = "chamar";
""
retornar o que + + quem;
}()),
getMsg: função ()
{ retornar [Link];
}

}; // uso
[Link](); // "me ligue"
[Link]; // "me ligue"

Neste exemplo, [Link] é uma propriedade de string, não uma função, mas precisa de uma função,
que é executada enquanto o script está sendo carregado e que ajuda a definir a propriedade.

Benefícios e Uso O

padrão de função imediata é amplamente utilizado. Ele ajuda a encapsular uma quantidade de trabalho
que você deseja fazer sem deixar nenhuma variável global para trás. Todas as variáveis que você
definir serão locais para as funções autoinvocadas e você não precisa se preocupar em poluir o
espaço global com variáveis temporárias.

Outros nomes para o padrão de função imediata incluem função “auto-invocadora”


ou “autoexecutável”, porque a função se executa assim que é definida.

Esse padrão também é frequentemente usado em bookmarklets, porque eles são executados em
qualquer página e manter o namespace global limpo (e seu código de bookmarklet discreto) é
essencial.

O padrão também permite que você agrupe recursos individuais em módulos autocontidos.
Imagine que sua página é estática e funciona bem sem JavaScript. Então, no espírito de aprimoramento
progressivo, você adiciona um trecho de código que aprimora a página de alguma forma.
Você pode encapsular esse código (também pode chamá-lo de “módulo” ou “recurso”) em um

72 | Capítulo 4: Funções
Machine Translated by Google [Link]

função e garantir que a página funcione bem com e sem ela. Em seguida, você pode adicionar mais
melhorias, removê-las, fazer testes de divisão, permitir que o usuário as desative e
breve.

Você pode usar o seguinte modelo para definir uma parte da funcionalidade; vamos chamá-lo de módulo1:

// módulo1 definido em mó[Link]


(função () {

// todo o código do módulo 1 ...

}());

Seguindo o mesmo modelo, você pode codificar seus outros módulos. Então, quando chegar a hora de
lançar o código no site, você decide quais recursos estão prontos para o lançamento e mescla os arquivos
correspondentes usando seu script de compilação.

Inicialização Imediata de Objetos


Outra maneira de se proteger da poluição de escopo global, semelhante ao padrão de funções imediatas
descrito anteriormente, é o seguinte padrão de inicialização imediata de objetos .
Este padrão usa um objeto com um método init() , que é executado imediatamente após a criação do
objeto. A função init() cuida de todas as tarefas de inicialização.

Aqui está um exemplo do padrão de objeto imediato:

({
// aqui você pode definir valores de configuração //
também conhecidos como constantes de
configuração
maxwidth: 600, maxheight: 400,

// você também pode definir métodos


utilitários gimmeMax: function () {
retornar [Link] + "x" + [Link];
},

// inicializar init:
function ()
{ [Link]([Link]()); //
mais tarefas init...

} }).init();

Em termos de sintaxe, você aborda esse padrão como se estivesse criando um objeto normal usando o
literal de objeto. Você também envolve o literal entre parênteses (operador de agrupamento), o que instrui
o mecanismo JavaScript a tratar as chaves como literais de objeto, não como um bloco de código. (Não é
um loop if ou for .) Após fechar os parênteses, você invoca o método init() imediatamente.

Inicialização Imediata de Objetos | 73


Machine Translated by Google [Link]

Você também pode encapsular o objeto e a invocação init() em parênteses de agrupamento em vez de
encapsular apenas o objeto. Em outras palavras, ambos funcionam: ({...}).init(); ({...}.init());

Os benefícios desse padrão são os mesmos do padrão de função imediata: você protege o namespace
global enquanto executa as tarefas de inicialização pontuais. Pode parecer um pouco mais complexo em
termos de sintaxe do que simplesmente encapsular um monte de código em uma função anônima, mas
se suas tarefas de inicialização forem mais complexas (como costumam ser), ele adiciona estrutura a
todo o procedimento de inicialização. Por exemplo, funções auxiliares privadas são claramente
distinguíveis porque são propriedades do objeto temporário, enquanto em um padrão de função imediata,
elas provavelmente são apenas funções espalhadas.

Uma desvantagem desse padrão é que a maioria dos minificadores JavaScript pode não minimizá-lo tão
eficientemente quanto o código simplesmente encapsulado em uma função. As propriedades e métodos
privados não serão renomeados para nomes mais curtos porque, do ponto de vista de um minificador,
não é trivial fazê-lo com segurança. No momento da escrita, o Compilador de Closure do Google em
modo "avançado" é o único minificador que renomeia as propriedades do objeto imediato para nomes
mais curtos, transformando o exemplo anterior em algo como:
({d:600,c:400,a:function(){retornar isto.d+"x"+isto.c},b:function(){[Link](isto.a())}}).b();

Este padrão é adequado principalmente para tarefas únicas, e não há


acesso ao objeto após a conclusão de init() . Se você quiser manter uma
referência ao objeto após a conclusão, pode fazer isso facilmente
adicionando return this; ao final de init().

Ramificação em tempo de inicialização

Ramificação em tempo de inicialização (também chamada de ramificação em tempo de carregamento) é


um padrão de otimização. Quando você sabe que uma determinada condição não mudará ao longo da
vida útil do programa, faz sentido testá-la apenas uma vez. A detecção de recursos no navegador (ou
sniffing) é um exemplo típico.

Por exemplo, depois de verificar se XMLHttpRequest é suportado como um objeto nativo, não há chance
de o navegador subjacente mudar no meio da execução do seu programa e, de repente, você precisar
lidar com objetos ActiveX. Como o ambiente não muda, não há motivo para seu código continuar
verificando (e chegando à mesma conclusão) toda vez que você precisar de outro objeto XHR.

Descobrir os estilos computados de um elemento DOM ou anexar manipuladores de eventos são outros
métodos que podem se beneficiar do padrão de ramificação em tempo de inicialização. A maioria dos
desenvolvedores já codificou — pelo menos uma vez na vida de programador do lado do cliente — um
utilitário com métodos para anexar e remover ouvintes de eventos, como no exemplo a seguir:

74 | Capítulo 4: Funções
Machine Translated by Google [Link]

// ANTES
var utils =
{ addListener: function (el, type, fn) { if
(typeof [Link] === 'function')
{ [Link](type, fn, false); }
else if (typeof [Link] === 'function') { // IE
[Link]('on' + type, fn); } else
{ // navegadores mais antigos
el['on' + type] = fn;
}
},
removeListener: function (el, type, fn) { //
praticamente o mesmo...
}
};

O problema com este código é que ele é um pouco ineficiente. Toda vez que você chama
[Link]() ou [Link](), as mesmas verificações são executadas repetidamente.

Usando a ramificação em tempo de inicialização, você verifica os recursos do navegador uma


vez, durante o carregamento inicial do script. Nesse momento, você redefine como a função
funcionará ao longo da vida útil da página. A seguir, um exemplo de como você pode abordar essa tarefa:
// DEPOIS

// a interface var utils


= { addListener:
null, removeListener: null

};

// a implementação if
(typeof [Link] === 'function')
{ [Link] = function (el, type, fn)
{ [Link](type, fn, false);
};
[Link] = função (el, tipo, fn)
{ [Link](tipo, fn, falso);
};
} senão se (tipo de [Link] === 'função') { // IE
[Link] = função (el, tipo, fn)
{ [Link]('on' + tipo, fn);
};
[Link] = função (el, tipo, fn)
{ [Link]('on' + tipo, fn);

}; } else { // navegadores mais


antigos [Link] = function (el, type, fn) {
el['on' + tipo] = fn;
};
[Link] = função (el, tipo, fn) {
el['on' + tipo] = nulo;
};
}

Ramificação em tempo de inicialização | 75


Machine Translated by Google [Link]

Este é o momento de mencionar um alerta contra o sniffing de navegador. Ao usar esse padrão, não exagere nas
funcionalidades do navegador. Por exemplo, se você detectou que o navegador não suporta [Link],
não presuma que o navegador com o qual está lidando é o IE e que ele também não suporta XMLHttpRequest
nativamente, embora isso tenha sido verdade em algum momento na história do navegador. Pode haver casos em
que você pode presumir com segurança que as funcionalidades andam juntas, como .addEventListener
e .removeEventListener, mas, em geral, as funcionalidades do navegador mudam independentemente. A melhor
estratégia é detectar as funcionalidades separadamente e, em seguida, usar a ramificação em tempo de carregamento
para realizar a detecção apenas uma vez.

Propriedades de Função — Um Padrão de Memorização


Funções são objetos, portanto, podem ter propriedades. Na verdade, elas têm propriedades e métodos prontos para
uso. Por exemplo, toda função, independentemente da sintaxe usada para criá-la, recebe automaticamente uma
propriedade de comprimento contendo o número de argumentos que a função espera: function func(a, b, c) {}
[Link]([Link]); // 3

Você pode adicionar propriedades personalizadas às suas funções a qualquer momento. Um caso de uso para
propriedades personalizadas é armazenar em cache os resultados (o valor de retorno) de uma função, para que, na
próxima vez que a função for chamada, não seja necessário refazer cálculos potencialmente pesados. Armazenar em
cache os resultados de uma função também é conhecido como memorização.

No exemplo a seguir, a função myFunc cria um cache de propriedades, acessível normalmente via [Link]. A
propriedade cache é um objeto (um hash) onde o parâmetro param passado para a função é usado como chave e o
resultado do cálculo é o valor. O resultado pode ser qualquer estrutura de dados complexa que você possa precisar:

var myFunc = function (param) { if (!


[Link][param]) { var
result = {}; // ...
operação cara ...
[Link][param] = result;

} retornar [Link][param];
};

// armazenamento em
cache [Link] = {};

O código anterior pressupõe que a função aceita apenas um parâmetro de argumento e que é um tipo de dado
primitivo (como uma string). Se você tiver mais parâmetros e parâmetros mais complexos, uma solução genérica seria
serializá-los. Por exemplo, você pode serializar o objeto de argumentos como uma string JSON e usar essa string
como uma chave no seu objeto de cache :

var minhaFunção = função () {

var cachekey = [Link]([Link](argumentos)),

76 | Capítulo 4: Funções
Machine Translated by Google [Link]

resultado;

if (![Link][cachekey]) { result =
{}; // ... operação
cara ... [Link][cachekey] =
result;

} retornar [Link][chave de cache];


};

// armazenamento em
cache [Link] = {};

Esteja ciente de que, na serialização, a "identidade" dos objetos é perdida. Se você tiver dois objetos
diferentes com as mesmas propriedades, ambos compartilharão a mesma entrada de cache.

Outra maneira de escrever a função anterior é usar [Link] para se referir à função, em vez de
codificar o nome da função. Embora isso seja possível atualmente, esteja ciente de que [Link]
não é permitido no modo estrito do ECMAScript 5: var myFunc = function (param) {

var f = [Link],
resultado;

if (![Link][param]) { result =
{}; // ... operação
cara ... [Link][param] = result;

} retornar [Link][parâmetro];
};

// armazenamento em
cache [Link] = {};

Objetos de configuração
O padrão de objeto de configuração é uma maneira de fornecer APIs mais limpas, especialmente se você
estiver criando uma biblioteca ou qualquer outro código que será consumido por outros programas.

É um fato que os requisitos de software mudam à medida que o software é desenvolvido e mantido. Muitas
vezes, você começa a trabalhar com alguns requisitos em mente, mas mais funcionalidades são adicionadas
posteriormente.

Imagine que você está escrevendo uma função chamada addPerson(), que aceita um nome e sobrenome
e adiciona uma pessoa a uma lista:
função addPerson(primeiro, último) {...}

Objetos de Configuração | 77
Machine Translated by Google [Link]

Mais tarde, você descobre que, na verdade, a data de nascimento também precisa ser armazenada,
assim como, opcionalmente, o gênero e o endereço. Então, você modifica a função adicionando os novos
parâmetros (colocando cuidadosamente os parâmetros opcionais no final da lista):
função addPerson(primeiro, último, data de nascimento, sexo, endereço) {...}

Neste ponto, a assinatura desta função já está ficando um pouco mais longa. E então você descobre que
precisa adicionar um nome de usuário, o que é absolutamente necessário, não opcional. Agora, quem
chama a função terá que passar até os parâmetros opcionais e tomar cuidado para não misturar a ordem
dos parâmetros: addPerson("Bruce", "Wayne",
new Date(), null, null, "batman");

Passar um grande número de parâmetros não é conveniente. Uma abordagem melhor é substituir todos
os parâmetros por apenas um e torná-lo um objeto; vamos chamá-lo de conf, de "configuração":
addPerson(conf);

Então o usuário da função pode fazer:

var conf = { nome


de usuário: "batman",
nome: "Bruce",
sobrenome: "Wayne"
};
adicionarPessoa(conf);

As vantagens dos objetos de configuração são:

• Não há necessidade de lembrar os parâmetros e sua ordem • Você


pode pular com segurança os parâmetros opcionais
• Mais fácil de ler e manter

• Mais fácil de adicionar e remover parâmetros

As desvantagens dos objetos de configuração são:

• Você precisa lembrar os nomes dos parâmetros • Os nomes das


propriedades não podem ser minimizados

Esse padrão pode ser útil quando sua função cria elementos DOM, por exemplo, ou na definição de
estilos CSS de um elemento, porque elementos e estilos podem ter um grande número de atributos e
propriedades, principalmente opcionais.

78 | Capítulo 4: Funções
Machine Translated by Google [Link]

Caril
O restante do capítulo discute o tópico de currying e aplicação de função parcial .
Mas antes de nos aprofundarmos neste tópico, vamos primeiro ver o que exatamente significa aplicação de função .

Aplicação de Função Em

algumas linguagens de programação puramente funcionais, uma função não é descrita como sendo chamada ou
invocada, mas sim como sendo aplicada. Em JavaScript, temos a mesma coisa — podemos aplicar uma função
usando o método [Link](), porque funções em JavaScript são, na verdade, objetos e possuem
métodos.

Aqui está um exemplo de aplicação de uma função:

// define uma função


var sayHi = function (who) {
retornar "Olá" + (quem ? ", " + quem : "") + "!";
};

// invocar uma função


sayHi(); // "Olá"
sayHi('mundo'); // "Olá, mundo!"

// aplicar uma função


[Link](null, ["hello"]); // "Olá, olá!"

Como você pode ver no exemplo, tanto invocar uma função quanto aplicá- la têm o mesmo resultado. apply() recebe
dois parâmetros : o primeiro é um objeto a ser vinculado a this dentro da função; o segundo é um array ou
argumentos, que então se torna o objeto de argumentos , semelhante a um array, disponível dentro da função. Se
o primeiro parâmetro for nulo, isso aponta para o objeto global, que é exatamente o que acontece quando você
chama uma função que não é um método de um objeto específico.

Quando uma função é um método de um objeto, não há referência nula passada (como no exemplo anterior). Aqui,
o objeto se torna o primeiro argumento de apply():

var alien =
{ sayHi: function (who)
{ return "Olá" + (quem ? ", " + quem : "") + "!";
}
};

[Link]('world'); // "Olá, mundo!"


[Link](alien, ["humans"]); // "Olá, humanos!"

No trecho anterior, this dentro de sayHi() aponta para alien. No exemplo anterior, this aponta para o objeto global.

Como os dois exemplos demonstram, verifica-se que o que pensamos sobre chamar uma função não é muito mais
do que um truque sintático, equivalente a uma aplicação de função.

Caril | 79
Machine Translated by Google [Link]

Observe que, além de apply(), há um método call() do objeto [Link] , mas ele ainda
é apenas um complemento de sintaxe sobre apply(). Às vezes, é melhor usar o complemento:
quando você tem uma função que recebe apenas um parâmetro, você pode economizar o
trabalho de criar arrays com apenas um elemento:
// o segundo é mais eficiente, salva um array
[Link](alien, ["humans"]); // "Olá, humanos!"
[Link](alien, "humans"); // "Olá, humanos!"

Aplicação Parcial:
Agora que sabemos que chamar uma função é, na verdade, aplicar um conjunto de argumentos
a ela, é possível passar apenas alguns argumentos, e não todos? Isso é semelhante a como
você faria normalmente se estivesse lidando com uma função matemática manualmente.

Digamos que você tenha uma função add() que soma dois números: x e y. O trecho a seguir
mostra como você pode chegar a uma solução considerando que x é 5 e y é 4:
// para fins ilustrativos // JavaScript
inválido

// temos esta função function


add(x, y) { return x + y;

// e sabemos os argumentos
add(5, 4);

// passo 1 -- substituir um argumento


function add(5, y)
{ return 5 + y;
}

// passo 2 -- substitua o outro argumento function


add(5, 4) { return 5 + 4;

Neste trecho, os passos 1 e 2 não são JavaScript válidos, mas é assim que você resolveria o
problema manualmente. Pegue o valor do primeiro argumento e substitua o valor desconhecido
x pelo valor conhecido 5 em toda a função. Em seguida, repita o mesmo procedimento até
esgotar os argumentos.

A etapa 1 neste exemplo poderia ser chamada de aplicação parcial: aplicamos apenas o
primeiro argumento. Ao executar uma aplicação parcial, você não obtém um resultado (uma
solução), mas sim outra função.

O próximo snippet demonstra o uso de um método partialApply() imaginário :


var add = função (x, y) { retornar
x + y;

80 | Capítulo 4: Funções
Machine Translated by Google [Link]

};

// aplicação completa
[Link](null, [5, 4]); // 9

// aplicação parcial var


newadd = [Link](null, [5]); // aplicando
um argumento à nova função [Link](null,
[4]); // 9

Como você pode ver, a aplicação parcial nos fornece outra função, que pode ser chamada com
os outros argumentos. Isso seria equivalente a algo como add(5)(4), pois add(5) retorna uma
função que pode ser chamada com (4). Novamente, o conhecido add(5, 4) pode ser considerado
pouco mais do que um adoçante sintático em vez de usar add(5)(4).

Agora, voltando à realidade: não existe o método partialApply() e funções em JavaScript não
se comportam assim por padrão. Mas você pode criá-las, porque JavaScript é dinâmico o
suficiente para permitir isso.

O processo de fazer uma função entender e lidar com aplicações parciais é chamado de
currying.

Currying
não tem nada a ver com o prato indiano picante; vem do nome do matemático Haskell Curry.
(A linguagem de programação Haskell também leva o nome dele.) Currying é um processo de
transformação — transformamos uma função. Um nome alternativo para currying poderia ser
schönfinkelisation, em homenagem ao nome de outro matemático, Moses Schönfinkel, o
inventor original dessa transformação.

Então, como podemos schönfinkelificar (ou schönfinkelizar ou curry) uma função? Outras
linguagens funcionais podem ter isso incorporado na própria linguagem e todas as funções são
curried por padrão. Em JavaScript, podemos modificar a função add() para uma função curried
que lidará com a aplicação parcial.

Vejamos um exemplo: //
um add() curried //
aceita lista parcial de argumentos function
add(x, y) { var oldx =
x, oldy = y; if (typeof oldy
=== "undefined") { // retorno parcial function (newy)
{ return oldx + newy;

};

} // aplicação completa
return x + y;
}

// teste

Caril | 81
Machine Translated by Google [Link]

typeof add(5); // "função" add(3)


(4); // 7

// cria e armazena uma nova função


var add2000 = add(2000);
add2000(10); // 2010

Neste trecho, na primeira vez que você chama add(), ele cria um fechamento em torno da função
interna que retorna. O fechamento armazena os valores originais x e y nas variáveis privadas oldx
e oldy. A primeira, oldx, é usada quando a função interna é executada. Se não houver aplicação
parcial e x e y forem passados, a função simplesmente os adiciona.
Esta implementação de add() é um pouco mais detalhada do que o necessário, apenas para fins
ilustrativos. Uma versão mais compacta é mostrada no próximo trecho, onde não há oldx e oldy,
simplesmente porque o x original é armazenado implicitamente na cláusula closure e reutilizamos
y como uma variável local em vez de criar uma nova variável newy como fizemos no exemplo
anterior:
// uma adição
curry // aceita lista parcial de argumentos
function add(x, y) { if
(typeof y === "undefined") { // retorno parcial
function (y) { return x +
y;
};

} // aplicação completa
return x + y;
}

Nestes exemplos, a própria função add() cuidou de aplicações parciais. Mas podemos fazer o
mesmo de forma mais genérica? Em outras palavras, podemos transformar qualquer função em
uma nova que aceite parâmetros parciais? O próximo trecho mostra um exemplo de uma função
de uso geral, vamos chamá-la de schonfinkelize(), que faz exatamente isso. Usamos o nome
schonfinkelize() em parte porque é difícil de pronunciar e em parte porque soa como um verbo (usar
"curry" pode ser ambíguo) e precisamos de um verbo para denotar que se trata de uma
transformação de uma função.

Aqui está a função de currying de uso geral: function


schonfinkelize(fn) { var slice =
[Link], stored_args =
[Link](arguments, 1); return function ()
{ var new_args =
[Link](arguments),
args = stored_args.concat(new_args);
retornar [Link](null, args);
};
}

A função schonfinkelize() é provavelmente um pouco mais complicada do que deveria ser, mas
apenas porque argumentos não são um array real em JavaScript. Pegar emprestado o método
slice() de [Link] nos ajuda a transformar argumentos em um array e trabalhar mais

82 | Capítulo 4: Funções
Machine Translated by Google [Link]

convenientemente com ele. Quando schonfinkelize() é chamado pela primeira vez, ele armazena
uma referência privada ao método slice() (chamado slice) e também armazena os argumentos
com os quais foi chamado (em stored_args), removendo apenas o primeiro, pois o primeiro
argumento é a função que está sendo processada. Então, schonfinkelize() retorna uma nova
função. Quando a nova função é chamada, ela tem acesso (por meio do fechamento) aos
argumentos já armazenados privadamente stored_args e à referência slice . A nova função
precisa mesclar apenas os argumentos antigos parcialmente aplicados (stored_args) com os
novos (new_args) e, em seguida, aplicá-los à função original fn (também disponível privadamente no fechamento).

Agora, armados com uma maneira geral de fazer qualquer função ser executada, vamos tentar com
alguns testes: // uma
função normal function
add(x, y) { return x + y;

// execute uma função para obter uma nova


função var newadd = schonfinkelize(add,
5); newadd(4); // 9

// outra opção -- chamar a nova função diretamente


schonfinkelize(add, 6)(7); // 13

A função de transformação schonfinkelize() não se limita a parâmetros únicos ou ao currying de


etapa única. Aqui estão mais alguns exemplos de uso:
// uma função normal
function add(a, b, c, d, e) { return
a + b + c + d + e;
}

// funciona com qualquer número de


argumentos schonfinkelize(add, 1, 2, 3)(5, 5); // 16

// currying em duas
etapas var addOne = schonfinkelize(add,
1); addOne(10, 10, 10, 10); // 41
var addSix = schonfinkelize(addOne, 2, 3);
addSix(5, 5); // 16

Quando usar o Currying:


Quando você se pega chamando a mesma função e passando basicamente os mesmos
parâmetros, a função provavelmente é uma boa candidata para o currying. Você pode criar uma
nova função dinamicamente aplicando parcialmente um conjunto de argumentos à sua função. A
nova função manterá os parâmetros repetidos armazenados (para que você não precise passá-
los todas as vezes) e os usará para preencher previamente a lista completa de argumentos que a
função original espera.

Caril | 83
Machine Translated by Google [Link]

Resumo
Em JavaScript, o conhecimento e o uso adequado de funções são essenciais. Este capítulo discutiu o
contexto e a terminologia relacionados a funções. Você aprendeu sobre dois recursos importantes das
funções em JavaScript, a saber:

1. Funções são objetos de primeira classe; elas podem ser passadas como valores e aumentadas com
propriedades e métodos.

2. Funções fornecem escopo local, o que outras chaves não oferecem. Também é importante lembrar
que as declarações de variáveis locais são elevadas ao topo da lista local.
escopo.

A sintaxe para criação de funções inclui:

1. Expressões de função nomeadas

2. Expressões de função (as mesmas que as anteriores, mas sem nome), também conhecidas como
funções anônimas 3.

Declarações de função, semelhantes à sintaxe de função em outras linguagens

Depois de abordar o contexto e a sintaxe das funções, você aprendeu sobre uma série de padrões úteis,
que podem ser agrupados nas seguintes categorias:

1. Padrões de API, que ajudam você a fornecer interfaces melhores e mais limpas para suas funções
ções. Esses padrões incluem:

Padrões de retorno
de chamada Passar uma função como

argumento Objetos de
configuração Ajudar a manter o número de argumentos para uma função sob

controle Retornar
funções Quando o valor de retorno de uma função é outra função

Currying
Quando novas funções são criadas com base nas existentes, além de uma lista parcial de
argumentos

2. Padrões de inicialização, que ajudam a executar tarefas de inicialização e configuração (muito comuns
em páginas e aplicativos da web) de forma mais clara e estruturada, sem poluir o namespace global
com variáveis temporárias. Estes incluem: Funções imediatas: executadas assim que definidas.

Inicialização imediata de
objetos: tarefas de inicialização estruturadas

em um objeto anônimo que fornece


um método a ser chamado imediatamente.

84 | Capítulo 4: Funções
Machine Translated by Google [Link]

Ramificação em tempo
de inicialização: ajuda a ramificar o código apenas uma vez durante a execução inicial, em vez de
muitas vezes posteriormente, durante a vida útil do

aplicativo. 3. Padrões de desempenho, que ajudam a acelerar o código. Estes incluem:


Memorização
Usando propriedades de função para que os valores calculados não sejam calculados novamente

Funções autodefinidas
Sobrescrever-se com novos corpos para fazer menos trabalho a partir da segunda invocação e
depois

Resumo | 85
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 5

Padrões de Criação de Objetos

Criar objetos em JavaScript é fácil — você usa o literal do objeto ou funções construtoras. Neste
capítulo, vamos além e vemos alguns padrões adicionais para a criação de objetos.

A linguagem JavaScript é simples e direta, e muitas vezes não há uma sintaxe específica para
recursos com os quais você pode estar acostumado em outras linguagens, como namespaces,
módulos, pacotes, propriedades privadas e membros estáticos. Este capítulo apresenta padrões
comuns para implementar, substituir ou simplesmente pensar de forma diferente sobre esses recursos.

Analisaremos namespaces, declarações de dependência, padrões de módulo e padrões sandbox —


eles ajudam a organizar e estruturar o código do seu aplicativo e a mitigar o efeito dos globais
implícitos. Outros tópicos de discussão incluem membros privados e privilegiados, membros estáticos
e estáticos privados, constantes de objeto, encadeamento e uma maneira inspirada em classes para
definir construtores.

Padrão de namespace
Os namespaces ajudam a reduzir o número de globais exigidos pelos nossos programas e, ao mesmo
tempo, também ajudam a evitar colisões de nomes ou prefixação excessiva de nomes.

JavaScript não possui namespaces incorporados à sintaxe da linguagem, mas esse é um recurso
bastante fácil de implementar. Em vez de poluir o escopo global com muitas funções, objetos e outras
variáveis, você pode criar um (e idealmente apenas um) objeto global para seu aplicativo ou biblioteca.
Assim, você pode adicionar todas as funcionalidades a esse objeto.

Considere o seguinte exemplo:

// ANTES: 5 globais
// Aviso: antipadrão

// construtores
função Parent() {}
função Child() {}

// uma variável

87
Machine Translated by Google [Link]

var alguma_var = 1;

// alguns objetos
var módulo1 = {};
módulo1. dados = {a: 1, b: 2}; var
módulo2 = {};

Você pode refatorar esse tipo de código criando um único objeto global para seu aplicativo, chamado, por
exemplo, MYAPP, e alterar todas as suas funções e variáveis para se tornarem propriedades do seu objeto
global:

// DEPOIS: 1 global

// objeto global var


MYAPP = {};

// construtores
[Link] = function () {};
[Link] = function () {};

// uma variável
MYAPP.some_var = 1;

// um contêiner de objetos
[Link] = {};

// objetos aninhados
[Link].module1 = {};
[Link] = {a: 1, b: 2};
[Link].module2 = {};

Para o nome do objeto de namespace global, você pode escolher, por exemplo, o nome do seu aplicativo
ou biblioteca, o nome do seu domínio ou o nome da sua empresa. Os desenvolvedores costumam usar a
convenção de escrever a variável global em LETRAS MAIÚSCULAS, para que ela se destaque para os
leitores do código. (Mas lembre-se de que letras maiúsculas também costumam ser usadas para constantes.)

Este padrão é uma boa maneira de criar namespaces para seu código e evitar conflitos de nomenclatura
em seu próprio código, bem como conflitos entre seu código e código de terceiros na mesma página, como
bibliotecas JavaScript ou widgets. Este padrão é altamente recomendado e perfeitamente aplicável a muitas
tarefas, mas apresenta algumas desvantagens:

• Um pouco mais para digitar; prefixar cada variável e função soma no total
quantidade de código que precisa ser baixado

• Apenas uma instância global significa que qualquer parte do código pode modificar a instância global
instância e o restante da funcionalidade obtém o estado atualizado • Nomes

aninhados longos significam pesquisas de resolução de propriedade mais longas (mais lentas)

O padrão sandbox discutido mais adiante no capítulo aborda essas desvantagens.

88 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Função de Namespace de Uso Geral À medida

que a complexidade de um programa aumenta e algumas partes do código são divididas em


arquivos diferentes e incluídas condicionalmente, torna-se perigoso presumir que seu código é o
primeiro a definir um determinado namespace ou uma propriedade dentro dele. Algumas das
propriedades que você está adicionando ao namespace podem já existir e você pode estar substituindo-as.
Portanto, antes de adicionar uma propriedade ou criar um namespace, é melhor verificar primeiro se ele
ainda não existe, conforme mostrado neste exemplo: //
unsafe
var MYAPP =
{}; // better
if (typeof MYAPP === "undefined") { var
MYAPP = {};

} // ou mais
curto var MYAPP = MYAPP || {};

Você pode ver como essas verificações adicionais podem rapidamente resultar em muita repetição de
código. Por exemplo, se você quiser definir [Link].module2, precisará fazer três verificações,
uma para cada objeto ou propriedade que estiver definindo. É por isso que é útil ter uma função
reutilizável que cuide dos detalhes do namespace. Vamos chamar essa função de namespace() e usá-
la assim:
// usando uma função de namespace
[Link]('[Link].module2');

// equivalente a: //
var MYAPP = { //
módulos: { //
módulo2: {} // // };
}

A seguir, um exemplo de implementação da função de namespace. Essa implementação é não


destrutiva, o que significa que, se um namespace existir, ele não será recriado:
var MEUAPP = MEUAPP || {};

[Link] = função (ns_string) { var


parts = ns_string.split('.'), parent =
MYAPP, i;

// remove a redundância global inicial


if (parts[0] === "MYAPP")
{ parts = [Link](1);
}

para (i = 0; i < [Link]; i += 1) {


// cria uma propriedade se ela não existir if
(typeof parent[parts[i]] === "undefined") {
pai[partes[i]] = {};
}

Padrão de namespace | 89
Machine Translated by Google [Link]

pai = pai[partes[i]];

} retornar pai;
};

Esta implementação permite todos estes usos: // atribuir


valor retornado a uma var local var module2
= [Link]('[Link].module2'); module2
=== [Link].module2; // true

// pule `MYAPP` inicial


[Link]('modules.module51');

// namespace longo
[Link]('[Link]');

A Figura 5-1 mostra a aparência dos namespaces no exemplo anterior quando


inspecionados no Firebug.

Figura 5-1. Namespace MYAPP inspecionado no Firebug

Declarando Dependências
Bibliotecas JavaScript são frequentemente modulares e com namespaces, o que permite
incluir apenas os módulos necessários. Por exemplo, no YUI2, há uma variável global
YAHOO, que serve como namespace, e módulos que são propriedades da variável
global, como [Link] (o módulo DOM) e [Link] (o módulo Events).

É uma boa ideia declarar os módulos dos quais seu código depende no topo da sua função ou módulo. A declaração
envolve a criação apenas de uma variável local e o apontamento para o módulo desejado:

90 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

var myFunction = function () { //


dependências var
event = [Link], dom =
[Link];

// use variáveis de evento e DOM //


para o resto da função...
};

Este é um padrão extremamente simples, mas ao mesmo tempo tem inúmeros benefícios:

• A declaração explícita de dependências sinaliza aos usuários do seu código as dependências específicas
arquivos de script que eles precisam ter certeza de que estão incluídos na página.

• A declaração inicial no topo da função facilita a localização e a resolução


dependências.

• Trabalhar com uma variável local (como dom) é sempre mais rápido do que trabalhar com uma global
(como YAHOO) e ainda mais rápido do que trabalhar com propriedades aninhadas de uma variável
global (como [Link]), resultando em melhor desempenho. Ao seguir esse padrão de
declaração de dependência, a resolução do símbolo global é realizada apenas uma vez na função.
Depois disso, a variável local é usada, o que é muito mais rápido.

• Ferramentas avançadas de minificação, como o YUICompressor e o compilador Google Closure,


renomearão variáveis locais (então o evento provavelmente se tornará apenas um caractere, como A),
resultando em um código menor, mas nunca em variáveis globais, porque não é seguro fazer isso.

O trecho a seguir ilustra o efeito do padrão de declaração de dependência no código minificado. Embora
test2(), que segue o padrão, pareça um pouco mais complexo por exigir mais linhas de código e uma variável
extra, na verdade resulta em menos código após a minificação, o que significa menos código para o usuário
baixar:

função test1()
{ alerta([Link].m1);
alerta([Link].m2);
alerta([Link].m51);
}

/*
corpo do teste1
minificado: alert([Link].m1);alert([Link].m2);alert([Link].m51)
*/

função test2() { var


módulos = [Link];
alerta(módulos.m1);
alerta(módulos.m2);
alerta(módulos.m51);
}

/*
corpo test2 minificado:

Declarando Dependências | 91
Machine Translated by Google [Link]

var a=[Link];alert(a.m1);alert(a.m2);alert(a.m51) */

Propriedades e Métodos Privados


Ao contrário do Java ou de outras linguagens, JavaScript não possui uma sintaxe específica para denotar
propriedades e métodos privados, protegidos ou públicos. Todos os membros do objeto são públicos:

var myobj =
{ minhaprop:
1, getProp: função ()
{ retornar [Link];
}
};
[Link]([Link]); // `myprop` é publicamente acessível
[Link]([Link]()); // getProp() também é público

O mesmo acontece quando você usa funções construtoras para criar objetos; todos os membros ainda são
públicos:

function Gadget()
{ [Link] = 'iPod';
[Link] = function () { return
'iPad';
};

} var toy = new Gadget();


[Link]([Link]); // `name` é público
[Link]([Link]()); // stretch() é público

Membros Privados
Embora a linguagem não tenha uma sintaxe especial para membros privados, você pode implementá-los
usando um fechamento. Suas funções construtoras criam um fechamento e quaisquer variáveis que façam parte
do escopo do fechamento não são expostas fora do construtor.
No entanto, essas variáveis privadas estão disponíveis para os métodos públicos: métodos definidos dentro do
construtor e expostos como parte dos objetos retornados. Vejamos um exemplo em que nome é um membro
privado, não acessível fora do construtor:

function Gadget() { //
membro privado var
name = 'iPod'; //
função pública
[Link] = function () { return
name;
};

} var brinquedo = novo Gadget();

// `name` é indefinido, é privado


[Link]([Link]); // indefinido

92 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

// o método público tem acesso a `nome`


[Link]([Link]()); // "iPod"

Como você pode ver, é fácil alcançar privacidade em JavaScript. Tudo o que você precisa fazer é
encapsular os dados que deseja manter privados em uma função e garantir que eles sejam locais para
a função, o que significa não disponibilizá-los fora dela.

Métodos Privilegiados A

noção de métodos privilegiados não envolve nenhuma sintaxe específica; é apenas um nome dado aos
métodos públicos que têm acesso aos membros privados (e, portanto, têm mais privilégios).

No exemplo anterior, getName() é um método privilegiado porque tem acesso “especial” ao nome da
propriedade privada.

Falhas de privacidade

Existem alguns casos extremos quando você está preocupado com privacidade:

• Algumas versões anteriores do Firefox permitem que um segundo parâmetro seja passado para
eval() , que é um objeto de contexto que permite que você acesse o escopo privado da função. Da
mesma forma, a propriedade __parent__ no Mozilla Rhino permite acesso ao escopo privado.
Esses casos extremos não se aplicam aos navegadores amplamente utilizados hoje em dia.

• Quando você está retornando diretamente uma variável privada de um método privilegiado e essa
variável é um objeto ou array, o código externo pode modificar a variável privada porque ela é
passada por referência.

Vamos examinar o segundo caso um pouco mais de perto. A seguinte implementação de Gadget parece
inocente:

função Gadget() { //
membro privado var
specs =
{ largura_da_tela: 320,
altura_da_tela: 480, cor:
"branco"
};

// função pública
[Link] = function () { return
specs;
};
}

O problema aqui é que getSpecs() retorna uma referência ao objeto specs . Isso permite que o usuário
do Gadget modifique as especificações aparentemente ocultas e privadas:

var brinquedo = novo Gadget(),


especificações = [Link]ções();

Propriedades e Métodos Privados | 93


Machine Translated by Google [Link]

especificaçõ[Link] = "preto";
especificaçõ[Link]ço = "grátis";

[Link]([Link]());

O resultado da impressão no console do Firebug é mostrado na Figura 5-2.

Figura 5-2. O objeto privado foi modificado

A solução para esse comportamento inesperado é ter cuidado para não passar referências a
objetos e arrays que você deseja manter privados. Uma maneira de fazer isso é fazer com que
getSpecs() retorne um novo objeto contendo apenas alguns dos dados que podem ser
interessantes para o consumidor do objeto. Isso também é conhecido como Princípio da Autoridade
Mínima (POLA), que afirma que você nunca deve fornecer mais do que o necessário. Nesse caso,
se o consumidor de Gadget estiver interessado em saber se o gadget se encaixa em um
determinado padrão, ele precisa apenas das dimensões. Portanto, em vez de fornecer tudo, você
pode criar getDimensions(), que retorna um novo objeto contendo apenas largura e altura. Talvez
você não precise implementar getSpecs() .

Outra abordagem, quando você precisa passar todos os dados, é criar uma cópia do objeto specs
usando uma função de clonagem de objetos de uso geral. O próximo capítulo oferece duas dessas
funções — uma, chamada extend() , faz uma cópia superficial do objeto fornecido (copia apenas
os parâmetros de nível superior). A outra, chamada extendDeep(), faz uma cópia profunda,
copiando recursivamente todas as propriedades e suas propriedades aninhadas.

Literais de Objeto e Privacidade


Até agora, vimos apenas exemplos de uso de construtores para obter privacidade. Mas e os casos
em que seus objetos são criados com literais de objeto? Ainda é possível ter membros privados?

Como você viu antes, tudo o que você precisa é de uma função para encapsular os dados privados.
Portanto, no caso de literais de objeto, você pode usar o fechamento criado por uma função imediata
anônima adicional. Aqui está um
exemplo: var myobj; // este será o objeto
(function() { //
membros privados
var name = "my, oh my";

// implementa a parte pública //


nota -- sem `var`

94 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

myobj =
{ // método privilegiado
getName: function ()
{ return name;
}

}; }());

[Link](); // "meu, meu Deus"

A mesma ideia, mas com uma implementação ligeiramente diferente, é apresentada no exemplo a seguir: var
myobj =
(function () { // membros
privados var name =
"my, oh my";

// implementa a parte pública


return
{ getName: function ()
{ return name;
}

}; }());

[Link](); // "meu, meu Deus"

Este exemplo também é o esqueleto do que é conhecido como “padrão de módulo”, que examinaremos
em breve.

Protótipos e privacidade Uma

desvantagem dos membros privados quando usados com construtores é que eles são recriados toda
vez que o construtor é invocado para criar um novo objeto.

Na verdade, isso é um problema com qualquer membro que você adiciona dentro de construtores.
Para evitar a duplicação de esforços e economizar memória, você pode adicionar propriedades e
métodos comuns à propriedade de protótipo do construtor. Dessa forma, as partes comuns são
compartilhadas entre todas as instâncias criadas com o mesmo construtor. Você também pode
compartilhar os membros privados ocultos entre as instâncias. Para isso, você pode usar uma
combinação de dois padrões: propriedades privadas dentro de construtores e propriedades privadas em
literais de objeto. Como a propriedade de protótipo é apenas um objeto, ela pode ser criada com os
literais de objeto.

Aqui está um exemplo de como você pode conseguir isso:

function Gadget() { //
membro privado var
name = 'iPod'; //
função pública
[Link] = function () { return
name;
};

Propriedades e Métodos Privados | 95


Machine Translated by Google [Link]

[Link] = (function () { //
membro privado var
browser = "Mobile Webkit"; //
membros de protótipo público
return
{ getBrowser: function ()
{ return browser;
}

}; }());

var toy = new Gadget();


[Link]([Link]()); // método "próprio" privilegiado
[Link]([Link]()); // método de protótipo privilegiado

Revelando Funções Privadas como Métodos Públicos


O padrão de revelação consiste em ter métodos privados, que você também expõe como
métodos públicos. Isso pode ser útil quando toda a funcionalidade de um objeto é crítica
para o funcionamento do objeto e você deseja protegê-la o máximo possível. Mas, ao
mesmo tempo, você deseja fornecer acesso público a algumas dessas funcionalidades,
pois isso também pode ser útil. Ao expor métodos publicamente, você os torna vulneráveis;
alguns usuários da sua API pública podem modificá-la, mesmo involuntariamente. No
ECMAScript 5, você tem a opção de congelar um objeto, mas não nas versões anteriores da linguagem.
Entra em cena o padrão de revelação (o termo cunhado por Christian Heilmann originalmente
era “padrão de módulo revelador”).

Vamos dar um exemplo, baseado em um dos padrões de privacidade — os membros privados


em literais de objeto:
var meuarray;

(função () {

var astr = "[matriz de objetos]",


toString = [Link];

função isArray(a)
{ retornar [Link](a) === astr;
}

função indexOf(palheiro, agulha) { var i =


0, max =
[Link]; para (; i <
max; i += 1) { se (palheiro[i]
=== agulha) { retornar i;

} retornar ÿ1;
}

96 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

myarray =
{ isArray: isArray,
indexOf: indexOf,
inArray: indexOf
};

}());

Aqui você tem duas variáveis privadas e duas funções privadas — isArray() e indexOf(). Próximo ao
final da função imediata, o objeto myarray é preenchido com a funcionalidade que você decidir tornar
pública. Neste caso, o mesmo indexOf() privado é exposto como indexOf no estilo ECMAScript 5 e
inArray inspirado em PHP . Testando o novo objeto myarray :

[Link]([1,2]); // verdadeiro
[Link]({0: 1}); // falso
[Link](["a", "b", "z"], "z"); // 2
[Link](["a", "b", "z"], "z"); // 2

Agora, se algo inesperado acontecer, por exemplo, com o indexOf() público, o indexOf() privado
ainda estará seguro e, portanto, inArray() continuará funcionando:
[Link] = nulo;
[Link](["a", "b", "z"], "z"); // 2

Padrão de módulo
O padrão de módulos é amplamente utilizado porque fornece estrutura e ajuda a organizar seu código
à medida que ele cresce. Ao contrário de outras linguagens, JavaScript não possui sintaxe específica
para pacotes, mas o padrão de módulos fornece as ferramentas para criar trechos de código
autocontidos e desacoplados, que podem ser tratados como caixas-pretas de funcionalidade e
adicionados, substituídos ou removidos de acordo com os requisitos (em constante mudança) do
software que você está escrevendo.

O padrão de módulo é uma combinação de vários padrões descritos até agora no livro, a saber:

• Espaços de
nomes • Funções imediatas

• Membros privados e privilegiados •


Declarando dependências O

primeiro passo é configurar um namespace. Vamos usar a função namespace() descrita anteriormente
neste capítulo e iniciar um módulo utilitário de exemplo que fornece métodos de array úteis:
[Link]('[Link]');

O próximo passo é definir o módulo. O padrão usa uma função imediata que fornecerá escopo privado
caso privacidade seja necessária. A função imediata retorna um objeto—

Padrão de Módulo | 97
Machine Translated by Google [Link]

o módulo real com sua interface pública, que estará disponível aos consumidores do módulo:

[Link] = (function () { return


{ // todo...

}; }());

Em seguida, vamos adicionar alguns métodos à interface pública:


[Link] = (function () { return
{ inArray:
function (agulha, palheiro) { // ...

},
isArray: function (a) { // ...

}; }());

Usando o escopo privado fornecido pela função imediata, você pode declarar algumas propriedades e
métodos privados conforme necessário. Logo no topo da função imediata também estará o local para
declarar quaisquer dependências que seu módulo possa ter. Após as declarações de variáveis, você
pode, opcionalmente, inserir qualquer código de inicialização único que ajude a configurar o módulo. O
resultado final é um objeto retornado pela função imediata que contém a API pública do seu módulo:
[Link]('[Link]');

[Link] = (função () {

// dependências
var uobj = [Link], ulang
= [Link],

// propriedades privadas
array_string = "[object Array]", ops =
[Link];

// métodos
privados // ...

// fim da variável

// procedimentos de inicialização opcionais de uso


único // ...

// API pública
return {

inArray: função (agulha, palheiro) { para


(var i = 0, max = [Link]; i < max; i += 1) {
se (palheiro[i] === agulha) {

98 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

retornar verdadeiro;
}
}
},

isArray: função (a) { return


[Link](a) === array_string;

} // ... mais métodos e propriedades

}; }());

O padrão de módulo é uma maneira amplamente utilizada e altamente recomendada de organizar seu código,
especialmente à medida que ele cresce.

Padrão de Módulo Revelador: Já

discutimos o padrão de revelação neste capítulo, ao analisar os padrões de privacidade. O padrão de módulo
pode ser organizado de forma semelhante, onde todos os métodos são mantidos privados e você expõe apenas
aqueles que decidir no final, ao configurar a API pública.

O acima pode se tornar:

[Link] = (função () {

// propriedades privadas
var array_string = "[object Array]", ops =
[Link],

// métodos privados
inArray = função (palheiro, agulha) { para (var i
= 0, max = [Link]; i < max; i += 1) {
se (palheiro[i] === agulha) { retornar
i;
}

} retornar ÿ1;
},
isArray = function (a) { return
[Link](a) === array_string;

}; // fim da variável

// revelando o retorno da
API
pública { isArray:
isArray, indexOf: inArray

}; }());

Padrão de Módulo | 99
Machine Translated by Google [Link]

Módulos que criam construtores


O exemplo anterior produziu um objeto [Link], mas às vezes é mais
conveniente criar seus objetos usando funções construtoras. Você ainda pode fazer
isso usando o padrão de módulo. A única diferença é que a função imediata que
encapsula o módulo retornará uma função no final, e não um objeto.
Considere o seguinte exemplo do padrão de módulo que cria uma função construtora
[Link]:
[Link]('[Link]');

[Link] = (função () {

// dependências
var uobj = [Link], ulang =
[Link],

// propriedades e métodos privados...


Construção;

// fim da variável

// procedimentos de inicialização opcionais de uso


único // ...

// API pública -- construtor Constr


= function (o) { [Link]
= [Link](o);

}; // API pública -- protótipo


[Link] =
{ construtor: [Link], versão:
"2.0", toArray:
função (obj) { para (var i = 0,
a = [], len = [Link]; i < len; i += 1) { a[i] = obj[i];

} retornar um;
}
};

// retorna o construtor // a ser


atribuído ao novo namespace return Constr;

}());

A maneira de usar esse novo construtor será assim:


var arr = novo [Link](obj);

100 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Importando Globais para um Módulo Em

uma variação comum do padrão, você pode passar argumentos para a função imediata que
encapsula o módulo. Você pode passar qualquer valor, mas geralmente são referências a variáveis
globais e até mesmo ao próprio objeto global. Importar globais ajuda a acelerar a resolução de
símbolos globais dentro da função imediata, pois as variáveis importadas se tornam locais para a
função:

[Link] = (função (aplicativo, global) {

// referências ao objeto global // e ao


objeto de namespace do aplicativo global // agora
estão localizadas

}(MEU APLICATIVO, isso));

Padrão Sandbox
O padrão sandbox aborda as desvantagens do padrão de namespace, a saber:

• Dependência de uma única variável global para ser a global do aplicativo. No padrão de
espaçamento de nomes, não há como executar duas versões do mesmo aplicativo ou
biblioteca na mesma página, pois ambas precisam do mesmo nome de símbolo global, por
exemplo, MYAPP. •
Nomes longos e com pontos para digitar e resolver em tempo de execução, por exemplo,
[Link].

Como o nome sugere, o padrão sandbox fornece um ambiente para os módulos “brincarem”
sem afetar outros módulos e seus sandboxes pessoais.

O padrão é muito usado na versão 3 do YUI, por exemplo, mas tenha em mente que a discussão
a seguir é uma implementação de referência de exemplo e não tenta descrever como a
implementação sandbox do YUI3 funciona.

Um construtor global

No padrão de namespace, você tem um objeto global; no padrão sandbox, o único objeto global
é um construtor: vamos chamá-lo de Sandbox(). Você cria objetos usando esse construtor e
também passa uma função de retorno de chamada, que se torna o ambiente isolado em
sandbox para o seu código.

O uso do sandbox ficará assim:


novo Sandbox(função (caixa) { //
seu código aqui... });

A caixa de objeto será como MYAPP no exemplo de namespace: ela terá todas as
funcionalidades de biblioteca necessárias para fazer seu código funcionar.

Padrão Sandbox | 101


Machine Translated by Google [Link]

Vamos adicionar mais duas coisas ao padrão:

• Com um pouco de mágica (impondo o padrão new do Capítulo 3), você pode assumir new e não
exigi-lo ao criar o objeto. • O construtor
Sandbox() pode aceitar um argumento (ou argumentos) de configuração adicional especificando
os nomes dos módulos necessários para esta instância do objeto. Queremos que o código seja
modular, portanto, a maior parte da funcionalidade que Sandbox() fornece estará contida em
módulos.

Com esses dois recursos adicionais, vamos ver alguns exemplos de como ficará o código para
instanciar objetos.

Você pode omitir new e criar um objeto que use alguns módulos fictícios “ajax” e “event” como:

Sandbox(['ajax', 'evento'], função (caixa) {


// [Link](caixa); });

Este exemplo é semelhante ao anterior, mas desta vez os nomes dos módulos são passados como
argumentos individuais:
Sandbox('ajax', 'dom', função (caixa) { //
[Link](caixa); });

E que tal usar um argumento curinga “*” para significar “usar todos os módulos disponíveis”?
Por conveniência, digamos também que, quando nenhum módulo for passado, o sandbox assumirá
'*'. Portanto, duas maneiras de usar todos os módulos disponíveis seriam:
Sandbox('*', function (box) { //
[Link](box); });

Sandbox(função (caixa) { //
[Link](caixa); });

E mais um exemplo de uso do padrão ilustra como você pode instanciar objetos sandbox várias
vezes — e você pode até mesmo aninhá-los um dentro do outro sem que os dois interfiram:

Sandbox('dom', 'evento', função (caixa) {

// trabalhar com dom e evento

Sandbox('ajax', função (caixa) {


// outro objeto "box" em sandbox //
esta "box" não é a mesma que // a
"box" fora desta função

//...

// feito com Ajax

102 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

});

// nenhum vestígio do módulo Ajax aqui

});

Como você pode ver nesses exemplos, ao usar o padrão sandbox, você pode proteger o namespace
global tendo seu código encapsulado em funções de retorno de chamada.

Se necessário, você também pode usar o fato de que funções são objetos e armazenam alguns dados
como propriedades “estáticas” do construtor Sandbox() .

E, finalmente, você pode ter instâncias diferentes dependendo do tipo de módulo que você precisa e
essas instâncias funcionam independentemente umas das outras.

Agora vamos ver como você pode abordar a implementação do construtor Sandbox() e seus módulos
para dar suporte a toda essa funcionalidade.

Adicionando Módulos

Antes de implementar o construtor real, vamos ver como podemos abordar a adição de módulos.

A função construtora Sandbox() também é um objeto, então você pode adicionar uma propriedade
estática chamada módulos a ela. Essa propriedade será outro objeto contendo pares chave-valor,
onde as chaves são os nomes dos módulos e os valores são as funções que implementam cada
módulo: [Link] =
{};

[Link] = função (caixa)


{ [Link] = função () {};
[Link] = função () {};
[Link] = "bar";
};

[Link] = função (caixa) {


// acesso ao protótipo Sandbox, se necessário: //
[Link].m = "mmm";
[Link] = function () {};
[Link] = function () {};
};

[Link] = função (caixa)


{ [Link] = função () {};
[Link] = função () {};
};

Neste exemplo, adicionamos os módulos dom, event e ajax, que são funcionalidades comuns em
todas as bibliotecas ou aplicativos web complexos.

As funções que implementam cada módulo aceitam a caixa de instância atual como um parâmetro e
podem adicionar propriedades e métodos adicionais a essa instância.

Padrão Sandbox | 103


Machine Translated by Google [Link]

Implementando o Construtor
Por fim, vamos implementar o construtor Sandbox() (naturalmente, você deve
renomear esse tipo de construtor para algo que faça sentido para sua biblioteca
ou aplicativo):
function Sandbox() { //
transformando argumentos em um array
var args = [Link](arguments), // o último
argumento é o retorno de chamada callback
= [Link](), // módulos
podem ser passados como um array ou como parâmetros individuais modules
= (args[0] && typeof args[0] === "string") ? args : args[0], i;

// certifique-se de que a função seja


chamada // como um
construtor if (!(this instanceof Sandbox))
{ return new Sandbox(modules, callback);
}

// adicione propriedades a `this` conforme


necessário:
this.a = 1; this.b = 2;

// agora adicione módulos ao objeto principal `this` // nenhum


módulo ou "*" significa "usar todos os módulos" if (!modules ||
modules === '*') { modules = []; for (i in
[Link])
{ if ([Link](i))
{ [Link](i);

}
}
}

// inicializa os módulos necessários para


(i = 0; i < [Link]; i += 1)
{ [Link][modules[i]](this);
}

// chamar o retorno de
chamada callback(this);
}

// quaisquer propriedades de protótipo


conforme necessário
[Link] =
{ name: "Meu
aplicativo", version: "1.0",
getName: function () { return [Link];
}
};

104 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Os principais itens na implementação são:

• Há uma verificação se esta é uma instância de Sandbox e, se não for (o que significa que
Sandbox() foi chamado sem new), chamamos a função novamente como um construtor. •
Você pode adicionar propriedades a isso dentro do construtor. Você também pode adicionar
propriedades ao protótipo do construtor.
• Os módulos necessários podem ser passados como um array de nomes de módulos, ou como
argumentos individuais, ou com o curinga * (ou omitido), o que significa que devemos carregar
todos os módulos disponíveis. Observe que neste exemplo de implementação não nos
preocupamos em carregar a funcionalidade necessária de arquivos adicionais, mas isso é
definitivamente uma opção. Isso é algo suportado pelo YUI3, por exemplo. Você pode carregar
apenas o módulo mais básico (também conhecido como "seed") e quaisquer módulos que você
precisar serão carregados de arquivos externos usando uma convenção de nomenclatura onde
os nomes dos arquivos correspondem aos nomes dos módulos.
• Quando conhecemos os módulos necessários, nós os inicializamos, o que significa que
chamamos a função que implementa cada
módulo. • O último argumento para o construtor é o retorno de chamada. O retorno de chamada
será invocado no final usando a instância recém-criada. Esse retorno de chamada é, na
verdade, a sandbox do usuário e recebe um objeto de caixa preenchido com todas as funcionalidades solicitadas.

Membros estáticos
Propriedades e métodos estáticos são aqueles que não mudam de uma instância para outra.
Em linguagens baseadas em classes, membros estáticos são criados usando uma sintaxe especial
e, em seguida, usados como se fossem membros da própria classe. Por exemplo, um método
estático max() de alguma classe MathUtils seria invocado como [Link](3, 5). Este é um
exemplo de um membro estático público, que pode ser usado sem a necessidade de criar uma
instância da classe. Também pode haver membros estáticos privados — não visíveis ao consumidor
da classe, mas ainda compartilhados entre todas as instâncias da classe. Vamos ver como
implementar membros estáticos públicos e privados em JavaScript.

Membros Estáticos Públicos

Em JavaScript, não existe uma sintaxe especial para denotar membros estáticos. Mas você pode ter
a mesma sintaxe de uma linguagem "elegante" usando uma função construtora e adicionando
propriedades a ela. Isso funciona porque construtores, como todas as outras funções, são objetos e
podem ter propriedades. O padrão de memorização discutido no capítulo anterior empregou a
mesma ideia — adicionar propriedades a uma função.

O exemplo a seguir define um construtor Gadget com um método estático isShiny() e um método de
instância regular setPrice(). O método isShiny() é um método estático porque não precisa de um
objeto gadget específico para funcionar (assim como você não precisa de um

Membros Estáticos | 105


Machine Translated by Google [Link]

gadget específico para descobrir que todos os gadgets são brilhantes). setPrice(), por outro lado, precisa
de um objeto, porque os gadgets podem ter preços diferentes: //
construtor var
Gadget = function () {};

// um método estático
[Link] = function () { return
"você aposta";
};

// um método normal adicionado ao protótipo


[Link] = function (price) { [Link]
= price;
};

Agora, vamos chamar esses métodos. O método estático isShiny() é invocado diretamente no construtor,
enquanto o método regular precisa de uma instância:
// chamando um método
estático [Link](); // "pode apostar"

// criando uma instância e chamando um método


var iphone = new Gadget();
[Link](500);

Tentar chamar um método de instância estaticamente não funcionará; o mesmo vale para chamar um
método estático usando o objeto de instância
iphone : typeof [Link]; // "indefinido"
typeof [Link]; // "indefinido"

Às vezes, pode ser conveniente ter os métodos estáticos funcionando também com uma instância. Isso é fácil de
conseguir simplesmente adicionando um novo método ao protótipo, que serve como uma fachada apontando para o
método estático original: [Link] = [Link];

[Link](); // "pode apostar"

Nesses casos, você precisa ter cuidado ao usar this dentro do método estático. Ao usar [Link]() ,
this dentro de isShiny() se referirá à função construtora Gadget . Se você usar [Link]() , this
apontará para iphone.

Um último exemplo mostra como você pode ter o mesmo método sendo chamado estaticamente e não
estaticamente e se comportando de forma ligeiramente diferente, dependendo do padrão de invocação.
Aqui, instanceof ajuda a determinar como o método foi chamado: //
construtor var
Gadget = function (preço) { [Link]ço
= preço;
};

// um método estático
[Link] = function () {

// isso sempre funciona

106 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

var msg = "pode apostar";

if (this instanceof Gadget) { // isso


só funciona se for chamado de forma não estática
msg += ", custa $" + [Link] + '!';
}

retornar mensagem;
};

// um método normal adicionado ao protótipo


[Link] = function () { return
[Link](this);
};

Testando uma chamada de método estático:

[Link](); // "pode apostar"

Testando uma instância, chamada não


estática: var a = new
Gadget('499.99'); [Link](); // "pode apostar, custa US$ 499,99!"

Membros Estáticos Privados

A discussão até agora foi sobre métodos estáticos públicos; agora vamos dar uma olhada em como
implementar membros estáticos privados . Por membros estáticos privados, queremos dizer membros
que são:

• Compartilhado por todos os objetos criados com a mesma função construtora •


Não acessível fora do construtor

Vejamos um exemplo em que counter é uma propriedade estática privada no construtor Gadget.
Neste capítulo, já discutimos sobre propriedades privadas, então esta parte continua a mesma —
você precisa de uma função para atuar como um fechamento e envolver os membros privados.
Então, vamos executar a mesma função wrapper imediatamente e retornar uma nova função. O valor
da função retornada é atribuído à variável Gadget e se torna o novo construtor:

var Gadget = (função () {

// variável/propriedade estática
var counter = 0;

// retornando a nova implementação // do


construtor return function
() { [Link](counter
+= 1);
};

}()); // execute imediatamente

Membros Estáticos | 107


Machine Translated by Google [Link]

O novo construtor Gadget simplesmente incrementa e registra o contador privado. Testando com
várias instâncias, você pode ver que o contador é de fato compartilhado entre todas as instâncias:
var g1 = novo gadget(); // registra 1 var
g2 = new Gadget(); // logs 2 var g3 =
new Gadget(); //registra 3

Como estamos incrementando o contador com um para cada objeto, essa propriedade estática se
torna um ID que identifica exclusivamente cada objeto criado com o construtor Gadget .
O identificador exclusivo pode ser útil, então por que não expô-lo por meio de um método privilegiado?
Abaixo está um exemplo que se baseia no anterior e adiciona um método privilegiado getLastId() para
acessar a propriedade privada estática:
// construtor var
Gadget = (function () {

// variável/propriedade estática
var counter = 0,
Novo Gadget;

// isso se tornará a // nova


implementação do construtor
NewGadget = função ()
{ contador += 1;
};

// um método privilegiado
[Link] = function () { return
counter;
};

// sobrescreve o construtor return


NewGadget;

}()); // execute imediatamente

Testando a nova implementação: var


iphone = new Gadget();
[Link](); // 1 var
ipod = new Gadget();
[Link](); // 2 var
ipad = new Gadget();
[Link](); //3

Propriedades estáticas (privadas e públicas) podem ser bastante úteis. Elas podem conter métodos
e dados que não são específicos da instância e não são recriados a cada instância.
No Capítulo 7, quando discutimos o padrão singleton, você pode ver um exemplo de implementação
que usa propriedades estáticas para implementar construtores singleton semelhantes a classes.

108 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Constantes de Objeto

Não há constantes em JavaScript, embora muitos ambientes modernos possam oferecer a instrução const
para criar constantes.

Como solução alternativa, uma abordagem comum é usar uma convenção de nomenclatura e destacar as
variáveis que não devem ser alteradas usando letras maiúsculas. Essa convenção é usada nos objetos
JavaScript integrados:
[Link]; // 3,141592653589793
Math.SQRT2; // 1,4142135623730951
Número.VALOR_MÁXIMO; // 1,7976931348623157e+308

Para suas próprias constantes, você pode adotar a mesma convenção de nomenclatura e adicioná-las
como propriedades estáticas à função construtora: //
construtor var
Widget = function () { //
implementação...
};

// constantes
Widget.MAX_HEIGHT = 320;
Widget.MAX_WIDTH = 480;

A mesma convenção pode ser aplicada para objetos criados com um literal; as constantes podem ser
propriedades normais com nomes em letras maiúsculas.

Se você realmente deseja ter um valor imutável, pode criar uma propriedade privada e fornecer um método
getter, mas nenhum setter. Isso provavelmente é um exagero em muitos casos, quando você pode se virar
com uma convenção simples, mas ainda é uma opção.

O exemplo a seguir é uma implementação de um objeto constante de uso geral , que fornece estes métodos:

definir(nome, valor)
Para definir uma nova constante

isDefined(nome)
Para verificar se uma constante existe

obter(nome)
Para obter o valor de uma constante

Nesta implementação, apenas valores primitivos são permitidos como constantes. Além disso,
tomamos um cuidado extra para garantir que seja aceitável declarar constantes com nomes que
sejam nomes de propriedades internas, como toString ou hasOwnProperty , usando uma verificação
hasOwnProperty() e, adicionalmente, adicionando um prefixo gerado aleatoriamente a todos os
nomes de constantes: var
constant = (function () { var
constants = {},
ownProp = [Link],
allowed = {

Constantes de Objetos | 109


Machine Translated by Google [Link]

string: 1,
número: 1,
booleano: 1
},
prefix = ([Link]() + "_").slice(2); return { set:
function
(nome, valor) {
se ([Link](nome)) { retornar
falso;

} se (![Link](permitido, tipo de valor)) { retornar


falso;

} constantes[prefixo + nome] = valor;


retornar verdadeiro;
},
isDefined: function (nome) { return
[Link](constantes, prefixo + nome);
},
get: function (nome) { if
([Link](nome)) { return
constantes[prefixo + nome];

} retornar nulo;
}

}; }());

Testando a implementação:
// verifique se constante
[Link]("maxwidth"); // falso

// define
[Link]("maxwidth", 480); // verdadeiro

// verifique
novamente [Link]("maxwidth"); // true

// tentativa de redefinir
[Link]("maxwidth", 320); // falso

// o valor ainda está intacto?


[Link]("maxwidth"); // 480

Padrão de encadeamento

O padrão de encadeamento permite que você chame métodos em um objeto um após o


outro, sem atribuir os valores de retorno das operações anteriores às variáveis e sem
precisar dividir suas chamadas em várias linhas:
myobj.method1("olá").method2().method3("mundo").method4();

110 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Ao criar métodos sem valor de retorno significativo, você pode fazer com que eles retornem this, a
instância do objeto com o qual estão trabalhando. Isso permitirá que os consumidores desse objeto
chamem o próximo método encadeado ao anterior:
var obj =
{ valor: 1,
incremento: função ()
{ [Link] += 1;
retornar isto;
},
adicione: função (v)
{ [Link] += v;
retorne isto;
},
shout: função ()
{ alerta([Link]);
}
};

// método de cadeia
chama [Link]().add(3).shout(); // 5

// em vez de chamá-los um por um


[Link]();
[Link](3);
[Link](); // 5

Prós e contras do padrão de encadeamento Um

benefício de usar o padrão de encadeamento é que você pode economizar digitação e criar um
código mais conciso que quase parece uma frase.

Outro benefício é que ele ajuda você a pensar em dividir suas funções e criar funções menores e
mais especializadas, em vez de funções que tentam fazer muita coisa.
Isso melhora a manutenibilidade a longo prazo.

Uma desvantagem é que fica mais difícil depurar código escrito dessa maneira. Você pode saber
que um erro ocorreu em uma linha específica, mas há muita coisa acontecendo nessa linha.
Quando um dos vários métodos que você encadeou falha silenciosamente, você não sabe qual deles.
Robert Martin, autor do livro Clean Code, chega a chamar isso de padrão de “desastre”.

De qualquer forma, é bom reconhecer esse padrão, e quando um método que você escreve não
tem um valor de retorno óbvio e significativo, você sempre pode retornar isso. O padrão é
amplamente utilizado, por exemplo, na biblioteca jQuery. E se você observar a API DOM, notará
que ela também é propensa a encadeamentos com construções como:
[Link]('cabeça')[0].appendChild(novo nó);

Padrão de Encadeamento | 111


Machine Translated by Google [Link]

método() Método
JavaScript pode ser confuso para programadores acostumados a pensar em termos de classes. É
por isso que alguns desenvolvedores optam por tornar o JavaScript mais semelhante a classes.
Uma dessas tentativas é a ideia do método method() , introduzida por Douglas Crockford. Em
retrospectiva, ele admite que tornar o JavaScript semelhante a classes não é uma abordagem
recomendada, mas, ainda assim, é um padrão interessante, e você pode encontrá-lo em algumas
aplicações.

Usar funções construtoras é semelhante ao uso de classes em Java. Elas também permitem
adicionar propriedades de instância a this dentro do corpo do construtor. No entanto, adicionar
métodos a this é ineficiente, pois eles acabam sendo recriados a cada instância, o que consome
mais memória. É por isso que métodos reutilizáveis devem ser adicionados à propriedade
prototype do construtor. O protótipo pode parecer estranho para muitos desenvolvedores, então
você pode escondê-lo atrás de um método.

Adicionar funcionalidades convenientes a uma linguagem é frequentemente chamado de "açúcar


sintático" ou simplesmente "açúcar". Nesse caso, você poderia chamar o método method() de
"método açúcar".

A maneira de definir uma “classe” usando o método sugar() seria algo como o seguinte:
var Pessoa = função (nome)
{ [Link] = nome;
}.
método('getName', função () { return
[Link];
}).
método('setName', função (nome)
{ [Link] = nome;
retornar
isto; });

Observe como o construtor está encadeado à chamada de method(), que por sua vez está
encadeada à próxima chamada de method() e assim por diante. Isso segue o padrão de
encadeamento descrito anteriormente e ajuda a definir toda a "classe" com uma única instrução.

O método() recebe dois parâmetros:


• O nome do novo método

• A implementação do método

Este novo método é então adicionado à "classe" Person . A implementação é apenas mais uma
função, e dentro da função de implementação this aponta para o objeto criado por Person, como
seria de se esperar.

112 | Capítulo 5: Padrões de Criação de Objetos


Machine Translated by Google [Link]

Veja como você pode usar Person() para criar e usar um novo objeto:
var a = new Pessoa('Adam');
[Link](); // 'Adão'
[Link]('Eva').getName(); // 'Eva'

Novamente, observe o padrão de encadeamento em ação, possível simplesmente porque setName()


retornou isso.

E finalmente, veja como o método method() é implementado:


se (tipo de Funçã[Link]ótipo.método !== "função") {
[Link] = função (nome, implementação) {
[Link][nome] = implementação;
retornar isto;
};
}

Na implementação de method(), primeiro fazemos a devida diligência para verificar se o método já


não está implementado. Caso contrário, prosseguimos adicionando a função passada como
implementação de argumento ao protótipo do construtor. Neste caso, isso se refere à função
construtora, cujo protótipo é ampliado.

Resumo
Neste capítulo, você aprendeu sobre diferentes padrões para criar objetos que vão além dos padrões
básicos de uso de literais de objeto e funções construtoras.

Você aprendeu sobre o padrão de namespace que mantém o espaço global limpo e ajuda a organizar
e estruturar o código. Você também aprendeu sobre o padrão simples, porém surpreendentemente
útil, de declaração de dependências. Em seguida, houve uma discussão detalhada sobre padrões
de privacidade, incluindo membros privados , métodos privilegiados , alguns casos extremos com
privacidade, o uso de literais de objeto com membros privados e a revelação de métodos privados
como públicos. Todos esses padrões são os blocos de construção do popular e poderoso padrão de
módulo.

Depois, você aprendeu sobre o padrão sandbox como uma alternativa ao namespace longo, o que
também ajuda a criar ambientes independentes para seu código e módulos.

Para encerrar a discussão, demos uma olhada mais aprofundada em constantes de objetos, métodos
estáticos (públicos e privados), encadeamento e um curioso método method() .

Resumo | 113
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 6

Padrões de Reutilização de Código

A reutilização de código é um tópico importante e interessante simplesmente porque é natural se


esforçar para escrever o mínimo possível e reutilizar o máximo possível de código existente, que
você ou outra pessoa já tenha escrito. Principalmente se for um código bom, testado, sustentável,
extensível e documentado.

Ao falar sobre reutilização de código, a primeira coisa que vem à mente é herança, e grande parte
do capítulo é dedicada a esse tópico. Você verá várias maneiras de fazer herança "clássica" e
não clássica. Mas é importante manter o objetivo final em mente — queremos reutilizar código.
Herança é uma maneira (meio) de atingirmos esse objetivo. E não é a única. Você verá como
compor objetos a partir de outros objetos, como usar mix-ins de objetos e como pode pegar
emprestado e reutilizar apenas a funcionalidade necessária sem, tecnicamente, herdar nada
permanentemente.

Ao abordar uma tarefa de reutilização de código, tenha em mente o conselho que o livro Gang of
Four oferece sobre criação de objetos: “Prefira composição de objetos à herança de classes”.

Padrões de herança clássicos versus modernos


Frequentemente, ouvimos o termo "herança clássica" em discussões sobre o tema de herança
em JavaScript, então vamos primeiro esclarecer o que significa "clássica ". O termo não é usado
no sentido de algo antigo, estabelecido ou amplamente aceito como a maneira correta de fazer as
coisas. O termo é apenas um trocadilho com a palavra "classe".

Muitas linguagens de programação têm a noção de classes como modelos para objetos. Nessas
linguagens, cada objeto é uma instância de uma classe específica e (no caso de Java, por exemplo)
um objeto não pode ser criado se a classe correspondente não existir. Em JavaScript, como não
existem classes, a noção de instâncias de classes não faz muito sentido.
Objetos em JavaScript são simplesmente pares chave-valor, que você pode criar e alterar
rapidamente.

Mas JavaScript tem funções construtoras, e a sintaxe do operador new se assemelha muito à
sintaxe de usar classes.

115
Machine Translated by Google [Link]

Em Java você poderia fazer algo como:


Pessoa adam = new Pessoa();

Em JavaScript você faria: var


adam = new Person();

Além do fato de que Java é fortemente tipado e você precisa declarar que adam é do tipo
Person, a sintaxe é a mesma. A invocação do construtor em JavaScript parece como se Person
fosse uma classe, mas é importante ter em mente que Person ainda é apenas uma função.
A similaridade na sintaxe levou muitos desenvolvedores a pensar em JavaScript em termos de
classes e a desenvolver ideias e padrões de herança que pressupõem classes. Podemos
chamar tais implementações de "clássicas". Digamos também que "modernos" são quaisquer
outros padrões que não exijam que você pense em classes.

Quando se trata de adotar um padrão de herança para o seu projeto, você tem algumas opções.
Você deve sempre se esforçar para escolher um padrão moderno, a menos que a equipe se
sinta realmente desconfortável com a ausência de classes.

Este capítulo discute primeiro os padrões clássicos e depois passa para os modernos.

Resultado esperado ao usar herança clássica


O objetivo de implementar a herança clássica é fazer com que objetos criados por uma função
construtora Child() obtenham propriedades que vêm de outro construtor Parent().

Embora a discussão seja sobre padrões clássicos, vamos evitar o uso da palavra
"classe". Dizer "função construtora" ou "construtor" é mais longo, mas é preciso e não
ambíguo. Em geral, procure eliminar a palavra "classe" ao se comunicar com sua
equipe, pois, quando se trata de JavaScript, a palavra pode ter significados diferentes
para cada pessoa.

Aqui está um exemplo de definição dos dois construtores Parent() e Child():


// a função construtora pai
Parent(name) { [Link]
= name || 'Adam';
}

// adicionando funcionalidade ao protótipo


[Link] = function () { return
[Link];
};

// função construtora de
criança vazia Child(nome) {}

116 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

// a mágica da herança acontece


aqui inherit(Child, Parent);

Aqui você tem os construtores pai e filho, um método say() adicionado ao protótipo do construtor
pai e uma chamada para uma função chamada inherit() que cuida da herança. A função inherit()
não é fornecida pela linguagem, então você precisa implementá-la. Vejamos várias abordagens
para implementá-la de forma genérica.

Padrão Clássico #1 — O Padrão Padrão


O método padrão mais comumente usado é criar um objeto usando o construtor Parent() e
atribuí-lo ao protótipo do Child() . Aqui está a primeira implementação da função reutilizável
inherit() :
função herdar(C, P) {
[Link]ótipo = novo P();
}

É importante lembrar que a propriedade prototype deve apontar para um objeto, não para uma
função, portanto, ela precisa apontar para uma instância (um objeto) criada com o construtor
pai, não para o construtor em si. Em outras palavras, preste atenção ao operador new , pois
você precisa dele para que este padrão funcione.

Mais tarde em seu aplicativo, quando você usa new Child() para criar um objeto, ele obtém
funcionalidade da instância Parent() por meio do protótipo, conforme mostrado no exemplo a
seguir:
var kid = new Child();
[Link](); // "Adam"

Seguindo a cadeia de protótipos


Usando esse padrão, você herda propriedades próprias (propriedades específicas da instância
adicionadas a isso, como nome) e propriedades e métodos de protótipo (como say()).

Vamos revisar como a cadeia de protótipos funciona neste padrão de herança. Para os
propósitos desta discussão, vamos pensar nos objetos como blocos em algum lugar da
memória, que podem conter dados e referências a outros blocos. Ao criar um objeto usando
new Parent(), você cria um bloco desse tipo (marcado como bloco nº 2 na Figura 6-1). Ele
contém os dados para a propriedade name . Se você tentar acessar o método say() (por
exemplo, usando (new Parent).say()), o bloco nº 2 não contém esse método. Mas, usando o
link oculto __proto__ que aponta para a propriedade prototype da função construtora Parent(),
você obtém acesso ao objeto nº 1 ([Link]), que conhece o método say() . Tudo isso
acontece nos bastidores e você não precisa se preocupar com isso, mas é importante saber
como funciona e onde estão os dados que você está acessando ou talvez modificando. Observe
que __proto__ é usado aqui para explicar a cadeia de protótipos; essa propriedade não está
disponível no idioma em si, embora seja fornecida em alguns ambientes (por exemplo, Firefox).

Padrão Clássico #1 — O Padrão Padrão | 117


Machine Translated by Google [Link]

Figura 6-1. Cadeia de protótipos para o construtor Parent()

Agora vamos ver o que acontece quando um novo objeto é criado usando var kid = new
Child() após usar a função inherit() . O diagrama é mostrado na Figura 6-2.

Figura 6-2. Cadeia de protótipos após herança

O construtor Child() estava vazio e nenhuma propriedade foi adicionada a [Link];


portanto, usar new Child() cria objetos praticamente vazios, exceto pelo link oculto
__proto__. Nesse caso, __proto__ aponta para o novo objeto Parent() criado na função
inherit() .
Agora, o que acontece quando você executa [Link]()? O objeto nº 3 não possui esse
método, então ele procura o nº 2 por meio da cadeia de protótipos. O objeto nº 2 também
não o possui, então ele segue a cadeia até o nº 1, que por acaso o possui. Então, dentro
de say(), há uma referência a [Link], que precisa ser resolvida. A busca começa
novamente. Nesse caso, this aponta para o objeto nº 3, que não possui name. O objeto nº
2 é consultado e possui uma propriedade name , com o valor "Adam".

118 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

Por fim, vamos dar uma olhada em mais um passo. Digamos que temos este código:
var criança = new Criança();
crianç[Link] = "Patrick";
crianç[Link](); // "Patrick"

A Figura 6-3 mostra como a corrente ficará neste caso.

Figura 6-3. Cadeia de protótipos após herança e adição de uma propriedade ao objeto filho

Definir [Link] não altera a propriedade name do objeto nº 2, mas cria uma propriedade própria
diretamente no objeto kid nº 3. Quando você executa [Link](), o método say é pesquisado no objeto nº
3, depois no nº 2 e, finalmente, encontrado no nº 1, como antes. Mas desta vez, a pesquisa por
[Link] (que é o mesmo que [Link]) é rápida, pois a propriedade é encontrada imediatamente no
objeto nº 3.

Se você remover a nova propriedade usando delete [Link], a propriedade name do objeto #2 “irá
brilhar” e será encontrada em pesquisas consecutivas.

Desvantagens ao Usar o Padrão nº 1 Uma

desvantagem desse padrão é que você herda tanto as propriedades próprias adicionadas a ele quanto
as propriedades do protótipo. Na maioria das vezes, você não quer as propriedades próprias, pois elas
provavelmente serão específicas de uma instância e não podem ser reutilizadas.

Uma regra geral com construtores é que os membros reutilizáveis devem ser
adicionados ao protótipo.

Padrão Clássico #1 — O Padrão Padrão | 119


Machine Translated by Google [Link]

Outra coisa sobre usar uma função inherit() genérica é que ela não permite que você passe parâmetros
para o construtor filho, que então o filho passa para o pai.
Considere este exemplo:
var s = new Child('Seth');
[Link](); // "Adão"

Isso não é o que você esperaria. É possível que o filho passe parâmetros para o construtor do pai,
mas você terá que fazer a herança toda vez que precisar de um novo filho, o que é ineficiente, porque
você acaba recriando os objetos pais repetidamente.
sobre.

Padrão Clássico #2 — Aluguel de Construtor


O próximo padrão resolve o problema de passar argumentos do filho para o pai.
Ele toma emprestado o construtor pai, passando o objeto filho a ser vinculado a isso e também
encaminhando quaisquer argumentos:
função Criança(a, c, b, d) {
[Link](isto, argumentos);
}

Dessa forma, você só pode herdar propriedades adicionadas a isso dentro do construtor pai.
Você não herda membros que foram adicionados ao protótipo.

Usando o padrão de construtor emprestado, os objetos filhos recebem cópias dos membros herdados,
ao contrário do padrão clássico nº 1, em que recebem apenas referências. O exemplo a seguir ilustra
a diferença:
// uma função construtora
pai Article() { [Link]
= ['js', 'css'];

} var artigo = novo Artigo();

// uma postagem de blog herda de um objeto de


artigo // por meio do padrão clássico
#1 função BlogPost() {}
[Link] = article; var
blog = new BlogPost(); //
observe que acima você não precisava de `new Article()` //
porque você já tinha uma instância disponível

// uma página estática herda do artigo // por


meio do padrão de construtor alugado
function StaticPage()
{ [Link](this);

} var página = nova StaticPage();

alert([Link]('tags')); // verdadeiro
alert([Link]('tags')); // falso
alert([Link]('tags')); // verdadeiro

120 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

Neste snippet, o objeto pai Article() é herdado de duas maneiras. O padrão padrão faz com que
o objeto do blog tenha acesso à propriedade tags por meio do protótipo, portanto, ele não a
possui como propriedade própria e hasOwnProperty() retorna falso. O objeto da página possui
uma propriedade tags própria porque, ao usar o construtor alugado, o novo objeto obteve uma
cópia (não uma referência) do membro tags do objeto pai .

Observe a diferença ao modificar a propriedade tags herdadas :


[Link]('html');
pá[Link]('php');
alerta([Link](', ')); // "js, css, html"

Neste exemplo, o objeto blog filho modifica a propriedade tags e, dessa forma, também modifica
o objeto pai, pois essencialmente [Link] e [Link] apontam para o mesmo array. Alterações
em [Link] não afetam o objeto pai, pois [Link] é uma cópia separada criada durante a
herança.

A Cadeia de Protótipos
Vamos dar uma olhada na aparência da cadeia de protótipos usando este padrão e os conhecidos
construtores Parent() e Child() . Child() será ligeiramente modificado para seguir o novo padrão: //
o construtor pai
function Parent(name)
{ [Link] = name ||
'Adam';
}

// adicionando funcionalidade ao protótipo


[Link] = function () { return
[Link];
};

// função construtora
infantil Child(nome)
{ [Link](this, argumentos);
}

var criança = new Criança("Patrick");


crianç[Link]; // "Patrick"
typeof crianç[Link]; // "indefinido"

Se você observar a Figura 6-4, notará que não há mais um link entre o novo objeto Child e o
Parent. Isso ocorre porque [Link] não foi usado e simplesmente aponta para um objeto
em branco. Usando esse padrão, kid recebeu seu próprio nome de propriedade, mas o método
say() nunca foi herdado, e uma tentativa de chamá-lo resultará em um erro. A herança foi uma
ação única que copiou as propriedades do parent como propriedades do child e foi basicamente
isso; nenhum link __proto__ foi mantido.

Padrão Clássico #2 — Aluguel de Construtor | 121


Machine Translated by Google [Link]

Figura 6-4. A cadeia quebrada ao usar o padrão de construtor de empréstimo

Herança múltipla por empréstimo de construtores Usando os

padrões de construtores de empréstimo, é possível implementar herança múltipla simplesmente


pegando emprestado de mais de um construtor: function Cat() { [Link]
= 4; [Link] =
function ()
{ return "meaowww";

}
}

função Bird()
{ [Link] = 2;
[Link] = true;
}

função CatWings()
{ [Link](isto);
Pá[Link](isto);
}

var jane = novo CatWings();


[Link](jane);

O resultado é mostrado na Figura 6-5. Quaisquer propriedades duplicadas serão resolvidas com a
vitória da última.

122 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

Figura 6-5. Um objeto CatWings inspecionado no Firebug

Prós e contras do padrão de construtor de empréstimo


A desvantagem desse padrão é, obviamente, que nada do protótipo é herdado e,
como mencionado antes, o protótipo é o lugar para adicionar métodos e
propriedades reutilizáveis, que não serão recriados para cada instância.
Uma vantagem é que você obtém cópias fiéis dos membros do pai ou da mãe, e não há risco de
um filho acidentalmente sobrescrever a propriedade do pai ou da mãe.

Então, como os filhos também podem herdar propriedades do protótipo, no caso anterior, e como a
criança pode ter acesso ao método say() ? O próximo padrão aborda essa questão.

Padrão Clássico #3 — Protótipo de Aluguel e Configuração


Combinando os dois padrões anteriores, você primeiro pega emprestado o construtor e então
também define o protótipo do filho para apontar para uma nova instância do
construtor: function Child(a, c, b, d) {
[Link](isto, argumentos);
}
[Link]ótipo = novo Pai();

A vantagem é que os objetos resultantes obtêm cópias dos próprios membros do objeto pai e
referências à funcionalidade reutilizável do objeto pai (implementada como membros do protótipo).
O objeto filho também pode passar quaisquer argumentos para o construtor do objeto pai. Esse
comportamento é provavelmente o mais próximo do que se esperaria em Java; você herda tudo o
que está no objeto pai e, ao mesmo tempo, é seguro modificar suas próprias propriedades sem o
risco de modificar o objeto pai.

Uma desvantagem é que o construtor pai é chamado duas vezes, o que pode ser ineficiente. No
final, as propriedades próprias (como o nome no nosso caso) são herdadas duas vezes.

Vamos dar uma olhada no código e fazer alguns testes:


// a função construtora pai
Parent(name) { [Link]
= name || 'Adam';
}

// adicionando funcionalidade ao protótipo


[Link] = function () { return
[Link];

Padrão Clássico #3 — Protótipo de Aluguel e Configuração | 123


Machine Translated by Google [Link]

};

// função construtora
infantil Child(nome)
{ [Link](this, argumentos);
}
[Link]ótipo = novo Pai();

var criança = new Criança("Patrick");


crianç[Link]; // "Patrick"
crianç[Link](); // "Patrick"
delete crianç[Link];
crianç[Link](); // "Adam"

Ao contrário do padrão anterior, agora say() é herdado corretamente. Você também pode notar que
o nome é herdado duas vezes e, após excluirmos a própria cópia, a que vem na cadeia de protótipos
se destacará.

A Figura 6-6 mostra como funcionam os relacionamentos entre os objetos. Os relacionamentos são
semelhantes aos mostrados na Figura 6-3, mas o caminho que percorremos até lá foi diferente.

Figura 6-6. A cadeia de protótipos é mantida além dos membros próprios herdados

Padrão Clássico #4 — Compartilhe o Protótipo


Diferentemente do padrão de herança clássico anterior, que exigia duas chamadas ao construtor pai,
o próximo padrão não envolve nenhuma chamada ao construtor pai.

A regra geral era que os membros reutilizáveis deveriam ir para o protótipo e não para este. Portanto,
para fins de herança, tudo o que valesse a pena herdar deveria estar no

124 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

protótipo. Então, você pode simplesmente definir o protótipo do filho para ser o mesmo que o protótipo
do pai:
function inherit(C, P)
{ [Link] = [Link];
}

Isso proporciona consultas curtas e rápidas na cadeia de protótipos, pois todos os objetos compartilham
o mesmo protótipo. Mas isso também é uma desvantagem, pois se um filho ou neto em algum ponto
da cadeia de herança modifica o protótipo, isso afeta todos os pais e avós.

Como mostra a Figura 6-7 , objetos filho e pai compartilham o mesmo protótipo e têm acesso igual ao
método say() . No entanto, objetos filhos não herdam a propriedade name .

Figura 6-7. Relacionamentos ao compartilhar o mesmo protótipo

Padrão Clássico #5 — Um Construtor Temporário


O próximo padrão resolve o problema do mesmo protótipo quebrando o vínculo direto entre o protótipo
do pai e o do filho e, ao mesmo tempo, se beneficiando da cadeia de protótipos.

Abaixo está uma implementação deste padrão, onde você tem uma função F() vazia, que serve como
um proxy entre o filho e o pai. A propriedade prototype de F() aponta para o protótipo do pai. O
protótipo do filho é uma instância da função em branco:

função herdar(C, P) {
var F = função () {};
[Link]ótipo = [Link]ótipo;
[Link]ótipo = novo F();
}

Este padrão tem um comportamento ligeiramente diferente do padrão padrão (padrão clássico nº 1)
porque aqui o filho herda apenas propriedades do protótipo (veja Figura 6-8).

Padrão Clássico #5 — Um Construtor Temporário | 125


Machine Translated by Google [Link]

Figura 6-8. Herança clássica usando um construtor temporário (proxy) F()

E isso geralmente é aceitável, na verdade preferível, porque o protótipo é o lugar para funcionalidades
reutilizáveis. Nesse padrão, quaisquer membros que o construtor pai adiciona a este não são herdados.

Vamos criar um novo objeto filho e inspecionar seu comportamento:


var criança = novo Filho();

Se você acessar [Link], ele será indefinido. Neste caso, name é uma propriedade própria do objeto
pai e, durante a herança, nunca chamamos new Parent(), então essa propriedade nunca foi criada. Ao
acessar [Link](), ele não estará disponível no objeto nº 3, então a cadeia de protótipos é consultada. O
objeto nº 4 também não possui esse método, mas o objeto nº 1 possui, e este é o mesmo local na
memória, que será reutilizado por todos os diferentes construtores que herdam Parent() e por todos os
objetos criados por todos os filhos.

Armazenando o Superclass

Building em cima do padrão anterior, você pode adicionar uma referência ao pai original.
Isso é como ter acesso à superclasse em outras linguagens e pode ser útil em algumas ocasiões.

A propriedade é chamada de uber porque “super” é uma palavra reservada e “superclasse” pode levar o
desenvolvedor desavisado a pensar que JavaScript tem classes.
Aqui está uma implementação melhorada deste padrão clássico:

126 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

função herdar(C, P) {
var F = função () {};
[Link]ótipo = [Link]ótipo;
[Link]ótipo = novo F();
[Link] = [Link]ótipo;
}

Redefinindo o ponteiro do construtor


Uma última coisa a adicionar a essa função de herança clássica quase perfeita é
redefinir o ponteiro para a função do construtor, caso você precise dele no futuro.
Se você não redefinir o ponteiro para o construtor, todos os objetos filhos reportarão que Parent()
foi seu construtor, o que não é útil. Portanto, usando a implementação anterior de inherit(), você
pode observar este comportamento:
// pai, filho, função de herança
Parent() {} função
Child() {} herdar(Filho,
Pai);

// testando as águas var


kid = new Child();
[Link]; // "Pai"
[Link] === Parent; // true

A propriedade construtora raramente é usada, mas pode ser conveniente para introspecção de
objetos em tempo de execução. Você pode redefini-la para apontar para a função construtora
esperada sem afetar a funcionalidade, pois essa propriedade é principalmente informativa.

A versão final do Santo Graal desse padrão clássico de herança será semelhante a esta:
função herdar(C, P) {
var F = função () {};
[Link]ótipo = [Link]ótipo;
[Link]ótipo = novo F();
[Link] = [Link]ótipo;
[Link]ó[Link] = C;
}

Uma função semelhante a esta existe na biblioteca YUI (e provavelmente em outras bibliotecas)
e traz a herança clássica para uma linguagem sem classes, se você decidir que esta é a melhor
abordagem para seu projeto.

Esse padrão também é conhecido como aquele que usa uma função proxy ou um
construtor proxy, em vez de um construtor temporário, porque o construtor temporário é
usado como um proxy para chegar ao protótipo do pai.

Uma otimização comum do padrão Santo Graal é evitar a criação do construtor temporário (proxy)
sempre que você precisar de herança. Basta criá-lo uma vez e

Padrão Clássico #5 — Um Construtor Temporário | 127


Machine Translated by Google [Link]

alterar apenas seu protótipo. Você pode usar uma função imediata e armazenar a função proxy em
seu fechamento:

var herdar = (função () { var F =


função () {}; retornar
função (C, P) { [Link]ótipo
= [Link]ótipo; [Link]ótipo =
novo F(); [Link] =
[Link]ótipo;
[Link]ó[Link] = C;

} }());

Classe

Muitas bibliotecas JavaScript emulam classes, introduzindo uma nova sintaxe sugar. As
implementações diferem, mas geralmente há alguns pontos em comum, incluindo os seguintes:

• Há uma convenção sobre como nomear um método, que deve ser considerado o construtor da
classe, por exemplo, initialize, _init ou algo semelhante, e que é chamado automaticamente. •
As classes herdam de outras classes.

• Há acesso à classe pai (superclasse) de dentro da classe filha.

Vamos mudar de assunto aqui e, somente nesta parte do capítulo, usar a palavra “classe” livremente porque o

tópico é emular classes.

Sem entrar em muitos detalhes, vejamos um exemplo de implementação de classes simuladas em


JavaScript. Primeiro, como a solução será usada da perspectiva do cliente?
var Man = klass(null,
{ __construct: function (what)
{ [Link]("Construtor do Man");
[Link] = what;
},
getName: função ()
{ return [Link];

} });

A sintaxe sugar vem na forma de uma função chamada klass(). Em algumas implementações,
você pode vê-la como um construtor Klass() ou como um [Link] aumentado , mas neste
exemplo, vamos mantê-la como uma função simples.

A função recebe dois parâmetros: uma classe pai a ser herdada e a implementação da nova classe
fornecida por um literal de objeto. Influenciados pelo PHP, vamos estabelecer a convenção de que
o construtor da classe deve ser um método chamado __construct.

128 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

No snippet anterior, uma nova classe chamada Man é criada e não herda de nada (o que significa que
herda de Object nos bastidores). A classe Man tem uma propriedade name criada dentro de __construct
e um método getName(). A classe é uma função construtora, então o seguinte ainda funcionará (e se
parecerá com uma instanciação de classe):
var first = new Man('Adam'); // registra o "construtor do Man"
[Link](); // "Adam"

Agora vamos estender esta classe e criar uma classe SuperMan :


var SuperMan = klass(Man,
{ __construct: function (what)
{ [Link]("Construtor do SuperMan");
},
getName: function () { var name
= [Link](this); return "Eu sou + nome;
"
}
});

Aqui, o primeiro parâmetro para klass() é a classe Man a ser herdada. Observe também em getName()
que a função getName() da classe pai é chamada primeiro usando a propriedade uber (super) estática
de SuperMan. Vamos testar: var clark = new
SuperMan('Clark Kent'); [Link](); //
"Eu sou Clark Kent"

A primeira linha registra no console “Construtor do Homem” e depois “Construtor do Superman”. Em


algumas linguagens, o construtor do pai é chamado automaticamente toda vez que o construtor do
filho é chamado, então por que não emular isso também?

Testando se o operador instanceof retorna os resultados esperados:


clark instanceof Man; // verdadeiro
clark instanceof SuperMan; // verdadeiro

Por fim, vamos ver como a função klass() pode ser implementada: var klass
= function (Parent, props) {

var Criança, F, i;

// 1. //
novo construtor Child
= function () { if
([Link] && [Link]("__construct"))
{ [Link].__construct.apply(this, argumentos);

} se (Crianç[Link]ó[Link]("__construct")) {
[Link].__construct.apply(isto, argumentos);
}
};

// 2. //
herdar Pai
= Pai || Objeto;

Classe | 129
Machine Translated by Google [Link]

F = função () {};
[Link]ótipo = [Link]ótipo;
[Link]ótipo = novo F();
[Link] = [Link]ótipo;
[Link]ó[Link] = Filho;

// 3. //
adicione métodos de
implementação para
(i em props) { if ([Link](i)) {
Crianç[Link]ótipo[i] = adereços[i];
}
}

// retorna a "classe"
return Child;
};

A implementação de klass() tem três partes interessantes e distintas:

1. Uma função construtora Child() é criada. Esta é a função que será retornada no final e será
usada como uma classe. Nesta função, o método __construct é chamado, se existir. Além
disso, antes disso, o método __construct do método pai é chamado (novamente, se existir)
usando a propriedade estática uber . Pode haver casos em que uber não esteja definido —
quando você herda de Object , por exemplo, como foi o caso com a definição da classe Man .

2. A segunda parte cuida da parte da herança. Ela simplesmente usa o padrão do Santo Graal da
herança clássica, discutido na seção anterior do capítulo.
Há apenas uma coisa nova: definir o Pai como Objeto se nenhum Pai foi passado para herdar.

3. A seção final faz um loop em todos os métodos de implementação (como __construct e


getName nos exemplos), que são a definição real da classe, e os adiciona ao protótipo de
Child.

Quando usar esse padrão? Bem, na verdade é melhor evitá-lo, porque ele traz consigo toda a
noção confusa de classes, que tecnicamente não existem na linguagem. Ele adiciona nova sintaxe
e novas regras para aprender e memorizar. Dito isso, se você ou a equipe se sentem à vontade
com classes e, ao mesmo tempo, se sentem desconfortáveis com protótipos, então isso pode ser
algo a ser explorado. Esse padrão permite que você esqueça completamente os protótipos, e o
bom é que você pode ajustar a sintaxe e as convenções para se assemelharem a outra de suas
linguagens favoritas.

Herança Prototípica
Vamos começar a discussão sobre padrões sem classes "modernos" com um padrão chamado
herança prototípica. Nesse padrão, não há classes envolvidas; aqui, objetos herdam de outros
objetos. Você pode pensar da seguinte forma: você tem um objeto que gostaria de reutilizar e quer
criar um segundo objeto que obtenha sua funcionalidade do primeiro.

130 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

Veja como você faria isso: // objeto para


herdar de var parent =
{ name: "Papa"

};

// o novo objeto var


child = object(parent);

// alerta de
teste(crianç[Link]); // "Papai"

No snippet anterior, você tem um objeto existente chamado " parent" criado com o literal de objeto
e deseja criar outro objeto chamado "child" que possui as mesmas propriedades e métodos do
"parent". O objeto "child" foi criado com uma função chamada "object()". Essa função não existe
em JavaScript (não confunda com a função construtora " Object()"), então vamos ver como você
pode defini-la.

Similarmente ao Santo Graal clássico, você usaria uma função construtora temporária vazia, F().
Em seguida, você definiria o protótipo de F() como o objeto pai. Por fim, você retornaria uma nova
instância do construtor temporário:
função objeto(o)
{ função F() {}
[Link] = o;
retornar novo F();
}

A Figura 6-9 mostra a cadeia de protótipos ao usar o padrão de herança prototípica.


Aqui, a criança sempre começa como um objeto vazio, que não tem propriedades próprias, mas
ao mesmo tempo tem toda a funcionalidade de seu pai, beneficiando-se do __proto__
link.

Figura 6-9. Padrão de herança prototípico

Herança Prototípica | 131


Machine Translated by Google [Link]

Discussão
No padrão de herança prototípica, seu pai não precisa ser criado com a notação literal (embora essa
seja provavelmente a forma mais comum). Você pode fazer com que funções construtoras criem o pai.
Observe que, se você fizer isso, tanto as propriedades "próprias" quanto as propriedades do protótipo
do construtor serão herdadas: // construtor pai função Person() { // uma
propriedade "própria"
[Link] = "Adam";

} // uma propriedade adicionada ao


protótipo [Link] = function
() { return [Link];
};

// cria uma nova pessoa


var papa = new Person(); //
herda var
kid = object(papa);

// teste se tanto a propriedade própria //


quanto a propriedade do protótipo foram herdadas
[Link](); // "Adam"

Em outra variação deste padrão, você tem a opção de herdar apenas o objeto protótipo de um construtor
existente. Lembre-se: objetos herdam de objetos, independentemente de como os objetos pais foram
criados. Aqui está uma ilustração usando o exemplo anterior, ligeiramente modificado: // construtor pai
function Person() { // uma
propriedade "própria"
[Link] = "Adam";

} // uma propriedade adicionada ao


protótipo [Link] = function
() { return [Link];
};

// herdar
var kid = object([Link]);

typeof [Link]; // "função", porque estava no protótipo typeof [Link]; //


"indefinido", porque apenas o protótipo foi herdado

Adição ao ECMAScript 5 No

ECMAScript 5, o padrão de herança prototípica torna-se oficialmente parte da linguagem. Este padrão é
implementado através do método [Link](). Em outras palavras,

132 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

palavras, você não precisará criar sua própria função semelhante a object(); ela será incorporada à
linguagem: var child
= [Link](parent);

[Link]() aceita um parâmetro adicional, um objeto. As propriedades do objeto extra serão


adicionadas como propriedades próprias do novo objeto filho retornado. Esta é uma conveniência que
permite herdar e desenvolver o objeto filho com uma única chamada de método. Por exemplo: var child =
[Link](parent, {

idade: { valor: 2 } // descritor ECMA5 });

[Link]("idade"); // verdadeiro

Você também pode descobrir que o padrão de herança prototípico é implementado em bibliotecas
JavaScript; por exemplo, no YUI3 é o método [Link]() :
YUI().use('*', função (Y) {
var criança = [Link](pai); });

Herança por Cópia de Propriedades Vamos dar uma

olhada em outro padrão de herança: a herança por cópia de propriedades. Nesse padrão, um objeto
obtém funcionalidade de outro objeto simplesmente copiando-o.
Aqui está um exemplo de implementação de uma função de amostra extend() que faz isso: function
extend(parent, child) { var i; child =
child ||
{}; for (i in parent) { if

([Link](i)) { child[i] =
parent[i];
}

} retornar criança;
}

É uma implementação simples, apenas percorrendo os membros do pai e copiando-os. Nesta implementação, filho é
opcional; se você não passar um objeto existente para ser aumentado, um novo objeto será criado e retornado: var dad
= {name: "Adam"}; var kid = extend(dad); [Link]; // "Adam"

A implementação apresentada é uma chamada "cópia superficial" do objeto. Uma cópia profunda, por
outro lado, significaria verificar se a propriedade que você está prestes a copiar é um objeto ou um array
e, em caso afirmativo, iterar recursivamente por suas propriedades e copiá-las também. Com a cópia
superficial (porque os objetos são passados por referência em JavaScript), se você alterar uma propriedade
do objeto filho, e essa propriedade for um objeto, então

Herança por Cópia de Propriedades | 133


Machine Translated by Google [Link]

Você também modificará o pai. Isso é preferível para métodos (já que funções também são objetos e são
passadas por referência), mas pode causar surpresas ao trabalhar com outros objetos e arrays. Considere
o seguinte:
var dad =
{ contagens: [1, 2, 3],
leituras: {papel: verdadeiro}
};
var criança = estender(pai);
crianç[Link](4);
[Link](); // "1,2,3,4" pai.lê ===
criança.lê; // verdadeiro

Agora, vamos modificar a função extend() para fazer cópias profundas. Tudo o que você precisa é verificar
se o tipo de uma propriedade é um objeto e, em caso afirmativo, copiar recursivamente suas propriedades.
Outra verificação necessária é se o objeto é um objeto verdadeiro ou se é um array. Vamos usar a
verificação de array discutida no Capítulo 3. Portanto, a versão de cópia profunda de extend() ficaria assim:
function extendDeep(parent, child) { var i,
toStr
= [Link], astr =
"[object Array]";

criança = criança || {};

para (i no pai) { se
([Link](i)) { se
(typeof parent[i] === "object") { filho[i] =
([Link](parent[i]) === astr) ? [] : {}; extendDeep(parent[i],
filho[i]); } senão { filho[i] = pai[i];

}
}

} retornar criança;
}

Agora, testar a nova implementação nos dá cópias verdadeiras dos objetos, para que os objetos filhos não
modifiquem seus pais: var dad =
{ counts: [1,
2, 3], reads: {paper:
true}
};
var criança = extendDeep(pai);

crianç[Link](4);
crianç[Link](); // "1,2,3,4"
[Link](); // "1,2,3"

pai.lê === criança.lê; // falso


criança.lê.papel = falso;

134 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

criança.lê.teia =
verdadeiro; pai.lê.papel; // verdadeiro

Este padrão de cópia de propriedades é simples e amplamente utilizado; por exemplo, o Firebug
(as extensões do Firefox são escritas em JavaScript) tem um método chamado extend() que faz
cópias superficiais, e o extend() do jQuery cria uma cópia profunda. O YUI3 oferece um método
chamado [Link](), que cria uma cópia profunda e também copia funções, vinculando-as ao objeto
filho. (Mais informações sobre vinculação serão abordadas posteriormente neste capítulo.)

Vale ressaltar que não há protótipos envolvidos nesse padrão; trata-se apenas de objetos e suas
próprias propriedades.

Misturas
Levando a ideia de herança por cópia de propriedade um passo adiante, vamos considerar um
padrão de "mix-in". Em vez de copiar de um objeto, você pode copiar de qualquer número de
objetos e misturá-los em um novo objeto.

A implementação é simples; basta percorrer os argumentos e copiar todas as propriedades de cada objeto
passado para a função: function mix() { var arg,
prop, child = {}; for
(arg = 0; arg < [Link];
arg += 1) { for (prop in arguments[arg]) { if
(arguments[arg].hasOwnProperty(prop))
{ child[prop] = arguments[arg][prop];

}
}

} retornar criança;
}

Agora que você tem uma função mix-in genérica, pode passar qualquer número de objetos para
ela, e o resultado será um novo objeto que terá as propriedades de todos os objetos de origem.
Aqui está um exemplo de
uso: var cake =
mix( {eggs: 2, large: true},
{butter: 1, salted: true}, {flour:
"3 cups"}, {sugar:
"sure!"} );

A Figura 6-10 mostra o resultado da exibição das propriedades dos novos objetos cake misturados
executando [Link](cake) no console do Firebug.

Mix-ins | 135
Machine Translated by Google [Link]

Figura 6-10. Examinando o objeto bolo no Firebug

Se você está acostumado com o conceito de mix-in em linguagens nas quais é uma
parte oficial, pode esperar que a alteração de um ou mais dos pais afete o filho, mas
isso não é verdade na implementação em questão. Aqui, simplesmente fazemos
um loop, copiamos as próprias propriedades e quebramos o vínculo com o(s) pai(s).

Métodos de empréstimo
Às vezes pode acontecer de você gostar apenas de um ou dois métodos de um objeto existente.
Você quer reutilizá-los, mas não quer realmente formar uma relação pai-filho com esse objeto. Você
quer usar apenas os métodos que gosta, sem herdar todos os outros métodos dos quais nunca
precisará. Isso é possível com o padrão de empréstimo de métodos, que se beneficia dos métodos
de função call() e apply(). Você já viu esse padrão no livro e até mesmo neste capítulo, na
implementação de extendDeep(), por exemplo.

Como você sabe, funções em JavaScript são objetos e vêm com alguns métodos interessantes, como
call() e apply(). A única diferença entre os dois é que um recebe um array de parâmetros a serem
passados ao método que está sendo chamado, e o outro recebe parâmetros um por um. Você pode
usar esses métodos para pegar emprestada funcionalidades de objetos existentes: // call() exemplo
[Link](myobj, param1, p2,
p3); // apply()
exemplo [Link](myobj, [param1,
p2, p3]);

Aqui você tem um objeto chamado myobj e sabe que outro objeto chamado notmyobj possui este
método útil chamado doStuff(). Em vez de passar pelo incômodo da herança e herdar vários métodos
que seu myobj nunca precisará, você pode simplesmente pegar emprestado o método doStuff()
temporariamente.

Você passa seu objeto e quaisquer parâmetros, e o método emprestado vincula seu objeto como seu
próprio this. Basicamente, seu objeto finge ser o outro objeto por um tempo para se beneficiar do
método que você gosta. É como receber uma herança, mas sem pagar o imposto sobre herança
(onde o "imposto" vem na forma de propriedades e métodos extras dos quais você não precisa).

136 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

Exemplo: Pegar emprestado de uma

matriz Um uso comum para esse padrão é pegar emprestado métodos de matriz.

Arrays possuem métodos úteis, que objetos semelhantes a arrays, como argumentos, não possuem.
Portanto, argumentos podem tomar emprestado métodos de array, como o método slice(). Aqui está um exemplo:
função f() { var
args = [].[Link](argumentos, 1, 3); retornar
argumentos;
}

// exemplo
f(1, 2, 3, 4, 5, 6); // retorna [2,3]

Neste exemplo, um array vazio foi criado apenas para usar seu método. Uma maneira um pouco mais
demorada de fazer o mesmo é pegar emprestado o método diretamente do protótipo de Array , usando
[Link](...). Essa maneira é um pouco mais demorada para digitar, mas você economizará
o trabalho de criar um array vazio.

Emprestar e Vincular

Ao tomar métodos emprestados por meio de call()/apply() ou por atribuição simples, o objeto para o qual
this aponta dentro do método emprestado é determinado com base na expressão de chamada. Mas, às
vezes, é melhor ter o valor de this "bloqueado" ou vinculado a um objeto específico e predeterminado com
antecedência.

Vejamos um exemplo. Há um objeto chamado one que possui um método say() :


var um =
{ nome: "objeto",
diga: função (cumprimentar)
{ retorne cumprimentar + ", " + [Link];
}
};

// teste
[Link]('hi'); // "oi, objeto"

Agora, outro objeto dois não tem um método say() , mas pode pegá-lo emprestado de um:
var dois = {
nome: "outro objeto"
};

[Link](two, ['hello']); // "olá, outro objeto"

No caso anterior, this dentro de say() apontava para dois e [Link] era, portanto, "outro objeto". Mas e
quanto aos cenários em que você atribui o ponteiro de função a uma variável global ou passa a função
como um retorno de chamada? Na programação do lado do cliente, há muitos eventos e retornos de
chamada, então isso acontece bastante: // atribuindo a uma variável // `this`
apontará para o objeto global

Métodos de Empréstimo | 137


Machine Translated by Google [Link]

var dizer = [Link];


diga('hoho'); // "hoho, indefinido"

// passando como um retorno


de chamada var
yetanother = { name: "Yet another
object", method: function (callback)
{ return callback('Hola');
}
};
[Link]([Link]); // "Olá, indefinido"

Em ambos os casos, o método say() interno apontava para o objeto global, e o trecho inteiro não
funcionou como esperado. Para fixar (em outras palavras, vincular) um objeto a um método,
podemos usar uma função simples como
esta: function bind(o,
m) { return function ()
{ return [Link](o, [].[Link](arguments));
};
}

Esta função bind() aceita um objeto o e um método m, vincula os dois e retorna outra função. A
função retornada tem acesso a o e m por meio de um fechamento.
Portanto, mesmo após o retorno de bind() , a função interna terá acesso a o e m, que sempre
apontarão para o objeto e método originais. Vamos criar uma nova função usando bind(): var
twosay
= bind(two, [Link]); twosay('yo'); //
"yo, another object"

Como você pode ver, embora twosay() tenha sido criada como uma função global, this não
apontava para o objeto global, mas sim para o objeto two, que foi passado para bind().
Independentemente de como você chamar twosay(), this sempre estará vinculado a two.

O preço que você paga pelo luxo de ter uma encadernação é o fechamento adicional.

Funçã[Link]()
O ECMAScript 5 adiciona um método bind() a [Link], tornando-o tão fácil de usar
quanto apply() e call(). Assim, você pode criar expressões como:
var newFunc = [Link](myobj, 1, 2, 3);

Isso significa vincular someFunc() e myobj e também preencher previamente os três primeiros
argumentos esperados por someFunc() . Este também é um exemplo de aplicação de função
parcial discutido no Capítulo 4.

Vamos ver como você pode implementar [Link]() quando seu programa é
executado em ambientes pré-ES5:
se (tipo de Funçã[Link] === "indefinido")
{ Funçã[Link] = função (thisArg) { var fn = isto,

138 | Capítulo 6: Padrões de Reutilização de Código


Machine Translated by Google [Link]

fatia = [Link],
args = [Link](argumentos, 1);

retornar função ()
{ retornar [Link](thisArg, [Link]([Link](argumentos)));
};
};
}

Esta implementação provavelmente parece um pouco familiar; ela usa a aplicação parcial e concatena a lista de
argumentos — aqueles passados para bind() (exceto o primeiro) e aqueles passados quando a nova função retornada
por bind() é chamada posteriormente. Aqui está um exemplo de uso:

var twosay2 = [Link](two);


twosay2('Bonjour'); // "Bonjour, outro objeto"

No exemplo anterior, não passamos nenhum parâmetro para bind() além do objeto a ser vinculado. No próximo
exemplo, vamos passar um argumento a ser aplicado parcialmente:

var doissay3 = [Link](dois, 'Enchanté');


doisdiz3(); // "Enchanté, outro objeto"

Resumo
Existem muitas opções disponíveis quando se trata de herança em JavaScript. É benéfico estudar e compreender os
diferentes padrões, pois eles ajudam a aprimorar seu domínio da linguagem. Neste capítulo, você aprendeu sobre
vários padrões clássicos e modernos para abordar a herança.

Entretanto, herança provavelmente não é um problema que você enfrenta com frequência durante o desenvolvimento.
Isso se deve, em parte, ao fato de que esse problema provavelmente já está resolvido de alguma forma pela biblioteca
que você usa — e, em parte, porque é raro precisar estabelecer cadeias de herança longas e complexas em
JavaScript. Em linguagens estáticas fortemente tipadas, a herança pode ser a única maneira de reutilizar código. Em
JavaScript, você pode frequentemente ter maneiras muito mais simples e elegantes, incluindo pegar métodos
emprestados, vinculá-los, copiar propriedades e misturar propriedades de vários objetos.

Lembre-se de que a reutilização de código é o objetivo, e a herança é apenas uma das maneiras de atingir esse
objetivo.

Resumo | 139
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 7

Padrões de Design

Os padrões de design do livro Gang of Four oferecem soluções para problemas comuns relacionados ao
design de software orientado a objetos. Eles existem há bastante tempo e têm se mostrado úteis em diversas
situações. Por isso, é bom se familiarizar com eles e conversar sobre eles.

Embora esses padrões de design sejam independentes de linguagem e independentes de implementação,


eles têm sido estudados há muitos anos, principalmente da perspectiva de linguagens de classe estática
fortemente tipadas, como C++ e Java.

JavaScript, sendo uma linguagem baseada em protótipos dinâmicos e não tipados, às vezes torna
surpreendentemente fácil, até mesmo trivial, implementar alguns desses padrões.

Vamos começar com o primeiro exemplo de como as coisas são diferentes em JavaScript em comparação
com uma linguagem estática baseada em classes: o padrão singleton.

Solteiro
A ideia do padrão singleton é ter apenas uma instância de uma classe específica. Isso significa que, na
segunda vez que você usar a mesma classe para criar um novo objeto, deverá obter o mesmo objeto que foi
criado na primeira vez.

E como isso se aplica ao JavaScript? Em JavaScript não existem classes, apenas objetos.
Quando você cria um novo objeto, na verdade não há outro objeto igual a ele, e o novo objeto já é um
singleton. Criar um objeto simples usando o literal de objeto também é um exemplo de singleton:

var obj =
{ myprop: 'meu valor'
};

141
Machine Translated by Google [Link]

Em JavaScript, os objetos nunca são iguais, a menos que sejam o mesmo objeto, então mesmo se
você criar um objeto idêntico com exatamente os mesmos membros, ele não será igual ao primeiro
um:

var obj2 =
{ myprop: 'meu valor'
};
obj === obj2; // falso obj
== obj2; // falso

Então você pode dizer que toda vez que você cria um objeto usando o literal de objeto, você está
na verdade criando um singleton, e não há nenhuma sintaxe especial envolvida.

Observe que às vezes, quando as pessoas dizem “singleton” em um contexto JavaScript,


elas se referem ao padrão de módulo discutido no Capítulo 5.

Usar o novo

JavaScript não possui classes, então a definição literal de singleton não faz sentido tecnicamente.
Mas o JavaScript possui a sintaxe new para criar objetos usando funções construtoras, e às vezes
você pode querer uma implementação singleton usando essa sintaxe. A ideia é que, ao usar new
para criar vários objetos usando o mesmo construtor, você obtenha apenas ponteiros new para
exatamente o mesmo objeto.

Aviso de utilidade: A discussão a seguir não é tão útil como um padrão prático, mas
mais como um exercício teórico para imitar as soluções alternativas para problemas
relacionados aos designs de algumas linguagens baseadas em classes (estaticamente,
fortemente tipadas) nas quais as funções não são objetos de primeira classe.

O trecho a seguir mostra o comportamento esperado (supondo que você descarte a ideia do
Multiverso e aceite que existe apenas um Universo):
var uni = novo Universo();
var uni2 = novo Universo();
uni === uni2; // verdadeiro

Neste exemplo, uni é criado apenas na primeira vez que o construtor é chamado. Na segunda vez
(e na terceira, quarta e assim por diante), o mesmo objeto uni é retornado. É por isso que uni ===
uni2 — porque são essencialmente duas referências apontando exatamente para o mesmo objeto.
E como fazer isso em JavaScript?

Você precisa que o construtor Universe armazene em cache a instância do objeto this quando ele
for criado e a retorne na segunda vez que o construtor for chamado. Você tem várias opções para
fazer isso:

142 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

• Você pode usar uma variável global para armazenar a instância. Isso não é recomendado devido ao
princípio geral de que variáveis globais são ruins. Além disso, qualquer um pode sobrescrever essa
variável global, mesmo por acidente. Portanto, não vamos discutir mais essa opção. • Você

pode armazenar em cache uma propriedade estática do construtor. Funções em JavaScript são objetos,
portanto, podem ter propriedades. Você pode ter algo como [Link] e armazenar em cache
o objeto lá. Esta é uma solução boa e limpa, com a única desvantagem de que a propriedade da
instância é publicamente acessível, e o código fora do seu pode alterá-la, então você perde a
instância. • Você pode envolver a instância em um fechamento. Isso

mantém a instância privada e não disponível para modificações fora do seu construtor à custa de um
fechamento extra.

Vamos dar uma olhada em um exemplo de implementação da segunda e terceira opções.

Instância em uma propriedade estática

Aqui está um exemplo de armazenamento em cache da instância singular em uma propriedade estática do
Construtor do universo :

função Universo() {

// temos uma instância existente? if


(typeof [Link] === "object") { return
[Link];
}

// prossiga normalmente
this.start_time = 0;
[Link] = "Big";

// cache
[Link]ância = isto;

// retorno implícito: //
retornar isto;
}

// testando
var uni = new Universe(); var
uni2 = novo Universo(); uni
=== uni2; // verdadeiro

Como você pode ver, esta é uma solução simples, com a única desvantagem de que a instância é pública.
É improvável que outro código a altere por engano (muito menos provável do que se a instância fosse
global), mas ainda é possível.

Solteiro | 143
Machine Translated by Google [Link]

Instância em um fechamento

Outra maneira de criar um singleton semelhante a uma classe é usar um fechamento para
proteger a instância única. Você pode implementar isso usando o padrão de membro estático
privado discutido no Capítulo 5. O segredo aqui é reescrever o construtor:
função Universo() {

// a instância em cache
var instance = this;

// prossiga normalmente
this.start_time = 0;
[Link] = "Big";

// reescreva o construtor
Universo = função ()
{ return instância;
};
}

// testando
var uni = new Universe(); var
uni2 = novo Universo(); uni
=== uni2; // verdadeiro

O construtor original é chamado na primeira vez e retorna isso como de costume. Na segunda,
terceira vez e assim por diante, o construtor reescrito é executado. O construtor reescrito tem
acesso à variável de instância privada por meio do fechamento e simplesmente a retorna.

Esta implementação é, na verdade, outro exemplo do padrão de função autodefinida do


Capítulo 4. A desvantagem, como discutimos lá, é que a função reescrita (neste caso, o
construtor Universe()) perderá todas as propriedades adicionadas a ela entre o momento da
definição inicial e a redefinição. No nosso caso específico, qualquer coisa que você adicionar
ao protótipo de Universe() não terá um link ativo para a instância criada com a implementação
original.

Veja como você pode ver o problema com alguns testes:


// adicionando ao protótipo
[Link] = true;

var uni = novo Universo();

// adicionando novamente ao
protótipo // após o objeto inicial ser criado
[Link] = true;

var uni2 = novo Universo();

Teste: //
apenas o protótipo original foi //
vinculado aos objetos

144 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

[Link]; // verdadeiro
[Link]; // verdadeiro
[Link]; // indefinido
[Link]; // indefinido

// isso parece certo:


[Link]; // "Universo"

// mas isso é
estranho: [Link] === Universe; // false

O motivo pelo qual [Link] não é mais o mesmo que o construtor Universe() é porque
[Link] ainda aponta para o construtor original, não para o redefinido.

Se for necessário fazer com que o protótipo e o ponteiro do construtor funcionem conforme o
esperado, é possível conseguir isso com alguns ajustes:
função Universo() {

// a instância em cache
var instance;

// reescreva o construtor
Universo = função Universo() { return
instância;
};

// transferir as propriedades do protótipo


[Link] = this;

// a instância
instância = novo Universo();

// redefine a instância do ponteiro do


[Link] = Universe;

// todas as funcionalidades
instance.start_time = 0;
[Link] = "Big";

retornar instância;
}

Agora todos os casos de teste devem funcionar conforme o esperado:

// atualizar protótipo e criar instância


[Link] = true; // true var uni
= new Universe();
[Link] = true; // true var uni2
= new Universe();

// é a mesma instância única uni ===


uni2; // verdadeiro

// todas as propriedades do protótipo


funcionam // não importa quando foram definidas

Solteiro | 145
Machine Translated by Google [Link]

[Link] && [Link] && [Link] && [Link]; // true // as


propriedades normais funcionam
[Link]; // "Big" //
o construtor aponta corretamente
[Link] === Universe; // true

Uma solução alternativa também seria encapsular o construtor e a instância em uma função imediata.
Na primeira vez que o construtor é invocado, ele cria um objeto e também aponta a instância privada
para ele. A partir da segunda invocação, o construtor simplesmente retorna a variável privada. Todos
os testes do snippet anterior também funcionarão conforme o esperado com esta nova implementação:

var Universo;

(função () {

var instância;

Universo = função Universo() {

se (instância)
{ retornar instância;
}

instância = isto;

// todas as funcionalidades
this.start_time = 0;
[Link] = "Big";

};

}());

Fábrica: O

objetivo da fábrica é criar objetos. Geralmente, ela é implementada em uma classe ou em um método
estático de uma classe, que tem os seguintes propósitos:

• Executa operações repetitivas ao configurar objetos semelhantes • Oferece


uma maneira para os clientes da fábrica criarem objetos sem conhecer o tipo específico (classe)
no momento da compilação

O segundo ponto é mais importante em linguagens de classes estáticas, nas quais pode não ser
trivial criar instâncias de classes que não são conhecidas antecipadamente (em tempo de compilação).
Em JavaScript, esta parte da implementação é bem fácil.

Os objetos criados pelo método de fábrica (ou classe) são, por design, herdados do mesmo objeto
pai; eles são subclasses específicas que implementam funcionalidades especializadas.
Às vezes, o pai comum é a mesma classe que contém o método de fábrica.

146 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Vamos ver um exemplo de implementação onde temos:

• Um construtor pai comum do CarMaker .


• Um método estático do CarMaker chamado factory(), que cria objetos de
carro. • Construtores especializados [Link], [Link] e CarMaker.
Conversíveis que herdam do CarMaker. Todos eles serão definidos como propriedades
estáticas do pai para que possamos manter o namespace global limpo e também saber onde
encontrá-los quando precisarmos.

Vamos primeiro ver como a implementação finalizada será usada:


var corolla = [Link]('Compact'); var
solstice = [Link]('Convertible'); var
cherokee = [Link]('SUV');
[Link](); // "Vroom, tenho 4 portas"
[Link](); // "Vroom, tenho 2 portas"
[Link](); // "Vroom, tenho 17 portas"

Esta parte:
var corolla = [Link]('Compacto');

é provavelmente o mais reconhecível no padrão de fábrica. Você tem um método que aceita um
tipo fornecido como string em tempo de execução e, em seguida, cria e retorna objetos do tipo
solicitado. Não há construtores usados com new ou quaisquer literais de objeto à vista, apenas
uma função que cria objetos com base em um tipo identificado por uma string.

Aqui está um exemplo de implementação do padrão de fábrica que faria o código do snippet
anterior funcionar:
// função construtora pai
CarMaker() {}

// um método do pai
[Link] = function
" () { return
"Vroom, eu tenho + [Link] + "portas";
};

// o método de fábrica estático


[Link] = function (type) { var constr = type,
newcar;

// erro se o construtor não existir if (typeof


CarMaker[constr] !== "function") { throw { name:
"Error",
message: constr
+ "não existe"
};
}

// neste ponto, sabe-se que o construtor existe // vamos


fazer com que ele herde o pai, mas apenas uma vez if
(typeof CarMaker[constr].[Link] !== "function") {

Fábrica | 147
Machine Translated by Google [Link]

CarMaker[constr].prototype = novo CarMaker();

} // cria uma nova instância


newcar = new CarMaker[constr](); //
opcionalmente chama alguns métodos e então retorna...
return newcar;
};

// define fabricantes de automóveis


específicos [Link] =
function () { [Link] = 4;
};
[Link] = function ()
{ [Link] = 2;
};
[Link] = function ()
{ [Link] = 24;
};

Não há nada particularmente difícil na implementação do padrão de fábrica.


Tudo o que você precisa fazer é procurar a função construtora que cria um objeto do tipo necessário.
Neste caso, uma convenção de nomenclatura simples foi usada para mapear os tipos de objeto aos
construtores que os criam. A parte da herança foi apenas um exemplo de um trecho de código
repetitivo comum que poderia ser inserido no método de fábrica em vez de ser repetido para cada
tipo de construtor.

Fábrica de Objetos Integrada

E para um exemplo de "fábrica em ação", considere o construtor global Object() integrado. Ele
também se comporta como uma fábrica, pois cria objetos diferentes dependendo da entrada. Se
você passar um número primitivo, ele pode criar um objeto com o construtor Number() em segundo
plano. O mesmo vale para os valores string e booleanos. Quaisquer outros valores, incluindo valores
sem entrada, criarão um objeto normal.

Aqui estão alguns exemplos e testes do comportamento. Observe que Object pode ser chamado com
ou sem new:

var o = novo Objeto(), n


= novo Objeto(1), s =
Objeto('1'), b =
Objeto(verdadeiro);

// teste
[Link] === Objeto; // verdadeiro
[Link] === Número; // verdadeiro
[Link] === String; // verdadeiro
[Link] === Boolean; // verdadeiro

O fato de Object() também ser uma fábrica é de pouca utilidade prática, apenas algo que vale a pena
mencionar como um exemplo de que o padrão de fábrica está ao nosso redor.

148 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Iterador
No padrão iterador, você tem um objeto contendo algum tipo de dado agregado. Esses dados podem ser
armazenados internamente em uma estrutura complexa, e você deseja fornecer acesso fácil a cada
elemento dessa estrutura. O consumidor do seu objeto não precisa saber como você estrutura seus dados;
tudo o que ele precisa é trabalhar com os elementos individuais.

No padrão iterador, seu objeto precisa fornecer um método next() . Chamar next() em sequência deve
retornar o próximo elemento consecutivo, cabendo a você decidir o que "next" significa na sua estrutura
de dados específica.

Supondo que seu objeto seja chamado agg, você pode acessar cada elemento de dados simplesmente
chamando next() em um loop como este:

var element;
while (element = [Link]()) { // faça
algo com o elemento ... [Link](element);

No padrão iterador, o objeto agregado geralmente também fornece um método prático chamado hasNext() ,
para que os usuários do objeto possam determinar se chegaram ao fim dos seus dados. Outra maneira de
acessar todos os elementos sequencialmente, desta vez usando hasNext(), seria algo como o seguinte:

while ([Link]()) { //
faça algo com o próximo elemento...
[Link]([Link]());
}

Agora que temos os casos de uso, vamos ver como implementar esse objeto agregado.

Ao implementar o padrão iterador, faz sentido armazenar privadamente os dados e um ponteiro (índice)
para o próximo elemento disponível. Para demonstrar um exemplo de implementação, vamos supor que
os dados sejam apenas um array comum e que a lógica "especial" para recuperar o próximo elemento
consecutivo seja retornar um elemento sim, outro não do array:

var agg = (função () {

var índice = 0,
dados = [1, 2, 3, 4, 5],
comprimento = [Link];

retornar {

próximo: função ()
{ var elemento;
se (![Link]())
{ retornar nulo;

} elemento = dados[índice];
índice = índice + 2;
retornar elemento;

Iterador | 149
Machine Translated by Google [Link]

},

hasNext: função ()
{ retornar índice < comprimento;
}

}; }());

Para fornecer acesso mais fácil e capacidade de iterar várias vezes sobre os dados, seu objeto pode
fornecer métodos de conveniência adicionais: rewind()

Para redefinir o ponteiro de volta ao início current()

Para retornar o elemento atual, porque você não pode fazer isso com next() sem avançar o ponteiro

A implementação desses métodos não apresentará nenhuma dificuldade:

var agg = (função () {

// [recortar...]

retornar {

// [recortar...]

rebobinar: função ()
{ índice = 0;
},
atual: função () { retornar
dados[índice];
}

}; }());

Agora testando o iterador: //


este loop registra 1, depois 3, depois 5
while ([Link]())
{ [Link]([Link]());
}

// voltar
[Link]();
[Link]([Link]()); // 1

O resultado será registrado no console: 1, 3, 5 (do loop) e finalmente 1 (após o retrocesso).

150 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Decorador
No padrão decorador, funcionalidades adicionais podem ser adicionadas a um objeto dinamicamente, em
tempo de execução. Ao lidar com classes estáticas, isso pode ser um desafio. Em JavaScript, os objetos são
mutáveis, portanto, o processo de adicionar funcionalidade a um objeto não é um processo complexo.
problema em si.

Um recurso conveniente do padrão decorador é a personalização e configuração


do comportamento esperado. Você começa com seu objeto simples, que possui alguma funcionalidade
básica. Em seguida, você escolhe entre um conjunto de decoradores disponíveis quais
você deseja usar para aprimorar seu objeto simples e em que ordem, se a ordem for
importante.

Uso
Vamos dar uma olhada em um exemplo de uso do padrão. Digamos que você esteja trabalhando em um site
aplicativo que vende algo. Cada nova venda é um novo objeto de venda . A venda “sabe”
sobre o preço do item e pode retorná-lo chamando o método [Link]() .
Dependendo das circunstâncias, você pode começar a decorar este objeto com funcionalidade extra. Imagine
um cenário em que a venda para um cliente seja na província canadense
de Québec. Neste caso, o comprador precisa pagar um imposto federal e um imposto provincial de Québec
imposto. Seguindo o padrão do decorador, você dirá que “decora” o objeto com um
decorador de impostos federais e um decorador de impostos de Quebec. Você também pode decorar o objeto
com funcionalidade de formatação de preços. Este cenário poderia ser o seguinte:

var sale = new Sale(100); // o preço é 100 dólares


venda = [Link]('fedtax'); // adiciona imposto federal
venda = [Link]('quebec'); // adicionar imposto provincial
venda = [Link]('dinheiro'); // formato como dinheiro
[Link]ço(); // "$ 112,88"

Em outro cenário, o comprador poderia estar em uma província que não tem imposto provincial,
e você também pode querer formatar o preço usando dólares canadenses, então você pode fazer:

var sale = new Sale(100); // o preço é 100 dólares


venda = [Link]('fedtax'); // adiciona imposto federal
venda = [Link]('cdn'); // formato usando CDN
[Link](); // "CDN$ 105,00"

Como você pode ver, esta é uma maneira flexível de adicionar funcionalidades e ajustar um objeto em tempo de execução.
Vamos ver como abordar uma implementação do padrão.

Implementação
Uma maneira de implementar o padrão decorador é fazer com que cada decorador seja um objeto
contendo os métodos que devem ser sobrescritos. Cada decorador na verdade herda
o objeto aprimorado até agora após o decorador anterior. Cada método decorado chama

Decorador | 151
Machine Translated by Google [Link]

o mesmo método no uber (o objeto herdado) e obtém o valor e prossegue fazendo algo adicional.

O efeito final é que, ao executar [Link]() no primeiro exemplo de uso, você está chamando o
método do decorador money (veja a Figura 7-1). Mas, como cada método decorado chama primeiro o
método do pai, o método getPrice() de money chama primeiro o método getPrice() de quebec , que por
sua vez chama o método getPrice() de fedtax , e assim por diante. A cadeia vai até o método getPrice()
original, não decorado, implementado pelo construtor Sale() .

Figura 7-1. Implementação do padrão decorador

A implementação começa com um construtor e um método de protótipo:

função Venda(preço)
{ [Link]ço = preço || 100;
}
[Link]ó[Link]ço = função ()
{ retornar [Link]ço;
};

Todos os objetos decoradores serão implementados como propriedades de uma propriedade construtora:
[Link] = {};

Vejamos um exemplo de decorador. É um objeto que implementa o método personalizado get Price() . Observe que o
método primeiro obtém o valor do método pai e, em seguida, modifica esse valor:

152 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

[Link] =
{ getPrice: function ()
{ var price = [Link]();
*
preço += preço 5 / 100;
retornar preço;
}
};

Da mesma forma, podemos implementar outros decoradores, quantos forem necessários. Eles podem ser
extensões da funcionalidade principal do Sale() , implementados como plugins. Eles podem até mesmo "residir"
em arquivos adicionais e ser desenvolvidos e compartilhados por desenvolvedores terceirizados:

[Link] =
{ getPrice: function ()
{ var price = [Link]();
* 7,5 / 100;
preço += preço
return preço;
}
};

[Link] = {
obterPreço: função ()
{ retornar "$" + [Link]().toFixed(2);
}
};

[Link] =
{ obterPreço: função ()
{ retornar "CDN$ " + [Link]().toFixed(2);
}
};

Por fim, vamos ver o método “mágico” chamado decorate() que une todas as peças.
Lembre-se que será chamado assim:

venda = [Link]('imposto federal');

A string "fedtax" corresponderá a um objeto implementado em [Link]. O objeto recém-decorado


newobj herdará o objeto que temos até o momento (o original ou aquele após a adição do último decorador),
que é o objeto this. Para realizar a parte da herança, vamos usar o padrão de construtor temporário do capítulo
anterior. Também definimos a propriedade uber de newobj para que os filhos tenham acesso ao pai. Em
seguida, copiamos todas as propriedades extras do decorador para o objeto recém-decorado newobj. No
final, newobj é retornado e, em nosso exemplo de uso concreto, ele se torna o novo objeto de venda atualizado :

[Link] = função (decorador) {


var F = function () {},
substituições = [Link][decorador], i,
newobj;
[Link] = this;
newobj = new F();
[Link] = [Link];
para (i em substituições) {

Decorador | 153
Machine Translated by Google [Link]

se ([Link](i))
{ newobj[i] = substitui[i];
}

} retornar novoobjeto;
};

Implementação usando uma


lista: Vamos explorar uma implementação ligeiramente diferente, que se beneficia da
natureza dinâmica do JavaScript e não precisa usar herança. Além disso, em vez de fazer
com que cada método decorado chame o método anterior na cadeia, podemos simplesmente
passar o resultado do método anterior como parâmetro para o próximo método.

Essa implementação também pode permitir desfazer ou desfazer facilmente uma decoração, o que
significa simplesmente remover um item da lista de decoradores.

O exemplo de uso será um pouco mais simples porque não atribuímos o valor de retorno de decorate()
ao objeto. Nesta implementação, decorate() não faz nada ao objeto, apenas o anexa a uma lista:

var sale = new Sale(100); // o preço é 100 dólares


[Link]('fedtax'); // adiciona imposto federal
[Link]('quebec'); // adiciona imposto provincial
[Link]('money'); // formata como dinheiro
[Link](); // "$112,88"

O construtor Sale() agora tem uma lista de decoradores como uma propriedade própria:
função Venda(preço)
{ [Link] = (preço > 0) || 100;
this.decorators_list = [];
}

Os decoradores disponíveis são novamente implementados como propriedades de [Link].


Observe que os métodos getPrice() agora são mais simples, pois não chamam o método pai getPrice()
para obter o resultado intermediário; este resultado é passado a eles como um parâmetro:

[Link] = {};

[Link] =
{ getPrice: function (preço) { 5 /
*
retornar preço + preço 100;
}
};

[Link] =
{ getPrice: function (preço) { 7,5 /
*
retornar preço + preço 100;
}
};

[Link] = {

154 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

getPrice: função (preço) {


retornar "$" + preç[Link](2);
}
};

A parte interessante acontece nos métodos decorate() e getPrice() do método pai . Na


implementação anterior, decorate() era um tanto complexo e getPrice() era bem simples. Nesta
implementação, ocorre o inverso: decorate() apenas adiciona elementos a uma lista, enquanto
getPrice() faz todo o trabalho. O trabalho inclui percorrer a lista de decoradores adicionados
atualmente e chamar cada um dos métodos getPrice() , passando o resultado do método anterior:

[Link] = função (decorador)


{ this.decorators_list.push(decorador);
};

[Link] = function () { var


preço = [Link], i, max =

this.decorators_list.length,
nome;
para (i = 0; i < máx; i += 1) {
nome = this.decorators_list[i]; preço
= [Link][nome].getPrice(preço);

} preço de retorno;
};

Esta segunda implementação do padrão decorador é mais simples e não envolve herança. Os
métodos de decoração também são mais simples. Todo o trabalho é feito pelo método que
"concorda" em ser decorado. Nesta implementação de exemplo, getPrice() é o único método que
permite decoração. Se você quiser ter mais métodos que podem ser decorados, a parte de
percorrer a lista de decoradores deve ser repetida por cada método adicional. No entanto, isso
pode ser facilmente abstraído em um método auxiliar que recebe um método e o torna "decorável".
Em tal implementação, a propriedade deco rators_list se tornaria um objeto com propriedades
nomeadas após os métodos e valores sendo arrays de objetos decoradores.

Estratégia
O padrão de estratégia permite selecionar algoritmos em tempo de execução. Os clientes do seu
código podem trabalhar com a mesma interface, mas escolher entre vários algoritmos disponíveis
para lidar com suas tarefas específicas, dependendo do contexto do que estão tentando fazer.

Um exemplo de uso do padrão strategy seria resolver o problema de validação de formulário.


Você pode criar um objeto validador com um método validate() . Este é o método que será
chamado independentemente do tipo concreto de formulário e sempre retornará o mesmo
resultado — uma lista de dados que não foram validados e quaisquer mensagens de erro.

Estratégia | 155
Machine Translated by Google [Link]

Mas, dependendo da forma concreta e dos dados a serem validados, os clientes do seu validador
podem escolher diferentes tipos de verificação. O validador escolhe a melhor estratégia para lidar com
a tarefa e delega as verificações de dados concretos ao algoritmo apropriado.

Exemplo de validação de dados

Digamos que você tenha os seguintes dados, provavelmente vindos de um formulário em uma
página, e queira verificar se eles são válidos:
var data =
{ first_name: "Super",
last_name: "Man",
age: "unknown",
username: "o_O"
};

Para que o validador saiba qual é a melhor estratégia a ser usada neste exemplo concreto, você
precisa primeiro configurar o validador e definir as regras do que você considera válido e aceitável.

Digamos que você não exija sobrenome e aceite qualquer nome, mas exija que a idade seja um
número e que o nome de usuário contenha apenas letras e números, sem símbolos especiais. A
configuração será algo como:
[Link] =
{ first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};

Agora que o objeto validador está configurado para manipular seus dados, chame seu método
validate() e imprima quaisquer erros de validação no console:
[Link](dados); se
([Link]())
{ [Link]([Link]("\n"));
}

Isso pode imprimir as seguintes mensagens de erro:


Valor inválido para *idade*, o valor só pode ser um número válido, por exemplo, 1, 3,14 ou 2010
Valor inválido para *nome de usuário*, o valor só pode conter caracteres e números, sem símbolos
especiais

Agora, vamos ver como o validador é implementado. Os algoritmos disponíveis para as verificações
são objetos com uma interface predefinida — eles fornecem um método validate() e informações de
ajuda de uma linha para serem usadas em mensagens de erro:
// verifica se há valores não vazios
[Link] = {
validar: função (valor) { valor de
retorno !== "";
},
instruções: "o valor não pode estar vazio"

156 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

};

// verifica se um valor é um número


[Link] = {
validar: função (valor) { return !
isNaN(valor);
},
instruções: "o valor só pode ser um número válido, por exemplo, 1, 3,14 ou 2010"
};

// verifica se o valor contém apenas letras e números


[Link] = {
validar: função (valor) { return !/
[^a-z0-9]/[Link](valor);
},
instruções: "o valor só pode conter caracteres e números, sem símbolos especiais"
};
E finalmente o núcleo do objeto validador :

var validador = {

// todos os tipos de verificação


disponíveis: {},

// mensagens de erro na sessão de


validação atual
mensagens: [],

// configuração de validação atual //


nome: configuração do tipo de
validação: {},

// o método de interface //
`data` é chave => pares de valores
validar: função (dados) {

var i, msg, tipo, verificador, resultado_ok;

// redefine todas as
mensagens [Link] = [];

para (i em dados) {

se ([Link](i)) {

tipo = [Link][i];
verificador = [Link][tipo];

if (!type)
{ continue; // não há necessidade de validar

} if (!checker) { // uh-oh throw


{ name:
"ValidationError", message:
"
"Nenhum manipulador para validar o tipo + tipo

Estratégia | 157
Machine Translated by Google [Link]

};
}

result_ok = [Link](data[i]); if (!
result_ok) { msg =
"Valor inválido para *" + i + "*, " + [Link];
[Link](msg);
}
}

} retornar [Link]();
},

// auxiliar
hasErrors: função () { return
[Link] !== 0;
}
};

Como você pode ver, o objeto validador é genérico e pode ser mantido assim para todos os casos
de uso de validação. A maneira de melhorá-lo seria adicionar mais tipos de verificações. Se você
usá-lo em várias páginas, em breve terá uma boa coleção de verificações específicas. Então, tudo
o que você precisa fazer para cada novo caso de uso é configurar o validador e executar o método
validate() .

Fachada
A fachada é um padrão simples; ela fornece apenas uma interface alternativa para um objeto. É
uma boa prática de design manter seus métodos curtos e não deixá-los lidar com muito trabalho.
Seguindo essa prática, você terá um número maior de métodos do que se tivesse métodos
superdimensionados com muitos parâmetros. Às vezes, dois ou mais métodos podem ser chamados
juntos. Nesses casos, faz sentido criar outro método que envolva as chamadas de método repetidas.

Por exemplo, ao manipular eventos do navegador, você tem os seguintes métodos:

pararPropagação()
Captura o evento e não permite que ele chegue aos nós pais
preventDefault()
Não permite que o navegador execute a ação padrão (por exemplo, seguir um link ou enviar
um formulário)

Esses são dois métodos distintos com propósitos distintos e devem ser mantidos separados, mas,
ao mesmo tempo, costumam ser chamados juntos. Portanto, em vez de duplicar as duas chamadas
de método em toda a aplicação, você pode criar um método de fachada que chame ambos:

var myevent =
{ // ...
parar: função (e) {

158 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

[Link]();
[Link]();

} // ...
};

O padrão de fachada também é adequado para scripts de navegador, onde as diferenças entre os
navegadores podem ser ocultadas por trás de uma fachada. Continuando com o exemplo anterior,
você pode adicionar o código que lida com as diferenças na API de eventos do IE:
var myevent =
{ // ...
stop: function (e) { //
outros if
(typeof [Link] === "function")
{ [Link]();

} se (tipo de [Link] === "função")


{ [Link]();
}
// IE
if (typeof [Link] === "boolean")
{ [Link] = false;

} se (tipo de e. cancelBubble === "booleano") { e.


cancelBubble = verdadeiro;
}

} // ...
};

O padrão de fachada também é útil em esforços de redesign e refatoração. Quando você deseja
substituir um objeto por uma implementação diferente, é preciso fazê-lo ao longo de um período (é
um objeto complexo), enquanto, ao mesmo tempo, um novo código é escrito utilizando esse objeto.
Você pode começar pensando na API do novo objeto e, em seguida, criar uma fachada na frente do
objeto antigo que segue a nova API. Dessa forma, quando você chegar à substituição completa do
objeto antigo, terá menos código cliente para modificar, pois qualquer código cliente recente já usará
a nova API.

Procuração

No padrão de projeto proxy, um objeto atua como uma interface para outro objeto. É diferente do
padrão fachada, onde tudo o que você tem são métodos de conveniência que combinam várias
outras chamadas de método. O proxy fica entre o cliente de um objeto e o próprio objeto, protegendo
o acesso a esse objeto.

Este padrão pode parecer um overhead, mas é útil para fins de desempenho. O proxy atua como
guardião do objeto (também chamado de "sujeito real") e tenta fazer com que o sujeito real trabalhe
o mínimo possível.

Procuração | 159
Machine Translated by Google [Link]

Um exemplo de uso seria algo que podemos chamar de inicialização preguiçosa. Imagine que inicializar
o assunto real seja caro e que o cliente o inicialize, mas nunca o utilize. Nesse caso, o proxy pode ajudar
sendo a interface para o assunto real. O proxy recebe a solicitação de inicialização, mas nunca a repassa
até que fique claro que o assunto real está sendo realmente utilizado.

A Figura 7-2 ilustra o cenário em que o cliente faz uma solicitação de inicialização e o proxy responde
que está tudo bem, mas não repassa a mensagem até que fique óbvio que o cliente precisa de algum
trabalho do sujeito. Só então o proxy repassa as duas mensagens juntas.

Figura 7-2. Relação entre cliente e sujeito real ao passar por uma procuração

Um Exemplo:

O padrão proxy é útil quando o sujeito real realiza algo custoso. Em aplicações web, uma das operações
mais custosas que você pode realizar é uma solicitação de rede, portanto, faz sentido combinar
solicitações HTTP o máximo possível. Vejamos um exemplo que faz exatamente isso e demonstra o
padrão proxy em ação.

Uma expansão

de vídeo. Vamos usar um pequeno aplicativo que reproduz vídeos de um artista selecionado (Figura
7-3). Você pode experimentar o exemplo ao vivo e observar o código em http:// [Link]/ book/
7/ [Link] .

Você tem uma lista de títulos de vídeos em uma página. Quando o usuário clica no título de um vídeo, a
área abaixo do título se expande para mostrar mais informações sobre o vídeo e também permite que ele
seja reproduzido. As informações detalhadas do vídeo e a URL do vídeo não fazem parte da página; elas
precisam ser recuperadas por meio de uma chamada de serviço web. O serviço web pode aceitar vários
IDs de vídeo, portanto, podemos acelerar a aplicação fazendo menos solicitações HTTP sempre que
possível e recuperando dados de vários vídeos simultaneamente.

Nosso aplicativo permite que vários (ou todos) vídeos sejam expandidos ao mesmo tempo, então esta é
uma oportunidade perfeita para combinar solicitações de serviços web.

160 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Figura 7-3. Vídeo expando em ação

Sem um proxy
Os principais “atores” da aplicação são os dois objetos:
vídeos

Responsável pelas áreas de informação expandir/recolher (método [Link]()) e reproduzir vídeos


(método [Link]()) http Responsável

pela
comunicação com o servidor através do método [Link]()

Quando não há proxy, [Link]() chamará [Link]() uma vez para cada
vídeo. Quando adicionamos um proxy, ele se tornará um novo ator chamado proxy e
ficará entre vídeos e http , delegando as chamadas para makeRequest(), combinando-as
sempre que possível.

Procuração | 161
Machine Translated by Google [Link]

Vamos examinar primeiro o código sem o proxy e depois adicionar o proxy para melhorar a capacidade
de resposta do aplicativo.

HTML

O código HTML é apenas uma lista de links:


<p><span id="toggle-all">Alternar marcado</p> <ol id="vids"> <li><input
type="checkbox"
marcado><a
href="[Link]
<li><input type="checkbox" marcada><a
href="[Link] <li><input
type="checkbox" marcada><a href="http://
[Link]/videos/--45286339">Crush</a></li> <li><input
type="checkbox" marcada><a
href="[Link] beba a água</a></li>
<li><input type="checkbox" marcada><a
href="[Link] do jeito que é</a></li>
<li><input type="checkbox" marcada><a
href="[Link] que você diria</a></li>
</ol>

Manipuladores de eventos

Agora, vamos dar uma olhada nos manipuladores de eventos. Primeiro, definimos a função $ , que é uma abreviação
conveniente:

var $ = função (id) { return


[Link](id);
};

Usando a delegação de eventos (mais sobre esse padrão no Capítulo 8), vamos lidar com todos os
cliques que ocorrem na lista ordenada id="vids" com uma única função:
$('vids').onclick = função (e) { var src, id;

e = e || [Link]; src
= [Link] || [Link];

se ([Link] !== "A")


{ retornar;
}

se (tipo de [Link] === "função")


{ [Link]();

} [Link] = falso;

id = [Link]('--')[1];

se ([Link] ===
"reproduzir") {[Link] =
ví[Link](id); retornar;

162 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

[Link] = "v" + id;


ví[Link](id);
};

No manipulador de cliques genérico, estamos interessados em dois cliques: um para expandir/recolher a seção
de informações (chamando getInfo()) e outro para reproduzir o vídeo (quando o alvo tem o nome de classe
"play"), o que significa que a seção de informações já foi expandida, e podemos então chamar getPlayer(). Os
IDs dos vídeos são extraídos dos links hrefs.

O outro manipulador de cliques reage aos cliques para alternar todas as seções de informações. Basicamente, é
só chamar getInfo() novamente, mas em um loop:

$('toggle-all').onclick = função (e) {

var hrefs, i,

max,
id;

hrefs = $('vids').getElementsByTagName('a'); for (i =


0, max = [Link]; i < max; i += 1) { // pular links de
reprodução if
(hrefs[i].className === "play") { continue;

} // pular desmarcado
if (!hrefs[i].[Link]) { continue;

id = hrefs[i].[Link]('--')[1];
hrefs[i].[Link] = "v" + id;
ví[Link](id);
}
};

objeto de vídeos

O objeto vídeos tem três métodos:

obterJogador()
Retorna o HTML necessário para reproduzir o vídeo em Flash (não relevante para a discussão).

updateList()
O retorno de chamada que recebe todos os dados do serviço web e produz o código HTML a ser usado na
seção de informações expandidas. Também não há nada particularmente interessante acontecendo neste
método. getInfo()

O método que alterna a visibilidade das seções de informações e também faz chamadas para http passando
updateList() como um retorno de chamada.

Procuração | 163
Machine Translated by Google [Link]

Aqui está um trecho do objeto: var


videos = {

getPlayer: função (id) {...}, updateList:


função (dados) {...},

getInfo: função (id) {

var informação = $('info' + id);

se (!info)
{ [Link]([id], "[Link]"); retornar;

se ([Link] === "nenhum")


{ [Link] = ''; }
senão
{ [Link] = 'nenhum';
}

}
};

Objeto

http O objeto http tem apenas um método, que faz a solicitação JSONP ao serviço web YQL do
Yahoo!:
var http =
{ makeRequest: função (ids, retorno de
chamada) { var url = '[Link]
sql = 'select * from [Link] onde ids IN ("%ID%")', format =
"format=json", handler =
"callback=" + callback, script =
[Link]('script');

sql = [Link]('%ID%', [Link]('","')); sql =


encodeURIComponent(sql);

url += sql + '&' + formato + '&' + manipulador;


[Link] = url;

[Link](script);
}
};

YQL (Yahoo! Query Language) é um meta serviço web que oferece a capacidade de usar
sintaxe semelhante a SQL para consumir vários outros serviços web sem precisar
estudar a API de cada serviço.

164 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Quando todos os seis vídeos forem alternados, seis solicitações individuais serão enviadas ao serviço
web, com consultas YQL semelhantes a:
selecione * de [Link] onde ids IN ("2158073")

Insira o proxy

O código descrito anteriormente funciona bem, mas podemos fazer melhor. O objeto proxy entra em
cena e assume a comunicação entre http e vídeos. Ele tenta combinar as solicitações usando uma
lógica simples: um buffer de 50 ms. O objeto de vídeos não chama o serviço HTTP diretamente, mas
chama o proxy. O proxy então espera antes de encaminhar a solicitação. Se outras chamadas de vídeos
vierem no período de espera de 50 ms, elas serão mescladas em uma. Um atraso de 50 ms é
praticamente imperceptível para o usuário, mas pode ajudar a combinar solicitações e acelerar a
experiência ao clicar em "alternar" e expandir mais de um vídeo de uma vez. Também reduz
significativamente a carga do servidor, pois o servidor web precisa lidar com um número menor de
solicitações.

A consulta YQL combinada para dois vídeos será como:


selecione * de [Link] onde os ids IN ("2158073", "123456")

Na versão modificada do código, a única mudança é que [Link]() agora chama


[Link]() em vez de [Link](), como mostrado aqui:
[Link](id, [Link], videos);

O proxy configura uma fila para coletar os IDs dos vídeos recebidos nos últimos 50 ms e, em seguida,
limpa a fila chamando http e fornece sua própria função de retorno de chamada, porque o retorno de
chamada [Link]() pode manipular apenas um único registro de dados.

Aqui está o código para o proxy: var


proxy = { ids:
[], delay:
50, timeout:
null, callback:
null, context: null,
makeRequest:
function (id, callback, context) {

// adicionar à fila
[Link](id);

[Link] = retorno de
chamada; [Link] = contexto;

// configurar tempo
limite if (![Link])
{ [Link] = setTimeout(function ()
{ [Link](); },
[Link]);

Procuração | 165
Machine Translated by Google [Link]

}
},
flush: função () {

[Link]([Link], "[Link]");

// limpar tempo limite e enfileirar


[Link] = null;
[Link] = [];

},
manipulador: função (dados)
{ var i, max;

// vídeo único if
(parseInt([Link], 10) === 1) {
[Link]([Link], [Link].Vídeo);
retornar;
}

// vários vídeos para


(i = 0, max = [Link]; i < max; i += 1)
{ [Link]([Link], [Link][i]);
}
}
};

A introdução do proxy proporciona a capacidade de combinar várias solicitações de serviço web


em uma só, com apenas uma simples alteração no código original.

As Figuras 7-4 e 7-5 ilustram os cenários de três viagens de ida e volta ao servidor (sem um proxy)
versus uma viagem de ida e volta ao usar um proxy.

Figura 7-4. Três viagens de ida e volta ao servidor

166 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Figura 7-5. Usando um proxy para combinar e reduzir o número de viagens de ida e volta ao servidor

Proxy como Cache:

Neste exemplo, o objeto cliente (vídeos) foi inteligente o suficiente para não solicitar as informações
do mesmo vídeo novamente. Mas isso pode não ser sempre o caso. O proxy pode ir além na
proteção do objeto HTTP real , armazenando em cache os resultados de solicitações anteriores em
uma nova propriedade de cache (veja a Figura 7-6). Assim, se o objeto de vídeos solicitar informações
sobre o mesmo ID de vídeo pela segunda vez, o proxy pode extraí-las do cache e economizar o
tempo de ida e volta da rede.

Figura 7-6. O cache do proxy

Mediador
Aplicações — grandes e pequenas — são compostas por objetos separados. Todos esses objetos
precisam de uma maneira de se comunicar entre si de uma maneira que não prejudique a manutenção
e sua capacidade de alterar com segurança uma parte da aplicação sem interromper o restante. À
medida que a aplicação cresce, você adiciona mais e mais objetos. Então, durante a refatoração, os
objetos são removidos e reorganizados. Quando os objetos sabem muito uns sobre os outros e se
comunicam diretamente (chamam os métodos uns dos outros e alteram propriedades), isso leva a
um acoplamento rígido indesejável. Quando os objetos estão intimamente acoplados, não é fácil alterá-los.

Mediador | 167
Machine Translated by Google [Link]

um objeto sem afetar muitos outros. Assim, mesmo a mais simples alteração em um aplicativo
deixa de ser trivial, e é praticamente impossível estimar o tempo que uma alteração pode levar.

O padrão mediador ameniza essa situação, promovendo acoplamento fraco e ajudando a melhorar
a manutenibilidade (ver Figura 7-7). Nesse padrão, os objetos independentes (colegas) não se
comunicam diretamente, mas por meio de um objeto mediador . Quando um dos colegas muda de
estado, ele notifica o mediador, que comunica a mudança a quaisquer outros colegas que devam
saber.

Figura 7-7. Participantes no padrão mediador

Exemplo de Mediador:

Vamos explorar um exemplo de uso do padrão mediador. A aplicação será um jogo em que dois
jogadores têm meio minuto para competir para ver quem pressiona um botão mais vezes do que o
outro. O jogador 1 compete pressionando 1 e o jogador 2 pressiona 0 (para que fiquem mais
confortáveis e não precisem brigar pelo teclado). Um placar é atualizado com a pontuação atual.

Os objetos participantes serão:

• Jogador 1
• Jogador 2
• Placar
• Mediador

168 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

O mediador tem conhecimento de todos os outros objetos. Ele se comunica com o dispositivo de
entrada (teclado), processa eventos de pressionamento de tecla, determina qual jogador tem a vez
e o notifica (veja a Figura 7-8). O jogador joga (ou seja, apenas atualiza sua própria pontuação
com um ponto) e notifica o mediador de que terminou. O mediador comunica a pontuação
atualizada ao placar, que por sua vez atualiza a tela.

Além do mediador, nenhum dos outros objetos sabe nada sobre qualquer outro objeto. Isso torna
trivial atualizar o jogo, por exemplo, adicionando um novo jogador ou outra tela mostrando o tempo
restante.

Você pode ver a versão ao vivo do jogo e dar uma olhada no código-fonte em http:// [Link]/
book/ 7/ [Link] .

Figura 7-8. Participantes do jogo de pressionar teclas

Os objetos do jogador são criados com um construtor Player() e possuem suas próprias
propriedades, pontos e nome. O método play() do protótipo incrementa os pontos em um e então
notifica o mediador:

função Jogador(nome)
{ [Link] = 0;
[Link] = nome;
}
[Link]ó[Link] = função ()
{ [Link] += 1;
[Link]();
};

Mediador | 169
Machine Translated by Google [Link]

O objeto placar possui um método update() , que é chamado pelo objeto mediador após
a vez de cada jogador. O placar não sabe sobre nenhum jogador e não registra a
pontuação; ele exibe apenas a pontuação dada pelo mediador:
var placar = {

// Elemento HTML a ser atualizado


elemento: [Link]('results'),

// atualiza a exibição da
pontuação update: function (score) {

var i, msg = '';


para (i em pontuação)
{ se (pontuaçã[Link](i))
{ msg += '<p><strong>' + i + '<\/strong>: '; msg
+= pontuação[i];
msg += '<\/p>';
}

} [Link] = msg;
}
};

Agora, vamos dar uma olhada no objeto mediador . Ele inicializa o jogo, criando objetos
de jogador em seu método setup() e monitora os jogadores em sua propriedade players .
O método played() é chamado por cada jogador após cada jogada. Este método atualiza
um hash de pontuação e o envia ao placar para exibição. O último método, keypress(),
manipula os eventos do teclado, determina qual jogador tem sua vez e o notifica:
var mediador = {

// todos os jogadores
jogadores: {},

// configuração de
inicialização: function
() { var players = [Link];
[Link] = new Player('Home');
[Link] = new Player('Guest');

},

// alguém joga, atualiza a pontuação


tocada: function () { var
players = [Link], score =
{ Home:
[Link], Guest:
[Link]
};

[Link](pontuação);
},

170 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

// manipular interações do usuário


keypress: function (e) { e = e
|| [Link]; // IE if ([Link]
=== 49) { // tecla "1"
[Link]();
return;

} if ([Link] === 48) { // chave "0"


[Link]();
retornar;
}
}
};

E a última coisa é montar e desmontar o jogo:


// vá!
[Link]();
[Link] = [Link];

// jogo termina em 30 segundos


setTimeout(function ()
{ [Link] = null;
alert('Game over!'); },
30000);

Observador

O padrão Observer é amplamente utilizado na programação JavaScript do lado do cliente. Todos os


eventos do navegador (passar o mouse, pressionar uma tecla, etc.) são exemplos do padrão. Outro
nome para ele é eventos personalizados, ou seja, eventos que você cria programaticamente, em
oposição aos que o navegador dispara. Outro nome é padrão Assinante/ Publicador .

A principal motivação por trás desse padrão é promover o acoplamento flexível. Em vez de um
objeto chamar o método de outro objeto, um objeto se inscreve na atividade específica de outro
objeto e é notificado. O assinante também é chamado de observador, enquanto o objeto observado
é chamado de publicador ou sujeito. O publicador notifica (chama) todos os assinantes quando um
evento importante ocorre e pode frequentemente passar uma mensagem na forma de um objeto de
evento.

Exemplo nº 1: Assinaturas de Revistas. Para

entender como implementar esse padrão, vamos usar um exemplo concreto. Digamos que você
tenha uma editora que publica um jornal diário e uma revista mensal. O assinante Joe será
notificado sempre que isso acontecer.

O objeto de papel precisa ter uma propriedade subscribers , que é um array que armazena todos
os assinantes. O ato de assinar é simplesmente adicionar algo a esse array. Quando um evento
ocorre, o papel percorre a lista de assinantes e os notifica. A notificação significa

Observador | 171
Machine Translated by Google [Link]

chamando um método do objeto assinante. Portanto, ao assinar, o assinante fornece um de seus


métodos ao método subscribe() do paper .

O artigo também pode fornecer unsubscribe(), o que significa remover assinantes do array. O último
método importante do artigo é publish(), que chamará os métodos dos assinantes. Resumindo,
um objeto publicador precisa ter estes membros:

assinantes

Uma matriz

subscribe()
Adicionar ao conjunto de assinantes
unsubscribe()
Remover do array de assinantes publish()

Faça um loop pelos assinantes e chame os métodos que eles forneceram quando se

inscreveram. Todos os três métodos precisam de um parâmetro de tipo , porque um editor pode
disparar vários eventos (publicar uma revista e um jornal) e os assinantes podem escolher assinar
um, mas não o outro.

Como esses membros são genéricos para qualquer objeto publicador, faz sentido implementá-los
como parte de um objeto separado. Assim, podemos copiá-los (padrão mix-in) para qualquer objeto
e transformá-lo em um publicador.

Aqui está um exemplo de implementação da funcionalidade genérica do publicador, que define


todos os membros necessários listados anteriormente, além de um método auxiliar visitSubscribers() :
var publisher =
{ assinantes:
{ any: [] // tipo de evento: assinantes
},
subscribe: função (fn, tipo) { tipo =
tipo || 'qualquer'; se (tipo
[Link][tipo] === "indefinido") { [Link][tipo]
= [];

} [Link][tipo].push(fn);
},
cancelar inscrição: função (fn, tipo)
{ [Link]('cancelar inscrição', fn, tipo);
},
publicar: função (publicação, tipo)
{ [Link]('publicar', publicação, tipo);
},
visitSubscribers: função (ação, argumento, tipo) { var
pubtype = tipo || 'qualquer',
assinantes = [Link][pubtype], i, max
=
[Link];

para (i = 0; i < máx; i += 1) {

172 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

se (ação === 'publicar')


{ assinantes[i](arg); }
senão
{ se (assinantes[i] === arg)
{ [Link](i, 1);
}
}
}
}
};

E aqui está uma função que pega um objeto e o transforma em um publicador simplesmente
copiando os métodos do publicador genérico:
função makePublisher(o) { var
i; para
(i em publisher) { se
([Link](i) && typeof publisher[i] === "function") { o[i] = publisher[i];

} [Link] = {qualquer: []};


}

Agora vamos implementar o objeto de papel . Tudo o que ele pode fazer é publicar diariamente e mensalmente:

var paper =
{ daily: function ()
{ [Link]("grandes notícias de hoje");
},
mensal: function ()
{ [Link]("análise interessante", "mensal");
}
};

Tornar o papel uma editora:


makePublisher(papel);

Agora que temos um publicador, vamos ver o objeto assinante joe, que tem dois métodos:

var joe =
{ drinkCoffee: function (paper) '
{ [Link]('Apenas ler + papel);
},
sundayPreNap: função (mensal) '
{ [Link]('Estou prestes a adormecer lendo isso + mensal);
}
};

Agora o jornal assina Joe ( em outras palavras, Joe assina o jornal):


[Link]([Link]);
[Link]([Link], 'mensal');

Observador | 173
Machine Translated by Google [Link]

Como você pode ver, Joe fornece um método a ser chamado para o evento padrão "qualquer" e outro
método a ser chamado quando o evento do tipo "mensal" ocorre. Agora vamos disparar alguns
eventos:

[Link]ário();
[Link]ário();
[Link]ário();
[Link]();

Todas essas publicações chamam os métodos apropriados de Joe e o resultado no console é:


Acabei de ler as grandes notícias de hoje
Acabei de ler as grandes notícias de hoje
Acabei de ler as grandes notícias de hoje
Prestes a adormecer lendo esta análise interessante

A parte boa aqui é que o objeto de papel não codifica Joe, e Joe não codifica o papel. Também não
há um objeto mediador que saiba tudo. Os objetos participantes são fracamente acoplados e, sem
modificá-los, podemos adicionar muitos outros assinantes ao papel; Joe também pode cancelar a
assinatura a qualquer momento.

Vamos levar este exemplo um passo adiante e também tornar Joe um editor. (Afinal, com blogs e
microblogs, qualquer um pode ser um editor.) Então, Joe se torna um editor e pode postar atualizações
de status no Twitter:
makePublisher(joe);
[Link] = função (msg)
{ [Link](msg);
};

Agora imagine que o departamento de relações públicas do jornal decide ler o que seus leitores tuitam
e assina o Joe, fornecendo o método readTweets():
[Link] = function (tweet)
'
+ tweet);
{ alert('Ligue para uma reunião importante! Alguém
};
[Link]([Link]);

Agora, assim que Joe tuíta, o jornal é alertado:


[Link]("odiei o jornal hoje");

O resultado é um alerta: "Convoque uma reunião importante! Alguém odiou o jornal hoje."

Você pode ver o código-fonte completo e jogar no console com uma demonstração ao vivo em http://
[Link]/ book/ 7/ [Link] .

174 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Exemplo 2: O Jogo do Pressionamento de

Teclas. Vamos dar outro exemplo. Implementaremos o mesmo jogo do padrão mediador, mas desta
vez usando o padrão observador. Para torná-lo um pouco mais avançado, vamos aceitar um número
ilimitado de jogadores, não apenas dois. Ainda teremos o construtor Player() que cria objetos de jogador
e o objeto placar . Apenas o mediador se tornará um objeto de jogo .

No padrão mediador, o objeto mediador conhece todos os outros objetos participantes e chama seus
métodos. O objeto do jogo no padrão observador não fará isso; em vez disso, deixará que os objetos
se inscrevam em eventos interessantes. Por exemplo, o placar se inscreverá no evento "scorechange"
do jogo .

Vamos primeiro revisitar o objeto genérico do editor e ajustar um pouco sua interface para torná-lo mais
próximo do mundo do navegador:

• Em vez de publicar(), assinar() e cancelar assinatura(), teremos fogo(), on() e


remover().
• O tipo de evento será usado o tempo todo, então ele se torna o primeiro argumento para o
três funções.

• Um contexto extra pode ser fornecido além da função do assinante para permitir que o método de
retorno de chamada use this referindo-se ao seu próprio objeto.

O novo objeto do publicador se torna:


var publisher =
{ assinantes:
{ qualquer: []
},
em: função (tipo, fn, contexto) { tipo =
tipo || 'qualquer'; fn =
tipo de fn === "função" ? fn : contexto[fn];

se (tipo [Link][tipo] === "indefinido")


{ [Link][tipo] = [];

} [Link][tipo].push({fn: fn, contexto: contexto || isto});


},
remover: função (tipo, função, contexto)
{ [Link]('cancelar inscrição', tipo, função, contexto);
},
fire: function (tipo, publicação)
{ [Link]('publicar', tipo, publicação);
},
visitSubscribers: função (ação, tipo, argumento, contexto) {
var pubtype = tipo || 'qualquer',
assinantes = [Link][pubtype], i, max
=
assinantes ? [Link] : 0;

para (i = 0; i < máx; i += 1) { se


(ação === 'publicar') {

Observador | 175
Machine Translated by Google [Link]

assinantes[i].[Link](assinantes[i].contexto, arg); } senão { se

(assinantes[i].fn === arg && assinantes[i].contexto === contexto) { [Link](i,


1);
}
}
}
}
};

O novo construtor Player() se torna: function


Player(name, key) { [Link]
= 0; [Link] =
name; [Link] =
key;
[Link]('newplayer', this);
}

[Link]ó[Link] = função ()
{ [Link] += 1;
[Link]('jogar', isto);
};

A novidade aqui é que o construtor aceita a tecla key, a tecla do teclado que o jogador pressiona para
marcar pontos. (As teclas eram codificadas antes.) Além disso, toda vez que um novo objeto player é
criado, o evento "newplayer" é disparado. Da mesma forma, toda vez que um jogador joga, o evento
"play" é disparado.

O objeto do placar permanece o mesmo; ele simplesmente atualiza a exibição com o atual
pontuação.

O novo objeto de jogo pode rastrear todos os jogadores, produzindo uma pontuação e disparando um
evento "scorechange". Ele também se inscreverá em todos os eventos "keypress" do navegador e
saberá quais teclas correspondem a cada jogador: var game = {

chaves: {},

addPlayer: função (jogador) { var


chave = [Link]().charCodeAt(0);
[Link][chave] = jogador;
},

handleKeypress: função (e) {


e = e || [Link]; // IE se
([Link][[Link]]) {
[Link][[Link]].jogar();
}
},

handlePlay: função (jogador) { var i,

jogadores = [Link],

176 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

pontuação = {};

para (i em jogadores)
{ se ([Link](i))
{ pontuação[jogadores[i].nome] = jogadores[i].pontos;
}

} [Link]('scorechange', pontuação);
}
};

A função makePublisher() que transforma qualquer objeto em um publicador continua a mesma do


exemplo do jornal. O objeto do jogo se torna um publicador (para que possa disparar eventos de
"alteração de pontuação") e o [Link] se torna um publicador para que cada objeto jogador
possa disparar eventos de "jogar" e "novojogador" para quem quiser ouvir:
makePublisher([Link]ótipo);
makePublisher(jogo);

O objeto do jogo assina os eventos “play” e “newplayer” (e também “keypress” do navegador), enquanto
o placar assina “scorechange”: [Link]("newplayer", "addPlayer",
game); [Link]("play", "handlePlay", game);
[Link]("scorechange", [Link], scoreboard);
[Link] = [Link];

Como você pode ver aqui, o método on() permite que os assinantes especifiquem o retorno de chamada
como uma referência a uma função ([Link]) ou como uma string ("addPlayer"). A string
funciona desde que o contexto (por exemplo, jogo) também seja fornecido.

A parte final da configuração é criar dinamicamente objetos de jogador (com suas teclas correspondentes
a serem pressionadas), quantos o usuário desejar:
var playername, chave;
while (1)
{ playername = prompt("Adicionar jogador
(nome)"); if (!
playername) { break;

} enquanto (1)
"
{ chave = prompt("Chave + nome do jogador + "?");
para if
(chave) { break;
}

} novo Jogador(nome do jogador, chave);


}

E isso conclui o jogo. Você pode ver o código fonte completo e jogá-lo em http:// [Link]/ book/ 7/
[Link] .

Observe que, na implementação do padrão mediador, o objeto mediador precisava conhecer todos os
outros objetos para chamar os métodos corretos no momento certo e coordenar todo o jogo. Aqui, o
objeto do jogo é um pouco mais burro e depende dos objetos

Observador | 177
Machine Translated by Google [Link]

observando certos eventos e tomando medidas: por exemplo, o placar ouvindo o evento "scorechange". Isso
resulta em um acoplamento ainda mais fraco (quanto menos um objeto sabe, melhor) ao preço de tornar um
pouco mais difícil acompanhar quem ouve qual evento. Neste jogo de exemplo, todas as inscrições ocorreram
no mesmo lugar no código, mas à medida que um aplicativo cresce, as chamadas on() podem estar em todos
os lugares (por exemplo, no código de inicialização de cada objeto). Isso tornará mais difícil a depuração, pois
não haverá um único lugar para olhar o código e entender o que está acontecendo. No padrão observador,
você se afasta da execução sequencial procedural de código, onde começa do início e pode seguir o programa
até o fim.

Resumo
Neste capítulo, você aprendeu sobre vários padrões de design populares e viu como abordar sua
implementação em JavaScript. Os padrões de design que discutimos
eram:

Singleton:
Criando apenas um objeto de uma "classe". Analisamos diversas abordagens caso você queira substituir
a ideia de uma classe por uma função construtora e preservar a sintaxe semelhante à de Java. Caso
contrário, tecnicamente, todos os objetos em JavaScript são singletons. E, às vezes, os desenvolvedores
também usam o termo "singleton", referindo-se a objetos criados com o padrão de módulo.

Fábrica
Um método que cria objetos do tipo especificado como uma string em tempo de execução.
Iterador
Fornecer uma API para executar um loop e navegar em torno de dados personalizados complexos
estrutura.

Decorador
Ajustando objetos em tempo de execução adicionando funcionalidades de objetos decoradores
predefinidos.

Estratégia
Manter a mesma interface enquanto seleciona a melhor estratégia para lidar com a tarefa específica
(contexto).

Fachada
Fornecendo uma API mais conveniente ao encapsular métodos comuns (ou mal projetados) em uma
nova.

Encapsular um objeto para controlar o acesso a ele, com o objetivo de evitar operações caras, agrupando-as
ou executando-as somente quando realmente necessário.

178 | Capítulo 7: Padrões de Projeto


Machine Translated by Google [Link]

Mediador

Promovendo o acoplamento flexível fazendo com que seus objetos não “conversem” entre si diretamente, mas
apenas por meio de um objeto mediador.
Observador

Acoplamento fraco por meio da criação de objetos “observáveis” que notificam todos os seus observadores
quando ocorre um evento interessante (também chamado de assinante/publicador ou “eventos personalizados”).

Resumo | 179
Machine Translated by Google [Link]
Machine Translated by Google [Link]

CAPÍTULO 8

DOM e padrões de navegador

Nos capítulos anteriores do livro, o foco principal foi no JavaScript básico (ECMAScript) e não tanto nos
padrões de uso do JavaScript no navegador. Em contraste, este capítulo explora uma série de padrões
específicos do navegador, pois este é o ambiente mais comum para programas JavaScript. Scripts de
navegador também são o que a maioria das pessoas tem em mente quando diz que não gosta de
JavaScript. E isso é compreensível, considerando todos os objetos host inconsistentes e implementações
de DOM existentes nos navegadores. É óbvio que podemos nos beneficiar de quaisquer boas práticas
que ajudem a tornar a criação de scripts de cliente menos trabalhosa.

Neste capítulo, você verá alguns padrões divididos em diversas áreas, incluindo script DOM, tratamento
de eventos, script remoto, estratégias para carregar JavaScript na página e etapas para implantar
JavaScript em sites de produção.

Mas primeiro, vamos começar com uma discussão breve e um tanto filosófica sobre como abordar o
script do lado do cliente.

Separação de Preocupações
As três principais preocupações no desenvolvimento de aplicativos web são:

Contente
O documento HTML
Apresentação
Os estilos CSS que especificam a aparência do documento
Comportamento

O JavaScript, que lida com a interação do usuário e quaisquer alterações dinâmicas no documento

Manter as três preocupações o mais separadas possível melhora a entrega do aplicativo a uma ampla
gama de agentes de usuário — navegadores gráficos, navegadores somente texto, tecnologia assistiva
para usuários com deficiência, dispositivos móveis e assim por diante. A separação de preocupações
também anda de mãos dadas com a ideia de aprimoramento progressivo — você começa com o

181
Machine Translated by Google [Link]

Experiência básica (somente HTML) para os agentes de usuário mais simples e adicione mais à
experiência à medida que os recursos do agente de usuário melhoram. Se o navegador suportar CSS, o
usuário terá uma melhor apresentação do documento. Se o navegador suportar JavaScript, o documento
se tornará mais um aplicativo que adiciona mais recursos para aprimorar a experiência.

Na prática, a separação de preocupações significa:

• Testar a página com o CSS desativado para verificar se ela ainda pode ser usada e se o conteúdo
está presente e legível. •
Testar com o JavaScript desativado e verificar se a página ainda pode executar sua finalidade principal,
se todos os links funcionam (sem instâncias de href="#") e se todos os formulários ainda funcionam
e são enviados corretamente.
• Não usar manipuladores de eventos em linha (como onclick) ou atributos de estilo em linha , porque
estes não pertencem à camada de conteúdo
• Usando elementos HTML semanticamente significativos, como títulos e listas

A camada JavaScript (o comportamento) deve ser discreta, ou seja, não deve atrapalhar o usuário, não deve
tornar a página inutilizável em navegadores não suportados e não deve ser um requisito para o funcionamento
da página. Em vez disso, deve apenas aprimorar a página.

Uma técnica comum para lidar com diferenças entre navegadores com elegância é a detecção de
capacidade. Ela sugere que você não deve usar a detecção de agente de usuário para determinar um
caminho de código, mas sim verificar se um método ou propriedade que deseja usar existe no ambiente
atual. A detecção de agente de usuário é geralmente considerada um antipadrão. Às vezes, é inevitável,
mas deve ser o último recurso e apenas nos casos em que a detecção de capacidade não pode fornecer
um resultado conclusivo (ou causará uma queda significativa no desempenho):
// antipadrão se
([Link]('MSIE') !== ÿ1)
{ [Link]('onclick', [Link]);
}

// melhor
se ([Link])
{ [Link]('onclick', [Link]);
}

// ou ainda mais específico


if (typeof [Link] !== "undefined")
{ [Link]('onclick', [Link]);
}

Separar preocupações também ajuda no desenvolvimento, na manutenção e na facilidade de atualizações


de um aplicativo web existente, porque você sabe onde procurar quando algo quebra.
Quando há um erro de JavaScript, você não precisa olhar o HTML ou o CSS para
conserte isso.

182 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Scripting DOM
Trabalhar com a árvore DOM de uma página é uma das tarefas mais comuns em JavaScript do lado
do cliente. Isso também é uma grande causa de dores de cabeça (e dá má fama ao JavaScript), pois
os métodos do DOM são implementados de forma inconsistente em todos os navegadores. É por
isso que usar uma boa biblioteca JavaScript, que abstrai as diferenças entre navegadores, pode
acelerar significativamente o desenvolvimento.

Vamos ver alguns padrões recomendados ao acessar e modificar a árvore DOM, abordando
principalmente questões de desempenho.

Acesso DOM

O acesso ao DOM é caro; é o gargalo mais comum quando se trata de desempenho de JavaScript.
Isso ocorre porque o DOM geralmente é implementado separadamente do mecanismo JavaScript.
Da perspectiva de um navegador, faz sentido adotar essa abordagem, pois um aplicativo JavaScript
pode não precisar de DOM. Além disso, outras linguagens além de JavaScript (por exemplo, VBScript
no IE) podem ser usadas para trabalhar com o DOM da página.
O ponto principal é que o acesso ao DOM deve ser reduzido ao mínimo. Isso significa:

• Evitar acesso DOM em loops • Atribuir


referências DOM a variáveis locais e trabalhar com as variáveis locais • Usar API de
seletores quando disponível • Armazenar em
cache o comprimento ao iterar sobre coleções HTML (consulte o Capítulo 2)

Considere o exemplo a seguir, onde o segundo (melhor) loop, apesar de ser mais longo, será
dezenas a centenas de vezes mais rápido, dependendo do navegador: //
antipattern for
(var i = 0; i < 100; i += 1)
{ [Link]("result").innerHTML += i + ", ";
}

// melhor - atualiza uma variável local var


i, content = ""; for (i =
0; i < 100; i += 1) { content += i +
",";

} [Link]("resultado").innerHTML += conteúdo;

No próximo trecho, o segundo exemplo (usando uma variável local style) é melhor, embora exija mais
uma linha de código e mais uma variável: // antipattern var padding
=
[Link]("result").[Link], margin =
[Link]("result").[Link];

// melhor
var style = [Link]("result").style,

Scripting DOM | 183


Machine Translated by Google [Link]

preenchimento =
[Link], margem = [Link];

Usar APIs de seletor significa usar os métodos:


[Link]("ul .selected");
[Link]("#widget .class");

Esses métodos aceitam uma string seletora CSS e retornam uma lista de nós DOM que correspondem
à seleção. Os métodos seletores estão disponíveis em navegadores modernos (e no IE desde a versão
8) e sempre serão mais rápidos do que se você mesmo fizesse a seleção usando outros métodos
DOM. Versões recentes de bibliotecas JavaScript populares aproveitam as APIs do seletor, portanto,
certifique-se de usar uma versão atualizada da sua biblioteca preferida.

Também ajudará se você adicionar atributos id="" aos elementos que você acessará com frequência,
porque [Link](myid) é a maneira mais fácil e rápida de encontrar um nó.

Manipulação do DOM

Além de acessar os elementos do DOM, muitas vezes é necessário alterá-los, remover alguns deles
ou adicionar novos. Atualizar o DOM pode fazer com que o navegador redesenhe a tela e também,
com frequência, refaça o fluxo (recalcule a geometria dos elementos), o que pode ser caro.

Novamente, a regra geral é ter menos atualizações de DOM, o que significa agrupar alterações e
executá-las fora da árvore de documentos "ativa".

Quando precisar criar uma subárvore relativamente grande, você deve fazê-lo sem adicionar nada ao
documento ativo até o final. Para isso, você pode usar um fragmento de documento para conter todos
os seus nós.

Veja como não acrescentar nós:


// antipadrão //
anexando nós conforme eles são criados

var p, t;

p = [Link]('p');
t = [Link]('primeiro parágrafo');
[Link](t);
[Link](p);

p = [Link]('p');
t = [Link]('segundo parágrafo');
[Link](t);
[Link](p);

Uma versão melhor seria criar um fragmento de documento, atualizá-lo "offline" e adicioná-lo ao DOM
ativo quando estiver pronto. Quando você adiciona um fragmento de documento à árvore DOM, o
conteúdo do fragmento é adicionado, não o fragmento em si. E isso é realmente

184 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

conveniente. Portanto, o fragmento de documento é uma boa maneira de encapsular vários nós, mesmo
quando você não os contém em um elemento pai adequado (por exemplo, seus parágrafos não estão em
um elemento div ).

Aqui está um exemplo de uso de um fragmento de documento:


var p, t, frag;

frag = [Link]();

p = [Link]('p'); t
= [Link]('primeiro parágrafo');
[Link](t);
[Link](p);

p = [Link]('p'); t
= [Link]('segundo parágrafo');
[Link](t);
[Link](p);

[Link](frag);

Neste exemplo, o documento ativo é atualizado apenas uma vez, causando um único refluxo/repintura, em
oposição a um para cada parágrafo, como era o caso no snippet antipadrão anterior.

O fragmento de documento é útil ao adicionar novos nós à árvore. Mas, ao atualizar uma parte existente da
árvore, você ainda pode fazer alterações em lote. Você pode clonar a raiz da subárvore que está prestes a
alterar, fazer todas as alterações no clone e, quando terminar, trocar o original pelo clone: var oldnode =
[Link]('result'), clone = [Link](true);

// trabalhar com o clone...

// quando terminar:
[Link](clone, oldnode);

Eventos
Outra área de scripts de navegador repleta de inconsistências e fonte de frustração é o trabalho com
eventos do navegador, como clique, mouseover e assim por diante. Novamente, uma biblioteca JavaScript
pode eliminar grande parte do trabalho duplo necessário para suportar o IE (anterior à versão 9) e as
implementações em conformidade com o W3C.

Vamos rever os pontos principais, porque você pode não estar sempre usando uma biblioteca existente
para páginas simples e truques rápidos, ou você pode estar criando sua própria biblioteca.

Eventos | 185
Machine Translated by Google [Link]

Manipulação de

Eventos: Tudo começa com a anexação de ouvintes de eventos aos elementos. Digamos que você tenha um
botão que incrementa um contador cada vez que você clica nele. Você pode adicionar um atributo onclick
embutido , que funcionará em todos os navegadores, mas violará a separação de interesses e o aprimoramento
progressivo. Portanto, você deve se esforçar para anexar o ouvinte em JavaScript, fora de qualquer marcação.

Digamos que você tenha a seguinte marcação:


<button id="clickme">Clique em mim: 0</button>

Você pode atribuir uma função à propriedade onclick do nó, mas só pode fazer isso
uma vez:

// solução subótima var b =


[Link]('clickme'), count = 0; [Link] =
function ()
{ count += 1; [Link] = "Clique
em mim:
"
+ contagem;
};

Se você deseja que várias funções sejam executadas com um clique, não é possível fazê-lo com este padrão
mantendo o acoplamento fraco. Tecnicamente, você pode verificar se onclick já contém uma função e, em caso
afirmativo, adicionar a existente à sua própria função e substituir o valor onclick pela nova função. Mas uma
solução muito mais limpa é usar o método addEventListener() . Este método não existe no IE até a versão 8,
inclusive, então você precisa de attachEvent() para esses navegadores.

Quando analisamos o padrão de ramificação do tempo de inicialização (Capítulo 4), você viu um exemplo de
implementação de uma boa solução para definir um utilitário de escuta de eventos entre navegadores.
Sem entrar em muitos detalhes agora, vamos anexar um ouvinte ao nosso botão:

var b = [Link]('clickme'); if
([Link]) { // W3C
[Link]('click', myHandler, false);
} else if ([Link]) { // IE
[Link]('onclick', myHandler); } else
{ // último recurso [Link]
= myHandler;
}

Agora, quando este botão for clicado, a função myHandler() será executada. Vamos fazer com que esta função
incremente o número no rótulo do botão "Click me: 0". Para tornar a operação um pouco mais interessante,
vamos supor que temos vários botões e um único myHandler() para todos eles. Manter uma referência a cada
nó de botão e um contador para o número será ineficiente, visto que podemos obter essa informação do objeto
de evento criado a cada clique.

Vamos ver a solução primeiro e comentar depois:

186 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

função myHandler(e) {

var src, partes;

// obter evento e elemento de


origem e = e ||
[Link]; src = [Link] || [Link];

// trabalho real: atualizar rótulo


parts = [Link](": ");
parts[1] = parseInt(parts[1], 10) + 1;
[Link] = parts[0] + ": " + parts[1];

// sem bolha
if (typeof [Link] === "function")
{ [Link]();

} se (tipo de e. cancelBubble !== "indefinido") {


[Link] = verdadeiro;
}

// evita ação padrão if (typeof


[Link] === "function") { [Link]();

} se (tipo de [Link] !== "indefinido")


{ [Link] = falso;
}

Um exemplo ao vivo está disponível em http:// [Link]/ book/ 8/ [Link].

Há quatro partes na função do manipulador de eventos:

• Primeiro, precisamos obter acesso ao objeto de evento, que contém informações sobre o
evento e o elemento da página que o acionou. Este objeto de evento é passado para o
manipulador de eventos de retorno de chamada, mas não ao usar a propriedade onclick ,
onde é acessível através da propriedade global [Link] . • A
segunda parte é realizar o trabalho real de atualizar o rótulo. • Em seguida,
é cancelar a propagação do evento. Isso não é necessário neste exemplo específico, mas, em
geral, se você não fizer isso, o evento borbulha até a raiz do documento ou até mesmo o
objeto da janela. Novamente, precisamos fazer isso de duas maneiras: da maneira padrão
do W3C (stopPropagation()) e, em seguida, de forma diferente para o IE (usando
cancelBubble).
• Por fim, impeça a ação padrão, se necessário. Alguns eventos (clicar em um link, enviar um
formulário) têm ações padrão, mas você pode impedi-los usando preventDefault() (ou, no
IE, definindo returnValue como false).

Como você pode ver, há bastante trabalho duplicado envolvido, então faz sentido criar seu
utilitário de evento com métodos de fachada, conforme discutido no Capítulo 7.

Eventos | 187
Machine Translated by Google [Link]

Delegação de Eventos:

O padrão de delegação de eventos se beneficia do borbulhamento de eventos e reduz o número de


ouvintes de eventos vinculados a nós separados. Se houver 10 botões dentro de um elemento div ,
você poderá ter um ouvinte de eventos vinculado ao div , em vez de 10 ouvintes vinculados a cada
botão.

Vejamos um exemplo com três botões dentro de uma div (veja a Figura 8-1). Uma demonstração ao
vivo do exemplo de delegação de eventos está disponível em http:// [Link]/ book/ 8/ click-
[Link] .

Figura 8-1. Exemplo de delegação de evento: três botões que incrementam seus rótulos ao clicar

Então estamos trabalhando com a seguinte marcação:


<div id="click-wrap">
<button>Clique em mim: 0</button>
<button>Clique em mim também: 0</
button> <button>Clique em mim três vezes:
0</button> </div>

Em vez de anexar ouvintes a cada botão, você anexa um ao div “click-wrap”.


Então você pode usar a mesma função myHandler() do exemplo anterior com apenas uma pequena
mudança: você precisa filtrar os cliques nos quais não está interessado. Nesse caso, você procura
apenas por cliques em qualquer botão, então todos os outros cliques no mesmo div podem ser ignorados.

A mudança em myHandler() seria verificar se o nodeName da fonte do evento é um “botão”:

// ... //
obter evento e elemento de origem
e = e || [Link]; src
= [Link] || [Link];

se ([Link]() !== "botão")


{ retornar;

} // ...

A desvantagem da delegação de eventos é a necessidade de um pouco mais de código para filtrar os


eventos que ocorrem no contêiner e que não são interessantes para você. Mas os benefícios —
desempenho e código mais limpo — superam significativamente as desvantagens, por isso é um
padrão altamente recomendado.

Bibliotecas JavaScript modernas facilitam o uso da delegação de eventos, fornecendo APIs


convenientes. Por exemplo, o YUI3 possui o método [Link](), que permite especificar um seletor
CSS para corresponder ao wrapper e outro seletor para corresponder aos nós de seu interesse. Isso
é conveniente porque sua função de manipulador de eventos de retorno de chamada irá

188 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Na verdade, nunca será chamado quando o evento ocorrer fora dos nós que você considera
importantes. Nesse caso, o código para anexar um ouvinte de evento
seria simplesmente: [Link]('click', myHandler, "#click-wrap", "button");
E graças à abstração de todas as diferenças do navegador feitas no YUI e ao fato de que a origem
do evento é determinada por nós, a função de retorno de chamada será muito mais simples: function
myHandler(e) {

var src = [Link],


partes;

partes = [Link]('innerHTML').split(": ");


partes[1] = parseInt(partes[1], 10) + 1;
[Link]('innerHTML', partes[0] + ": " + partes[1]);

[Link]();
}

Um exemplo ao vivo está disponível em: http:// [Link]/ book/ 8/ [Link].

Scripts de longa duração


Você notou que às vezes o navegador reclama que um script está em execução há muito tempo e
pergunta ao usuário se ele deve ser interrompido. Você não quer que isso aconteça no seu aplicativo,
por mais complexa que seja a tarefa.

Além disso, se o script exigir muito esforço, a interface do navegador fica sem resposta a ponto de o
usuário não conseguir clicar em nada. Isso prejudica a experiência e deve ser evitado.

Em JavaScript não há threads, mas você pode simulá-las no navegador usando setTimeout() ou, em
navegadores mais modernos, web workers.

definirTempoEsgotado()

A ideia é dividir uma grande quantidade de trabalho em partes menores e executar cada parte com
um tempo limite de 1 ms. Usar partes com tempo limite de 1 ms pode fazer com que a tarefa seja
concluída mais lentamente, mas a interface do usuário permanecerá responsiva, e o usuário se
sentirá mais confortável e no controle do navegador.

Um tempo limite de 1 (ou mesmo 0 milissegundos) pode, na verdade, ser maior do que isso,
dependendo do navegador e do sistema operacional. Definir um tempo limite de 0 não significa
imediatamente, mas sim "o mais breve possível". No Internet Explorer, por exemplo, o menor
"tique" do relógio é de 15 milissegundos.

Roteiros de longa duração | 189


Machine Translated by Google [Link]

Trabalhadores da Web

Navegadores recentes oferecem outra solução para scripts de longa duração: web workers. Web
workers fornecem suporte a threads em segundo plano no navegador. Você coloca seus cálculos
pesados em um arquivo separado, por exemplo, my_web_worker.js, e então o chama a partir do
programa principal (página) da seguinte forma:
var ww = new Worker('my_web_worker.js');
[Link] = function (event)
{ [Link] "
+= "<p>mensagem do thread em segundo plano:+ [Link] + "</p>";
};

A fonte para um web worker que realiza uma operação aritmética simples 1e8 vezes (1 com 8 zeros)
é mostrada aqui:

var fim = 1e8, tmp = 1;

postMessage('olá');

enquanto (fim)
{ fim -= 1;
tmp += fim;
se (fim === 5e7) { // 5e7 é a metade de 1e8
'
postMessage('na metade do caminho, `tmp` agora +está
tmp);
}
}

postMessage('tudo pronto');

O trabalhador usa postMessage() para se comunicar com o chamador, e este assina o evento
onmessage para receber atualizações. O retorno de chamada onmessage recebe um objeto de evento
como argumento, e este objeto contém os dados da propriedade, que podem ser qualquer coisa que
o trabalhador queira passar. Da mesma forma, o chamador pode passar dados para o trabalhador
usando (neste exemplo) [Link]() , e o trabalhador pode assinar essas mensagens usando
um retorno de chamada onmessage .

O exemplo anterior imprimirá no navegador:


mensagem do tópico de fundo: olá mensagem do
tópico de fundo: na metade do caminho, `tmp` agora é 3749999975000001 mensagem do
tópico de fundo: tudo pronto

Scripting Remoto: Os

aplicativos web atuais costumam usar scripting remoto para se comunicar com o servidor sem precisar
recarregar a página atual. Isso permite aplicativos web muito mais responsivos e com aparência de
desktop. Vamos considerar algumas maneiras de se comunicar com o servidor usando JavaScript.

190 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

XMLHttpRequest

XMLHttpRequest é um objeto especial (uma função construtora) disponível na maioria dos


navegadores atuais, que permite fazer uma solicitação HTTP a partir de JavaScript. Há três
etapas para fazer uma solicitação:

1. Configure um objeto XMLHttpRequest (chamado XHR para abreviar).

2. Forneça uma função de retorno de chamada para ser notificado quando o objeto de solicitação mudar de estado.

3. Envie a solicitação.

O primeiro passo é tão fácil quanto:

var xhr = new XMLHttpRequest();

Mas no IE anterior à versão 7, a funcionalidade XHR era implementada como um objeto ActiveX, então um caso
especial é necessário aqui.

O segundo passo é fornecer um retorno de chamada para o evento readystatechange :

[Link] = manipular resposta;

O último passo é disparar a solicitação — usando dois métodos: open() e send(). O método open() configura o método
de solicitação HTTP (por exemplo, GET, POST) e a URL. O método send() passa quaisquer dados POST ou apenas
uma string em branco no caso de GET. O último parâmetro de open() especifica se a solicitação é assíncrona.

Assíncrono significa que o navegador não ficará bloqueado aguardando a resposta. Esta é
definitivamente a melhor experiência do usuário, portanto, a menos que haja um forte motivo
para isso, o parâmetro assíncrono deve ser sempre
verdadeiro: [Link]("GET",
"[Link]", true); [Link]();

Abaixo está um exemplo prático completo de como buscar o conteúdo de uma nova página e atualizar a página atual
com o novo conteúdo (demonstração disponível em http:// [Link]/ book/ 8/ [Link] ):

var i, xhr, activeXids =


[ '[Link].3.0',
'[Link]',
'[Link]'
];

se (typeof XMLHttpRequest === "função") { // XHR nativo


xhr = new XMLHttpRequest(); }
else { // IE antes de 7 para
(i = 0; i < [Link]; i += 1) {
tente
{ xhr = novo ActiveXObject(activeXids[i]);

quebrar; } pegar (e) {}


}
}

Scripting Remoto | 191


Machine Translated by Google [Link]

[Link] = função () { se
([Link] !== 4) { retornar
falso;

} se ([Link] !== 200) {


"
alert("Erro, código de status: + [Link]);
retornar falso;

} [Link] += "<pre>" + [Link] + "<\/pre>";


};

[Link]("OBTER", "[Link]",
verdadeiro); [Link]("");

Alguns comentários sobre o exemplo:

• Devido às versões 6 e anteriores do IE, o processo de criação de um novo objeto XHR é um pouco
mais complicado. Percorremos uma lista de identificadores ActiveX, da versão mais recente para a
mais antiga, e tentamos criar um objeto, envolvendo-o em um bloco try-catch.

• A função de retorno de chamada verifica a propriedade readyState do objeto xhr . Há cinco valores
possíveis para essa propriedade — de 0 a 4 — onde 4 significa "concluído". Se o estado ainda não
estiver completo, continuamos aguardando o próximo evento readystatechange . • O retorno de
chamada também verifica a propriedade status do objeto xhr . Essa propriedade corresponde ao código
de status HTTP, por exemplo, 200 (OK) ou 404 (Não encontrado).
Estamos interessados apenas nos 200 códigos de resposta e reportamos todos os outros como
erros (para simplificar; caso contrário, existem outros códigos de status válidos que você pode
verificar). • O código, conforme listado, verificará a maneira suportada de criar um objeto XHR sempre
que uma solicitação for feita. Tendo visto alguns padrões em capítulos anteriores (por exemplo,
ramificação em tempo de inicialização), você pode reescrever isso para fazer a verificação apenas
uma vez.

JSONP

JSONP (JSON com preenchimento) é outra maneira de fazer solicitações remotas. Ao contrário do XHR,
não é restrito pela política de navegador de mesmo domínio, portanto, deve ser usado com cautela devido
às implicações de segurança do carregamento de dados de sites de terceiros.

A resposta a uma solicitação XHR pode ser qualquer tipo de documento:

• Documentos XML (historicamente) •


Blocos HTML (bastante comuns) • Dados
JSON (leves e convenientes) • Arquivos de texto
simples e outros

Com JSONP, os dados geralmente são encapsulados em JSON em uma chamada de função, onde o
nome da função é fornecido com a solicitação.

192 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Um exemplo de URL de solicitação JSONP normalmente seria algo como isto:

http:// [Link]/ [Link]?callback=myHandler

[Link] pode ser qualquer tipo de página ou script. O parâmetro callback especifica qual função
JavaScript manipulará a resposta.

A URL é então carregada em um elemento <script> dinâmico , assim:


var script = [Link]("script");
[Link] = url;
[Link](script);

O servidor responde com alguns dados JSON passados como parâmetro para a função de retorno
de chamada. O resultado final é que você incluiu um novo script na página, que por acaso é uma
chamada de função. Por exemplo:
myHandler({"olá": "mundo"});

Exemplo de JSONP: Jogo

da velha. Vamos colocar o JSONP em prática com um exemplo: um jogo da velha, em que os
jogadores são o cliente (o navegador) e o servidor. Ambos gerarão números aleatórios entre 1 e 9, e
usaremos JSONP para obter o valor da vez do servidor (veja a Figura 8-2).

Você pode jogar o jogo ao vivo em http:// [Link]/ book/ 8/ [Link].

Figura 8-2. Desafio JSONP do jogo da velha

Existem dois botões: para iniciar um novo jogo e para obter a vez do servidor (a vez do cliente será
automática após um tempo limite):

<button id="new">Novo jogo</button>


<button id="server">Jogo no servidor</button>

Scripting Remoto | 193


Machine Translated by Google [Link]

O quadro conterá nove células de tabela com os atributos de id correspondentes . Por exemplo:
<td id="célula-1">&nbsp;</td>
<td id="célula-2">&nbsp;</td>
<td id="célula-3">&nbsp;</td>
...

Todo o jogo é implementado em um objeto global ttt :


var ttt = { //
células tocadas até agora
played: [],

// atalho get:
função (id) { return
[Link](id);
},

// manipular cliques
setup: function ()
{ [Link]('new').onclick = [Link];
[Link]('server').onclick = [Link];
},

// limpa o tabuleiro
newGame: function () { var
tds = [Link]("td"), max =
[Link], i;

para (i = 0; i < máx; i += 1) {


tds[i].innerHTML = "&nbsp;";

} [Link] = [];
},

// faça uma
solicitação remoteRequest:
function () { var script = [Link]("script");
[Link] = "[Link]?callback=[Link]&played=" + [Link](',');
[Link](script);
},

// retorno de chamada, é a vez do servidor


jogar serverPlay: function (data) { if
([Link])
{ alert([Link]);
return;

} dados = parseInt(dados, 10);


[Link](dados);

[Link]('cell-' + dados).innerHTML = '<span class="servidor">X<\/span>';

setTimeout(function ()
{ [Link](); },
300); // como se estivesse pensando bastante

194 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

},

// vez do cliente jogar


clientPlay: function () { var
data = 5;

se ([Link] === 9)
{ alerta("Fim de jogo");
retornar;
}

// continue criando números aleatórios de 1 a 9 //


até que uma célula não ocupada seja
encontrada while ([Link]('cell-' + data).innerHTML !== "&nbsp;")
{ data = [Link]([Link]() * 9);

} [Link]('célula-' + dados).innerHTML = 'O';


[Link](dados);

};

O objeto ttt mantém uma lista de células reproduzidas até o momento em [Link] e as envia ao servidor,
para que este possa retornar um novo número, excluindo as que já foram reproduzidas. Se ocorrer um erro,
o servidor responderá com uma saída como: [Link]({"error": "Descrição do
erro aqui"});

Como você pode ver, o retorno de chamada em JSONP precisa ser uma função pública e globalmente
disponível, não necessariamente uma função global, mas pode ser um método de um objeto global. Se não
houver erros, o servidor responderá com uma chamada de método como:

[Link](3);

Aqui, 3 significa que a célula número 3 é a escolha aleatória do servidor. Neste caso, os dados são tão
simples que nem precisamos do formato JSON; basta um único valor.

Frames e Image Beacons: Uma

maneira alternativa de executar scripts remotos é usar frames. Com JavaScript, você pode criar um iframe e
alterar sua URL de origem . A nova URL pode conter dados e chamadas de função que atualizam o autor da
chamada — a página pai fora do iframe.

A forma mais simples de script remoto é quando tudo o que você precisa fazer é enviar dados para o servidor
e não está esperando uma resposta. Nesses casos, você pode criar uma nova imagem e apontar sua origem
para o script no servidor:

nova Imagem().src = "[Link]

Este padrão é chamado de beacon de imagem e é útil quando você deseja enviar dados para serem
registrados pelo servidor, por exemplo, para coletar estatísticas de visitantes. Como você não tem

Scripting Remoto | 195


Machine Translated by Google [Link]

Para usar a resposta de tal beacon, uma prática comum (mas um antipadrão) é fazer com que o
servidor responda com uma imagem GIF 1×1. Uma opção melhor seria responder com uma resposta
HTTP “204 Sem Conteúdo”. Isso significa que apenas um cabeçalho e nenhum corpo de resposta são
enviados de volta ao cliente.

Implementando JavaScript
Existem algumas considerações de desempenho quando se trata de servir JavaScript. Vamos discutir
as mais importantes em detalhes. Para mais detalhes, consulte Sites de Alto Desempenho. e sites
ainda mais rápidos, ambos publicados pela O'Reilly.

Combinando Scripts: A

primeira regra ao criar páginas de carregamento rápido é ter o mínimo possível de componentes
externos, pois as solicitações HTTP são caras. No caso do JavaScript, isso significa que você pode
acelerar significativamente o tempo de carregamento da página combinando arquivos de script externos.

Digamos que sua página esteja usando a biblioteca jQuery. Trata-se de um arquivo .js . Você também
está usando alguns plugins jQuery, que também vêm em arquivos separados. Dessa forma, você pode
rapidamente terminar com 4 ou 5 arquivos antes de escrever uma única linha. Faz sentido combinar
esses arquivos em um, especialmente considerando que alguns deles serão pequenos (2 a 3 KB) e a
sobrecarga HTTP levará mais tempo do que o download em si. Combinar os scripts significa
simplesmente criar um novo arquivo e colar o conteúdo de cada um.

É claro que essa concatenação de arquivos deve ocorrer somente antes do código ir para produção e
não durante o desenvolvimento, onde tornará a depuração mais difícil.

As desvantagens de combinar arquivos de script são:

• É mais uma etapa antes de entrar no ar, mas pode ser facilmente automatizada e feita em
a linha de comando, por exemplo usando cat no Linux/Unix:
$ cat [Link] [Link] [Link] > [Link] • Perder alguns
dos benefícios do cache — ao fazer uma pequena alteração em um dos arquivos, você invalida todo
o pacote. Por isso, é bom ter um cronograma de lançamento para projetos maiores ou considerar
ter dois pacotes: um que contenha os arquivos que devem sofrer alterações e um pacote
"principal" que praticamente não sofre alterações. • Você precisa criar um padrão de
nomenclatura e controle de versão para o pacote, como usar um registro de data e hora:
all_20100426.js ou um hash do conteúdo do arquivo.

As desvantagens podem ser resumidas principalmente como uma inconveniência, mas o benefício
vale a pena.

196 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Minificação e Compactação No Capítulo

2, falamos sobre minificação de código. É importante que o processo de minificação também faça parte do processo
de lançamento do seu build.

Quando você pensa da perspectiva do usuário, não há razão para que ele tenha que baixar todos os comentários
no seu código, que não servem para nada na maneira como o aplicativo funciona.

O benefício da minificação pode variar dependendo da abundância de comentários e espaços em branco usados,
e também das ferramentas de minificação específicas que você utiliza. Mas, em média, você teria uma redução de
cerca de 50% no tamanho do arquivo.

Servir o arquivo de script compactado também é algo que você deve sempre fazer. É uma configuração simples e
única do servidor para habilitar a compactação gzip, o que proporciona um aumento instantâneo na velocidade.
Mesmo se você estiver usando um provedor de hospedagem compartilhada que não oferece muita liberdade em
termos de configuração do servidor, a maioria dos provedores pelo menos permite que você use os arquivos de
configuração .htaccess do Apache . Então, na raiz do seu site, adicione isto ao arquivo .htaccess :

AddOutputFilterByType DEFLATE texto/html texto/css texto/texto simples/aplicativo


xml/aplicativo javascript/json

A compactação resultará em arquivos 70% menores, em média. Combinando compactação com minificação, você
pode esperar que seus usuários baixem apenas 15% do tamanho do arquivo do código-fonte não minificado e não
compactado que você escreveu.

Cabeçalho Expira

Ao contrário da crença popular, os arquivos não ficam muito tempo no cache do navegador.
Você pode fazer sua devida diligência e aumentar as chances de ter seus arquivos no cache para visitas repetidas
usando um cabeçalho Expires .

Novamente, esta é uma configuração de servidor única que você pode fazer em .htaccess:

Expira em Ativo em
ExpiresByType application/x-javascript "acesso mais 10 anos"

A desvantagem é que se você quiser alterar o arquivo, também precisará renomeá-lo, mas provavelmente já fará
isso se tiver estabelecido uma convenção de nomenclatura para seus pacotes de arquivos combinados.

Usando uma

CDN CDN significa Rede de Distribuição de Conteúdo (Content Delivery Network). Trata-se de um
serviço de hospedagem pago (às vezes bastante caro) que permite distribuir cópias dos seus arquivos
em diferentes data centers ao redor do mundo e disponibilizá-las mais rapidamente aos seus usuários,
mantendo a mesma URL no seu código.

Implementando JavaScript | 197


Machine Translated by Google [Link]

Mesmo que você não tenha orçamento para CDN, você ainda pode se beneficiar de algumas opções gratuitas:

• O Google hospeda uma série de bibliotecas populares de código aberto, às quais você pode se conectar para
grátis e aproveite seu CDN.

• A Microsoft hospeda o jQuery e suas próprias bibliotecas Ajax. • O

Yahoo! hospeda a biblioteca YUI em seu CDN.

Estratégias de carregamento

Como incluir um script em uma página da web parece uma questão simples à primeira vista — você usa um
elemento <script> e incorpora o código JavaScript ou vincula a um arquivo separado no atributo src :

// opção 1
<script>
[Link]("olá mundo"); </
script> //
opção 2
<script src="[Link]"></script>

Mas há mais alguns padrões e considerações que você deve conhecer quando seu objetivo é criar aplicativos
web de alto desempenho.

Como observação lateral, há alguns atributos comuns que os desenvolvedores tendem a usar com o elemento
<script> :

idioma="JavaScript"
Em muitas formas de capitalizar “JavaScript” e às vezes com um número de versão.
O atributo "language" não deve ser usado, pois implica que a linguagem é JavaScript. O número da versão
não funciona muito bem e, em retrospecto, é considerado um erro. type="text/javascript"

Este atributo é exigido pelos padrões HTML4 e XHTML1, mas não deveria ser, pois os navegadores
assumem JavaScript de qualquer forma. O HTML5 está tornando este atributo desnecessário. Além de
satisfazer os validadores de marcação, não há outro motivo para usar o atributo type .

adiar
(E melhor ainda, o async do HTML5) também é uma maneira, embora não amplamente suportada, de
especificar que o download do arquivo de script externo não deve bloquear o restante da página. Mais
sobre bloqueio a seguir.

198 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

O Lugar do Elemento <script> Os


elementos de script bloqueiam downloads progressivos de páginas. Os navegadores baixam
vários componentes de uma só vez, mas quando encontram um script externo, interrompem
os downloads até que o arquivo de script seja baixado, analisado e executado. Isso prejudica
o tempo total da página, especialmente se ocorrer várias vezes durante o carregamento.
Para minimizar o efeito de bloqueio, você pode colocar o elemento de script próximo ao final da
página, logo antes da tag de fechamento </body> . Dessa forma, não haverá outros recursos para o
script bloquear. Os demais componentes da página serão baixados e já estarão engajando o usuário.

O pior antipadrão é usar arquivos separados no cabeçalho do documento:

<!doctype html>
<html>
<head>
<title>Meu aplicativo</title> <!--
ANTIPATTERN --> <script
src="[Link]"></script> <script
src="[Link]"></script> <script src="[Link]"></
script> <script src="[Link]"></script> </head> <body>

...
</corpo>
</html>

Uma opção melhor é combinar todos os arquivos:

<!doctype html>
<html>
<cabeçalho>
<title>Meu aplicativo</
title> <script src="all_20100426.js"></script>
</head>
<body>
...
</corpo>
</html>

E a melhor opção é colocar o script combinado bem no final da página:

<!doctype html>
<html>
<head>
<title>Meu aplicativo</title> </
head>
<body>
...
<script src="all_20100426.js"></script> </
corpo>
</html>

Estratégias de Carregamento | 199


Machine Translated by Google [Link]

Fragmentação

HTTP: O protocolo HTTP suporta a chamada codificação em blocos. Ela permite o envio da
página em partes. Portanto, se você tiver uma página complexa, não precisará esperar que todo
o trabalho do lado do servidor seja concluído antes de começar a enviar o cabeçalho mais ou
menos estático da página.

Uma estratégia simples é enviar o conteúdo <head> da página com o primeiro bloco enquanto
o restante da página está sendo montado. Em outras palavras, você pode ter algo assim:

<!doctype html>
<html>
<head>
<title>Meu aplicativo</title> </
head>
<!-- fim do bloco #1 --> <body>

...
<script src="all_20100426.js"></script> </body> </html>
<!-- fim
do bloco
#2 -->

Uma melhoria simples seria mover o JavaScript de volta para <head> e servi-lo com o
primeiro bloco. Dessa forma, o navegador tem uma vantagem inicial no download do arquivo
de script enquanto o restante da página ainda não está pronto no lado do servidor:

<!doctype html>
<html>
<head>
<title>Meu aplicativo</title>
<script src="all_20100426.js"></script> </head> <!-- fim
do bloco
#1 --> <body>

...
</body>
</html>
<!-- fim do bloco #2 -->

Uma opção ainda melhor seria ter um terceiro bloco, que contenha apenas o script no final
da página. Você também pode enviar parte do corpo do texto com o primeiro bloco se tiver
um cabeçalho estático no topo de cada página:

<!doctype html>
<html>
<head>
<title>Meu aplicativo</title> </
head>
<body>
<div id="header">
<img src="[Link]" />
...
</div>
<!-- fim do bloco #1 -->

200 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

... O corpo inteiro da página ...

<!-- fim do bloco #2 -->


<script src="all_20100426.js"></script> </body> </
html>
<!-- fim
do bloco #3 -->

Essa abordagem se encaixa perfeitamente no espírito de aprimoramento progressivo e do JavaScript discreto.


Logo após o final do segundo bloco de HTML, você deverá ter uma página completamente carregada, exibida
e utilizável, como se o JavaScript estivesse desabilitado no navegador.
Então, quando o JavaScript chega com o terceiro pedaço, ele aprimora a página, adicionando todos os recursos.

Elemento <script> dinâmico para downloads não bloqueados. Como já

mencionado, o JavaScript bloqueia o download dos arquivos subsequentes. Mas vários padrões permitem evitar
isso:

• Carregar o script com uma requisição XHR e, em seguida, usar eval() como uma string. Essa abordagem
sofre com restrições de mesmo domínio e também envolve eval(), que é um antipadrão por si só. • Usar
os atributos defer e async ,

mas estes não funcionam em todos os navegadores. • Usar um elemento <script> dinâmico .

O último é um padrão bom e factível. Semelhante ao que você viu com JSONP, você cria um novo elemento de
script, define sua origem e o anexa à página.

Este é um exemplo que carregará um arquivo JavaScript de forma assíncrona sem bloquear o restante dos
downloads:

var script = [Link]("script"); [Link] =


"all_20100426.js";
[Link](script);

A desvantagem desse padrão é que você não pode ter outros elementos de script que sigam esse padrão se
eles dependerem do carregamento do .js principal. O .js principal é carregado de forma assíncrona, então não
há garantia de quando ele chegará, e os scripts que vêm depois dele podem assumir objetos que ainda não
foram definidos.

Para resolver essa desvantagem, você pode fazer com que todos os scripts inline não sejam executados
imediatamente, mas sim reunidos como funções em um array. Assim, quando o script principal chega, ele pode
executar todas as funções coletadas no array de buffers. Portanto, há três etapas para conseguir isso.

Primeiro, crie uma matriz para armazenar todo o código embutido, o mais cedo possível na página:

var meunamespace =
{ inline_scripts: []
};

Estratégias de Carregamento | 201


Machine Translated by Google [Link]

Em seguida, você precisa encapsular todos os scripts inline individuais em uma função e anexar cada
função ao array inline_scripts . Em outras palavras:
// era: //
<script>[Link]("Estou inline");</script>

// torna-se:
<script>
mynamespace.inline_scripts.push(function ()
{ [Link]("Estou inline"); });
</
script>

E o último passo é fazer com que seu script principal faça um loop no buffer de scripts inline e execute
todos eles:

var i, scripts = mynamespace.inline_scripts, max = [Link]; para (i =


0; i < máx; máx += 1) { scripts[i]();

Acrescentando o elemento

<script> Normalmente, os scripts são acrescentados ao <head> do documento, mas você pode
acrescentá-los a qualquer elemento, incluindo o corpo (como no exemplo JSONP).

No exemplo anterior, usamos documentElement para anexar ao <head>, porque documentElement é


o <html> e seu primeiro filho é o <head>:

[Link](script);

Isso também é comumente escrito


como: [Link]("head")[0].appendChild(script);

Isso é aceitável quando você controla a marcação, mas e se você estiver criando um widget ou
anúncio e não tiver ideia de que tipo de página o hospedará? Tecnicamente, você pode não ter
<head> nem <body> na página; embora [Link] certamente funcione mesmo sem a tag
<body> :
[Link](script);

Mas, na verdade, há uma tag que sempre existirá na página onde seu script é executado: uma tag de
script. Se não houvesse uma tag de script (para um arquivo inline ou externo), seu código não seria
executado. Para usar esse fato, você pode usar insertBefore() para o primeiro elemento de script
disponível na página:
var primeiro_script = [Link]('script')[0];
primeiro_script.[Link](script, primeiro_script);

Aqui, first_script é o elemento de script que certamente estará na página e script é o novo elemento
de script que você cria.

202 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Carregamento

Preguiçoso: A técnica conhecida como carregamento lento refere-se ao carregamento de um arquivo externo após o
evento de carregamento da página . Geralmente, é vantajoso dividir um grande pacote de código em duas partes:

• Uma parte necessária para a página inicializar e anexar manipuladores de eventos ao


Elementos da interface do usuário

• Uma segunda parte que só é necessária após a interação do usuário ou outras condições

O objetivo é carregar a página progressivamente e dar ao usuário algo com que trabalhar o mais rápido possível. O
restante pode ser carregado em segundo plano enquanto o usuário está engajado e navegando pela página.

A maneira de carregar a segunda parte do JavaScript é simplesmente usar um elemento de script dinâmico anexado
ao cabeçalho ou ao corpo:

... O corpo inteiro da página ...

<!-- fim do bloco 2 --> <script


src="all_20100426.js"></script> <script> [Link]
= function
() { var script =
[Link]("script"); [Link] = "all_lazy_20100426.js";
[Link](script);

};
</script> </
body> </
html> <!--
fim do bloco #3 -->

Para muitas aplicações, a parte preguiçosa do código geralmente será maior que a parte principal, porque a “ação”
interessante (como arrastar e soltar, XHR e animações) acontece somente depois que o usuário a inicia.

Carregamento sob

demanda: o padrão anterior carregava JavaScript adicional incondicionalmente após o carregamento da


página, assumindo que o código provavelmente seria necessário. Mas será que podemos fazer melhor e
carregar apenas partes do código e apenas as partes realmente necessárias?

Imagine que você tenha uma barra lateral na página com diferentes abas. Clicar em uma aba faz uma solicitação XHR
para obter conteúdo, atualiza o conteúdo da aba e anima a atualização, esmaecendo a cor. E se este for o único lugar
na página em que você precisa do XHR e das bibliotecas de animação, e se o usuário nunca clicar em uma aba?

Entre no padrão de carregamento sob demanda. Você pode criar uma função ou método require() que recebe o nome
de arquivo de um script a ser carregado e uma função de retorno de chamada a ser executada quando o script
adicional for carregado.

Estratégias de Carregamento | 203


Machine Translated by Google [Link]

A função require() pode ser usada assim:

require("[Link]", função ()
{ functionDefinedInExtraJS(); });

Vamos ver como você pode implementar essa função. Solicitar o script adicional é simples — basta seguir o padrão
dinâmico do elemento <script> . Descobrir quando o script é carregado é um pouco mais complicado devido às
diferenças entre navegadores:

função require(arquivo, retorno de chamada) {

var script = [Link]('script')[0], newjs


= [Link]('script');

// IE
[Link] = function () { if
([Link] === 'carregado' || [Link] === 'completo')
{ [Link] = nulo;
retorno de chamada();
}
};

// outros
[Link] = function ()
{ callback();
};

[Link] = arquivo;
[Link](newjs, script);
}

Alguns comentários sobre esta implementação:

• No IE, você se inscreve no evento readystatechange e procura por um readyState “carregado” ou “completo”.
Todos os outros navegadores irão ignorar isso. • No Firefox, Safari e Opera,

você se inscreve no evento load por meio do onload


propriedade.

• Esta abordagem não funciona no Safari 2. Se este navegador for um requisito, para que funcione, você terá que
configurar um intervalo para verificar periodicamente se uma variável especificada (que você define no arquivo
adicional) foi definida. Quando ela for definida, significa que o novo script foi carregado e executado.

Você pode testar essa implementação criando um script artificialmente atrasado (para simular a latência da rede),
chamado [Link], por exemplo:

<?php
header('Tipo de conteúdo: aplicativo/javascript'); sleep(1); ?>

função extraFunction(logthis) {
[Link]('carregado e executado');
[Link](logthis);
}

204 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

Agora testando a função require() :


require('[Link]', função () {
extraFunction('carregado da página pai');
[Link]([Link]('done!')); });

Este snippet escreverá duas linhas no console e atualizará a página dizendo “concluído!”
Você pode ver o exemplo ao vivo em http:// [Link]/ book/ 7/ [Link].

Pré-carregamento de

JavaScript: No padrão de carregamento lento e no padrão sob demanda, carregamos posteriormente


os scripts necessários para a página atual. Além disso, você também pode carregar posteriormente
scripts que não são necessários na página atual, mas na página que provavelmente virá a seguir.
Dessa forma, quando o usuário acessa a segunda página, o script já está pré-carregado e a experiência
geral se torna muito mais rápida.

O pré-carregamento pode ser implementado simplesmente usando o padrão de script dinâmico. Mas
isso significa que o script será analisado e executado. Embora a análise apenas aumente o tempo
total gasto no pré-carregamento, a execução também pode causar erros de JavaScript quando o script
pré-carregado presume que está sendo executado na segunda página e, por exemplo, espera
encontrar determinados nós DOM.

É possível carregar scripts sem analisá-los e executá-los; isso também funciona para CSS e imagens.

No IE, você pode fazer uma solicitação com o conhecido padrão de image beacon:
nova Imagem().src = "[Link]";

Em todos os outros navegadores, você pode usar um <object> em vez de um elemento de script e
definir seu atributo de dados para apontar para a URL
do script: var obj = [Link]('object');
[Link] = "[Link]";
[Link](obj);

Para evitar que o objeto fique visível, você também deve definir seus atributos de largura e altura
como 0.

Você pode criar uma função ou método preload() de uso geral e também usar o padrão de ramificação
init-time (Capítulo 4) para lidar com as diferenças do navegador:
var preload; if
(/*@cc_on!@*/false) { // IE sniffing com comentários condicionais preload =
function (file) { new Image().src
= file;

}; } senão
{ pré-carregamento = função
(arquivo) { var obj = [Link]('object'),
corpo = [Link];

Estratégias de Carregamento | 205


Machine Translated by Google [Link]

[Link] = 0;
[Link] = 0;
[Link] =
arquivo; [Link](obj);
};
}

Usando a nova função:

pré-carregar('my_web_worker.js');

A desvantagem desse padrão é a presença de sniffing do agente do usuário, mas isso não pode ser evitado
porque, neste caso, a capacidade de detecção não nos informa o suficiente sobre o comportamento do navegador.
Neste padrão, por exemplo, teoricamente, você pode testar se typeof Image é uma "função" e usá-la em vez do
sniffing. No entanto, isso não ajudará aqui, porque todos os navegadores suportam new Image(); apenas alguns
têm um cache separado para imagens, o que significa que o pré-carregamento de componentes como uma
imagem não será usado como um script do cache na segunda página, mas será baixado novamente.

A detecção de navegadores usando comentários condicionais é interessante por si só. É


um pouco mais seguro do que procurar strings em [Link], pois essas strings
são fáceis de alterar pelo usuário.

Tendo isso:

var isIE = /*@cc_on!@*/false;

definirá isIE como falso em todos os navegadores (porque eles ignoram o comentário), mas será
verdadeiro no Internet Explorer, devido à negação ! no comentário condicional. É como se o IE
visse:

var isIE = !false; // verdadeiro

O padrão de pré-carregamento pode ser usado para todos os tipos de componentes, não apenas scripts. É útil,
por exemplo, em páginas de login. Quando o usuário começa a digitar seu nome de usuário, você pode usar
esse tempo de digitação para iniciar o pré-carregamento (nada confidencial, é claro), pois é provável que o
usuário acabe na segunda página de login.

Resumo
Enquanto os capítulos anteriores do livro cobriam principalmente padrões básicos do JavaScript, independentes
do ambiente, este se concentrou em padrões aplicáveis apenas no
ambiente de navegador do lado do cliente.

Nós olhamos para:

• As ideias de separação de interesses (HTML: conteúdo, CSS: apresentação, Javascript: comportamento),


JavaScript discreto e detecção de recursos versus sniffing de navegador. (Embora, no final do capítulo,
você tenha aprendido como quebrar esse padrão.)

206 | Capítulo 8: DOM e Padrões de Navegador


Machine Translated by Google [Link]

• Scripting DOM — padrões para acelerar o acesso e a manipulação do DOM, principalmente


agrupando as operações do DOM porque tocar no DOM sempre acontece
um custo.

• Eventos, tratamento de eventos entre navegadores e uso de delegação de eventos para reduzir o
número de ouvintes de eventos e melhorar o desempenho. •
Dois padrões para lidar com casos de computações pesadas de longa duração — usando set
Timeout() para dividir operações longas em pedaços menores e usando web workers em
navegadores modernos.

• Vários padrões para scripts remotos e comunicação entre servidor e


cliente—XHR, JSONP, frames e image beacons.
• Etapas para implantar JavaScript no ambiente de produção — garantindo que os scripts sejam
combinados em menos arquivos, minificados e compactados (economia total de 85%) e,
idealmente, hospedados em uma CDN e enviados com o cabeçalho Expires para
melhorar o cache. • Padrões para incluir os scripts em uma página para melhor desempenho,
incluindo: vários lugares para colocar o elemento <script> , ao mesmo tempo em que se beneficia do HTTP chunking.
Também para reduzir o "impacto" inicial de carregar um script grande, analisamos vários padrões,
como carregamento lento, pré-carregamento e carregamento sob demanda do JavaScript.

Resumo | 207
Machine Translated by Google [Link]
Machine Translated by Google [Link]

Índice

Um objeto de ativação, 3 Método C call(), 136, 137


métodos addEventListener(), 186 funções funções de retorno de chamada

anônimas, 58 antipadrões ouvintes de eventos assíncronos e, 66


definidos, 2 definidos, 62
strings de bibliotecas e, 67
passagem, 67 detecção parênteses e, 63 scripts
de agente de usuário, 182 remotos, 192 padrões de
documentação de API, 30, 34 sandbox e, 105 considerações
padrões de de escopo, 64–65 tempos limite e,
retorno de chamada de API, 66 exemplo de uso,
62–67 objetos de configuração, 77–78 63, 64 convenção de caso
currying, 81–83 de camelo, 28 detecção de
funções de retorno, 67 método capacidade, 182 instrução
apply(), 136, 137 propriedade catch, 53
[Link], 46, 77 sintaxe literal de CDN (Content Delivery Network), padrão de
array do construtor de encadeamento 197, codificação
array, 47 método isArray(), em blocos 110–111, tag @class
48 strings de repetição and, 200–201 (YUIDoc), 33 classes
48 operador typeof and, 48
exemplo de uso, 46, 47 notação emulando, 128–130
literal de array, 46–48 arrays, JavaScript e, 4 novos
empréstimo de, 137 ouvintes de operadores e, 115
eventos assíncronos, 66 atributos construtores de
empréstimo de herança clássica, 120–123
padrão padrão, 117–120
definido, 3 resultados esperados, 116
elementos de script, 198, 201 moderno versus, 115
protótipo de aluguel e conjunto, 123–124
protótipos de compartilhamento,
124 construtores temporários, 125–128
Marcadores B , 72
Closure Compiler (Google), 36, 74
Construtor booleano, 52
construtores
Tipo de valor booleano, 52
de empréstimo de reutilização de código, 120–123

Gostaríamos de ouvir suas sugestões para melhorar nossos índices. Envie um e-mail para index@[Link].

209
Machine Translated by Google [Link]

métodos de empréstimo, 136–139 D


propriedades de cópia e, 133–135
Construtor de data, 41
padrão de herança padrão, 117–120
depuradores, propriedade de nome e, 59, 60
resultados de herança esperados, 116
padrão de decorador, 151–155
importância de, 115
operador de exclusão,
padrões de herança e, 115 função
12 dependências, declarando, 90–91, 97
klass(), 128–130 padrão de
implantando JavaScript
mix-in e, 135 herança
sobre, 196
prototípica, 130–133 protótipo de
CDN e, 197
aluguel e conjunto, 123–124 protótipos
scripts combinando, 196
de compartilhamento, 124
Expira cabeçalho, 197
construtores temporários, 125–128
minimização e compactação, 197
convenções de
padrões de design
codificação chaves,
sobre, 2, 141
24–26 recuo, 24
decorador, 151–155
espaço em branco,
fábrica, 146–148
26, 27 padrões de codificação, definidos, 2
comentários fachada, 158
iterador, 149–150
processo de minificação, 36
mediador, 167–171
escrita, 30
observador, 171–178
objetos de configuração, 77, 78
proxy, 159–167
objeto de console, 6, 66
singleton, 141–146
instrução const, 109 objeto
estratégia, 155–158
constante, 109, 110 funções
construtoras personalizadas, método dir(), 7
elemento div, 188
42–44 herança e,
fragmentos de documento, 184, 185
116 invocação, 28
coleção de
privacidade e,
formulários de objetos
92 propriedade do
de documento, 16 método
construtor, 4 tag @constructor
getElementById(), 184 método
(YUIDoc), 34 construtores, 53 (veja
getElementsByClassName(), 15 método
também construtores
getElementsByName(), 15 método
específicos) padrão de herança
getElementsByTagName(),
clássico, 120–123, 125– 128
15 coleção de imagens, 16 coleção de links, 16
API DOM, 111
criando objetos de, 41, 100
Script DOM, 183–185
implementando, 104
herança e, 117
convenções de nomenclatura, E
28, 45 redefinindo Padrão ECMAScript
ponteiros, 127 sobre, 5
valores de retorno, 43 padrão convenção de caso de camelo,
sandbox, 101–103 29 objetos nativos,
autoinvocando, 46 esta palavra-chave e, 43, 95 3 herança prototípica, 132 modo
Rede de Distribuição de Conteúdo (CDN), 197 estrito, 5, 46, 77
Crockford, Douglas, 6, 112 notificação por e-mail,
chaves (convenção de codificação), 24–26 36 enumerações, definidas,
Curry, Haskell, 81 17 ambientes, objetos de host e, 5
processo de currying, 81–83 Construtor de erro, 53

210 | Índice
Machine Translated by Google [Link]

método eval() como objetos, 57


evitando, 21–23 propriedade de protótipo, 4, 19
falhas de privacidade e, 93 fornecendo escopo, 58
delegação de eventos, 188 valores de retorno e, 43, 67
padrão de fachada autodefinição, 68
de tratamento de eventos e, visão geral da terminologia, 58
158 padrão de proxy e, 162
script e, 186–187 padrão de G
retorno de
objetos globais
chamada de ouvintes de
acessando, 13
eventos e, 66 script e, 186
definidos, 10
Utilitário de eventos (biblioteca YUI2), 36
padrão de namespace, 88
Expira cabeçalho, 197
esta palavra-chave e, 44
propriedade de janela e, 10, 13
variáveis globais
Padrão de fachada F , acessando, 13
158 padrões de fábrica, 146– definidos, 10
148 loops for, 15–17 elevação e, 14
loops for-in, 17–19 padrão de função imediata e, 70 importando
barras, 51 quadros e para módulos, 101 minimizando, 10–
beacons de imagem, 195 aplicações de 15 namespaces e, 11,
função, 79, 80 88 considerações de
Construtor de função portabilidade, 12 problemas com, 11,
método bind(), 138 12 considerações de escopo,
passando strings para, 22 58 padrão de variável única,
exemplos de uso, 58 13, 14 instrução var e, 12
declarações de função
definidas, 59 Compilador Google Closure, 36, 74
expressões de função versus, 59, 60 içamento compressão gzip, 197
e, 61 ponto e vírgula
e, 59 expressões de
função definidas, 58
Método H hasOwnProperty(), 17
declarações
elementos principais, 200
de função versus, 59, 60 padrão de função
Heilmann, Christian, 96
imediata, 69 ponto e vírgula e, 59 literais
içamento, 14, 61, 62
de função, 59 funções,
objetos de host, 4, 5
62 (veja também tipos
arquivo de configuração htaccess, 197
específicos de
Objeto HTMLCollection, 15, 16
funções) construtores como, 28, 42–44
Fragmentação HTTP, 200–201
processo de currying, 81–83
declaração de variáveis, 10
documentação, 30 içamento
e, 14, 61, 62 imediato, 1 iframes, 195
69–73, 97 gerenciamento de beacons de imagem, 195
escopo, 10 padrão de funções imediatas
memorização, 76 sintaxe alternativa, 70
propriedade de nome, 59, 60 benefícios e uso, 72
convenções de nomenclatura, bookmarklets e, 72 definidos,
28 69

Índice | 211
Machine Translated by Google [Link]

padrão de módulo e, 97 método parse(), 22, 49 método


parâmetros, 70 stringify(), 50
parênteses e, 71 valores de JSON (JSONP com preenchimento), 192–195
retorno, 71, 72 inicialização
imediata de objeto, 73, 74 criação de globais implícita,
12 definidos, 11
Eventos de pressionamento de tecla K ,
declaração var
169, 175–178 função klass(), 128–130
e, 12 importação
de globais em módulos, 101

recuo (convenção de codificação), 24 herança, 115


(veja também herança clássica) reutilização de Inicialização preguiçosa L , 160
código e, 115 técnicas de carregamento preguiçoso,
construtores e, 117 copiando propriedades, 203 coleções de
133–135 propriedades de

comprimento, 16 objetos
de string, 52 bibliotecas

Suporte a JavaScript, 4 padrão de retorno de chamada e,


padrões de mixagem, 67 classes de emulação, 128–130
135 múltiplos, listas, padrão de decorador e, 154–155 padrões literais
122 prototípicos, 130–133
ramificações em tempo de Construtor de matriz, 46–48
inicialização, 74–76 padrões Construtor de objeto, 39–42 expressões
de inicialização, funções imediatas, regulares, 50, 51 evento de carga, 66
69–73 inicialização imediata de objeto, 73, 74 ramificação de
ramificações em tempo de tempo de carga, 74–76 estratégias de
inicialização, 74–76 carregamento (script) sobre, 198
inicialização lenta, 160 operador
instanceof, Fragmentação HTTP, 200–201 técnica
129 instâncias em de carregamento lento, 203 carregamento
fechamentos, 144–146 em sob demanda, 203–205 pré-carregamento
propriedades estáticas, 143 padrão de iterador, 149–150 de JavaScript, 205, 206 elemento de script,
199, 201–202 variáveis locais, 58 método
log(), 7 script de longa

execução sobre, 189


Utilitário J javadoc, 30
método setTimeout(), 189
Classes de linguagem
trabalhadores
JavaScript e, 4
da web e, 190 padrão de mediador de
considerações de implantação, 196–198 orientado a
acoplamento fraco e, 168 padrão
objetos, 3 pré-
de observador e, 171
carregamento, 205, 206
padrão de
encadeamento jQuery, 111
método parseJSON(), 50
Kit de ferramentas JSDoc, 30 M
Ferramenta Martin, Robert, 111 padrão
JSLint de mediador, 167–171 padrão de
sobre, 6 padrões de função imediata, 70 memorização, 76, 105 tag @method
recomendações de uso, 37 (YUIDoc), 33 método method(), 112
JSON (JavaScript Object Notation) sobre, 49 métodos

212 | Índice
Machine Translated by Google [Link]

empréstimo, 136–139 padrão de fábrica e, tipo de


padrão de encadeamento, valor numérico 148, 52
110 definido, 3,
39 privado, 92–97
privilegiado, 93, 97
Constantes de objeto O , 109, 110
propriedade de protótipo e, 95
Construtor de objetos
público, 96, 105–107
criando objetos, 41
padrão de revelação e, 96
padrão de fábrica e, 148
estático, 105–108
método toString(), 48
objetos de string, 52
considerações de uso, 41, 42
objetos wrapper, 52
padrões de criação de objetos
processo de minificação
padrão de encadeamento, 110–
sobre, 36
111 declaração de dependências, 90–
implantação e, 197
91 método method(), 112
ferramentas de suporte,
padrão de módulo, 97–101
91 padrão de mixagem,
namespaces, 87–90
135 padrão de módulo, 97–101
módulos constantes de objeto, 109, 110
propriedades e métodos privados, 92–97 padrão
adicionando,
de revelação, 96, 99 padrão
103 criando construtores, 100
sandbox, 88, 101–105 membros
declarando dependências, 90, 91, 97
estáticos, 105–108 literais de
definindo, 97
objeto sobre, 39–
importando globais para, 101
42 privacidade
herança múltipla, 122
e, 94, 95 linguagens
orientadas a objetos, 3 objetos, 4
(veja
Propriedade de nome N , 59, também protótipos)
60 expressões de função nomeadas, 58 métodos de empréstimo, 136–139
tag @namespace (YUIDoc), 33 configuração, 77, 78
funcionalidade criação, 4
de namespaces, 87 criação de construtores, 41, 100 definido, 3
função de propósito geral, 89, 97 variáveis erro, 53
globais e, 11, 88 padrão de módulo funções
e, 97 padrões de criação de como, 57 global, 10,
objeto, 87–90 desvantagens do padrão, 13, 44, 88 host, 4, 5
101 convenções de inicialização
nomenclatura padrões imediata, 73, 74 método init(), 73, 74
adicionais, 29 para modificando, 3 nativo, 3,
construtores, 28, 45 palavras 39 padrão de
de separação, 28 objetos criação preferido,
nativos, 3, 39 novas 41 wrapper primitivo, 52 membros
classes de privados, 92, 97 cópias
operadores e, 115 rasas, 133 string, 52 lançando,
imposição de padrões, 44–46 53 tipos de, 3 padrão de
padrão singleton e, 142 notificação, observador
e-mail, 36 tipo de valor nulo, sobre, 171
52
Construtor de números

criando objetos wrapper, 52

Índice | 213
Machine Translated by Google [Link]

exemplo de jogo de pressionamento de propriedade de protótipo e, 95


tecla, 175–178 exemplo de assinatura de revista, 171–174 estáticos, 105–108
atributo onclick, 186 padrões objetos de string, 52
de otimização, 74–76 objetos wrapper, 52 tag
@property (YUIDoc), 34 herança
prototípica, 130–133 propriedade de
protótipo adicionando
Tag P @param (YUIDoc), 33
propriedades e métodos, 95 aumentando, 19
propriedades _parent_ (Mozilla Rhino), 93 funções
definido, 4 padrão
de retorno de
de herança
chamada de parênteses e, 63
e, 117–119 construtores temporários e, 125
funções imediatas e, 71 método
protótipos definidos, 4 filtrando propriedades,
parseInt(), 23 aplicações
17 privacidade
parciais, 80–81 padrões, 62 (veja
e, 95 padrão
também
de aluguel e conjunto, 123–124
padrões específicos) definidos, 1
compartilhamento,
impondo
124 construtores de proxy, 127 funções
novos, 44–46 identificando,
de proxy, 127
2 revisões por
padrão de proxy sobre, 159
pares (desenvolvimento de software), 35 padrões de
considerações de cache,
desempenho
167 exemplo de
memorização, 76, 105
uso, 160–166
funções autodefinidas, 68 pré-
padrão de revelação de métodos
carregamento de JavaScript, 205, 206
públicos e, 96 estáticos, 105–
objetos wrapper primitivos, 52 funções
107 membros
construtoras de
estáticos públicos, 105–107
padrões de privacidade e, 92 literais
de objeto e, 94, 95 falhas de
privacidade, 93, 94 membros
privados, 92 métodos
privilegiados, 93 protótipos e, R
95 membros privados Construtor RegExp, 50
implementando, 92 notações literais de expressão regular, 51 scripts
padrão de módulo e, remotos sobre, 190
97 padrão de revelação e, 96 quadros e
estático, 107, 108 métodos beacons de imagem, 195
privilegiados JSONP e, 192–195
definidos, 93 padrão de Objeto XMLHttpRequest e, 191, 192 tag @return
módulo e, 97 (YUIDoc), 33 funções construtoras
aprimoramento progressivo, de valores de
181, 201 propriedades definidas, 3, 39 retorno, 43 funções como, 67
operador funções imediatas,
delete e, 12 71, 72 padrão de função de retorno,
objetos de erro, 53 filtragem, 67 padrão de revelação, 96, 99
17 herança por cópia,
133–135
memorização de, 76 padrão mix-in, 135
privado, 92–97
Padrão de caixa de areia S , 88, 101–105
Schönfinkel, Moisés, 81

214 | Índice
Machine Translated by Google [Link]

processo de schönfinkelização, 81–83 membros estáticos


anexação de definido, 105
elementos de script, privado, 107, 108
202 atributos comuns, 198, 201 público, 105–107
dinâmico, 201–202 propriedades estáticas, instâncias em, 143
funcionalidade, 199 padrão de estratégia, 155–158
exemplo de uso, 193 argumentos
eventos de de modo estrito. propriedade do chamador,
script do navegador, 185–189 46, 77
combinação, 196 definido, 5 construtor de string,
considerações de implantação, 196–198 41, 52 tipo de valor de
DOM, 183–185 string, 52 instrução switch,
estratégias de carregamento, 198– 20 construtor SyntaxError, 53
206 de longa duração, 189–
190 remoto, 190–196 T
separação de preocupações, 181, 182
construtores temporários, 127 essa
segurança, método eval() e, 21 funções
variável, 45 esta
autodefinidas, 68 expressões de
função de palavra-chave
métodos emprestados e, 137
ponto e vírgula e, 59
métodos de retorno de chamada
Mecanismo de inserção de JavaScript, 26
e, 65 exemplo de
separação de preocupações, 181, 182
codificação, 13 construtores e,
método setInterval(), 22 método
43, 95 objetos vazios e, 42
setTimeout(), 22, 189 padrão singleton
objetos globais e, 44
sobre, 141 instâncias
instrução throw, 53 jogo
em
da velha, 193–195 tempos limite
fechamentos, 144–146 instâncias em
propriedades estáticas, 143 novo operador
padrão de retorno de chamada
e, 142
e, 66 scripts e, 189 tag
sniffing, agente de usuário,
@type (YUIDoc), 34
182 desenvolvimento de
Construtor TypeError, 53 operador
software aumentando propriedade de
typeof, 48
protótipo, 19 convenções de
codificação, 23–27
loops for, 15–17 loops
for-in, 17–19 processo de Tipos de valores indefinidos U , 52
minificação, 36 minimizando
globais, 10–15 convenções de
nomenclatura, 28–30
Padrão de
método parseInt(), 23
estratégia de validação V e, 155
revisões por pares e,
exemplos de uso, 156–158
35 executando JSLint, 37 separação de
declaração var
interesses, 181, 182
operador delete e, 12 loops
instruções switch, 20 conversão de
for e, 16 variáveis
tipos de variáveis, 21–23 escrevendo
globais e, 12 considerações
documentação de API, 30–
de içamento, 14 globais implícitos
34 escrevendo comentários, 30
e, 12 recomendações, 11
escrevendo código sustentável,
padrão único, 13, 14
9, 10 sistema de controle de origem, 36 notação de colchetes, 21

Índice | 215
Machine Translated by Google [Link]

variáveis, 101
(ver também variáveis globais)
declarando, 11
definidas, 10
definindo, 3
elevando, 14, 61
local, 58
convenções de nomenclatura,
28 considerações de escopo,
58 conversão de tipos, 21–23

Trabalhadores da
web W , 190 espaços em branco (convenção de
codificação), 26, 27, 36 curingas, padrão sandbox
e, 102, 105 propriedade de
janela, 10, 13 objetos wrapper, 52

X
Objeto XMLHttpRequest, 191, 192

Variável global Y YAHOO (YUI2), 90 Yahoo!


YUICompressor, 36 Serviço web
YQL, 164 Biblioteca YUI
(Yahoo! User Interface), 31, 127 Ferramenta YUIDoc,
30, 31–34

Z
Zakas, Nicholas, 16

216 | Índice
Machine Translated by Google [Link]

Sobre o autor
Stoyan Stefanov é desenvolvedor web do Yahoo!, autor de livros (JavaScript Orientado a Objetos),
colaborador de livros (Sites Ainda Mais Rápidos, JavaScript de Alto Desempenho) e revisor técnico
(JavaScript: As Partes Boas, Mashups de PHP). Ele palestra regularmente sobre JavaScript, PHP e outros
tópicos de desenvolvimento web em conferências e em seu blog (http:// [Link]). Stoyan é o
criador da ferramenta de otimização de imagens [Link] e arquiteto da ferramenta de otimização de
desempenho do Yahoo, YSlow 2.0.

Colofão: O

animal na capa de JavaScript Patterns é uma perdiz europeia (Perdix per-dix), também chamada de perdiz cinzenta,
perdiz inglesa, perdiz húngara ou perdiz boêmia. Esta ave, de ampla distribuição, é nativa da Europa e da Ásia
Ocidental, mas foi introduzida na América do Norte e agora é comum em algumas partes do sul do Canadá e do norte
dos Estados Unidos.

As perdizes são membros da família dos faisões, Phasianidae. São nidificadores terrestres não migratórios
que se alimentam principalmente de grãos e sementes. Originalmente residentes em pastagens, adaptaram-
se e se espalharam com a agricultura humana; atualmente, são mais frequentemente encontradas perto de
campos cultivados.

As perdizes europeias são aves rotundas, semelhantes a galinhas (cerca de 30 centímetros de comprimento),
com pescoço e cauda curtos. Possuem dorso marrom, ventre cinza (com uma mancha castanho-escura na
barriga), focinho cor de ferrugem e bico e patas opacos. Suas ninhadas, compostas de 15 a 20 ovos, estão
entre as maiores de todas as aves. Amplamente introduzidas como aves de caça, as perdizes foram
extensivamente caçadas no final do século XIX e início do século XX.

O nome científico do pássaro vem de Pérdice, da mitologia grega, sobrinho do inventor Dédalo. Dédalo
tinha ciúmes de seu jovem aluno — a quem se atribui a invenção da serra, do cinzel, do compasso
geométrico e da roda de oleiro — e aproveitou a oportunidade para expulsá-lo da Acrópole. Atena,
compadecida com o menino inteligente, veio em seu socorro e o transformou em uma perdiz, uma ave que
evita alturas e prefere fazer ninhos no chão.

A imagem da capa é da Johnson's Natural History. A fonte da capa é Adobe ITC Ga-ramond. A fonte do
texto é Linotype Birka; a fonte do título é Adobe Myriad Condensed; e a fonte do código é
TheSansMonoCondensed, da LucasFont.
Machine Translated by Google [Link]
<[Link]>
eBook
Baixe
Wow!
do

Você também pode gostar