Professional Documents
Culture Documents
Objetos
Arrays e Funcões
Conceitos Importantes
Tony de Araujo, Technical Writer
New Jersey, Janeiro 2014
copyright © 2013 Tony de Araujo
All Rights Reserved
Sendo este livro em formato eBook e com hiperligações para ficheiros onde se encontram versões
reais dos exercícios aqui demonstrados, o leitor tirará mais proveito se ler o livro no seu
computador. Amazon tem vários aplicativos gratuitos que são bastante práticos para esse efeito.
Carregue no link indicado, selecione o leitor mais apropriado para o seu tipo de computador e
baixe o aplicativo.
"Excelência não é técnica, é atitude."
-- Soren Kierkegaard
A todos os professores e autores de quem aprendi, a minha gratitude.
À minha família, estudantes e amigos por este mundo afora,
um Grande obrigado!
INDEX
INDEX
Prefácio
Nota do Autor
O Conceito de Memória
Primeira Revisão
O Intocável Object
Objetos Nativos
Objetos Hospedados, Hosted Objects
O que é uma propriedade?
O que é um variável?
O que é um contexto de execução?
Segunda Revisão
PARTE 2
Em Conclusão
APPENDIX
Resources
Errata and Contact
Copyright
Prefácio
JavaScript, Objetos Arrays e Funções vem a preencher um vácuo entre as publicações
desenhadas para ensinar sintaxe e livros sobre implementações especificas da linguagem
ECMAScript. Este método compreensivo cobre o básico fundamental de Objetos em JavaScript
sem pedir a alguém que memorize construções que raramente serão usadas no mundo real de
desenvolvimento em JavaScript. O leitor navegará muito naturalmente pela anatomia de objetos
JavaScript dominando os conceitos sem grande esforço. O único prerequisito é a vontade e
disciplina de ler e fazer os exercícios em cada tópico.
JavaScript, Objetos Arrays e Funções é mais sobre conceitos do que sobre implementação de
scripts exóticos que poderão perder a atenção do leitor antes que ele tenha a chance de dominar a
essência do tópico. E porque JavaScript é uma língua complexa (mas não complicada), o autor
decidiu simplificar (sem diminuir) certos conceitos em áreas que poderão ser novas para uma
grande maioria de leitores. Um exemplo é o de closures. O autor ataca o tema de closures de uma
forma minimalista mas ao mesmo tempo profunda, com ilustrações e scripts simplificados para
que o conceito não se perca na complexidade da codificação em si.
A intenção do autor é a de apresentar este trabalho da maneira como gostaria de ter tido quando
iniciou os seus estudos em JavaScript. Este livro não é para principiantes porque não cobre regras
de sintaxe. No entanto, se o leitor já foi exposto ao básico de JavaScript em outro sitio, não terá
dificuldades em compreender o material aqui exposto. Por outro lado, se já tem experiência em
outra língua, ou se apenas necessita uma relembrança, haverá sempre uma explicação linha por
linha dos scripts aqui apresentados.
Cada tópico é mantido curto de propósito, mas aqui encontrará informação que geralmente não
aparece em livros introdutórios e muitas vezes nem sequer em livros mais avançados. O autor
assume que qualquer pessoa com interesse suficiente para aprender JavaScript tem a capacidade
de o fazer se lhe derem tal oportunidade.
O livro não foi só escrito para novos desenvolvedores. É também dirigido aqueles que já estão
envolvidos com JavaScript, mas que não têm tempo para andar procurando as razões porque
certos mecanismos de JavaScript funcionam da maneira como funcionam. Este livro é o resultado
de muitas centenas de horas de investigação e teste da parte do autor, com o intuído de oferecer a
cada leitor algo importante que lhe retribua pelo tempo despendido na sua leitura.
JavaScript, Objetos Arrays e Funções dar-lhe-á a fundação básica necessária que lhe permitirá
avançar para livros mais complexos. Abrirá muitas portas de compreensão sem insultar ou julgar
a inteligência de cada um.
Bem vindo à primeira edição de JavaScript, Objetos Arrays e Funções.
Nota do Autor
Estimado leitor, obrigado pelo tempo despendido na investigação deste livro. Com tantos livros
por ler, é realmente uma honra e uma experiência singela para um autor saber-se ser lido. Pode ter
a certeza de que trabalharei duro na escrita deste projeto para poder merecer tal escolha.
Este livro é pratico e requer as mangas da camisa arregaçadas. Teoria e laboratório juntam-se
para trazer conceitos a uma compreensão máxima. O livro poderá ser uma leitura bem rápida,
pode também ser utilizado como referencia, ou então pode ser executado. Eu sugiro esta ultima
opção. Depois, mais tarde poderá então regressar para ler ou investigar ao acaso.
Com a exceção de scripts de linha singular, todos os outros scripts terão um link para uma copia
raw no meu servidor. Assim poderá copiar e colocar no Console se não desejar codificar à mão.
A escolha será sua.
O livro explica a execução dos novos métodos disponíveis para cada objeto original do
JavaScript. E se desejar criar seus próprios objetos e métodos, haverão também vários capítulos
que abordam esse tema num sentido mais aplicativo do que teórico, limitando o tópico apenas ao
que funciona deixando outras teorias de lado. Despendi muitos dias meditando em cada tópico de
forma a o trazer à escrita de maneira que fosse entendido por alguém que não venha de outra
linguagem semelhante. É com certeza o mínimo que posso fazer por quem investe o seu tempo
lendo este trabalho.
Objetos Nativos (os que vêm originalmente com JavaScript) são os mais utilizados na
implementação da língua. Tendo sido programados em formato mais chegado à maquina, eles são
mais rápidos e mais bem reconhecidos pelos browsers modernos. No entanto, muitos autores
decidem ensinar a criação de novos objetos logo desde o principio, confundindo o leitor com
terminologia e conceitos que o estudante raramente implementará em JavaScript na sua vida real.
Penso que esta maneira de escrever imita a maneira como outras linguagens são ensinadas. Mas
JavaScript não é outra língua. JavaScript é uma combinação de ECMAScript com a mistura do
ambiente onde se encontra ativo. É uma língua mutante: porta-se de maneira diferente
dependendo de como e onde está sendo utilizada. Até o Objeto Global não é acessível na sua
forma original; Ele se torna o interface entre ECKMAScript e a plataforma anfitriã, como por
exemplo o browser. No browser , o Objeto Global transforma-se no objeto window e é assim que
ele tem que ser endereçado.
Neste livro, conceitos virão em camadas. Dizem que JavaScript é uma língua fácil. Mas ela não é
tão fácil como parece, por vezes até é bem complexa. Complexa no sentido em que reside no seu
próprio mundo. A lenda diz que Brendan Eich, o inventor de JavaScript, queria construir uma
língua do tipo Scheme (um dialeto da Lisp), mas a realidade bateu à porta e ele teve que colocar
uma capa na língua para que a sintaxe se parecesse com C/Java a fim de poder acomodar os
milhares de programadores já existentes. Na publicação oficial da ECMAScript 5 diz assim:
“Algumas das facetas de ECMAScript são semelhantes aquelas utilizadas por outras línguas, em
particular Java™, Self, e Scheme”
Hoje, muitos programadores odeiam a língua pelo que ela não é; mas depois de conhecerem
JavaScript mais a fundo, eles amam a língua pelo que ela é. Eu certamente que a aprecio bastante
porque me surpreende todos os dias de forma muito positiva.
Obrigado pela oportunidade,
Tony de Araujo
- Technical writer – New Jersey, 2014
Representação de Cores Neste Livro
· LARANJA - Novo termo ou elemento: anotar mentalmente.
· CASTANHO - Termo já introduzido.
· VERDE - Nova ação a tomar, geralmente é aplicado a código que tem que ser escrito
pelo leitor: Focar na ação.
· AZUL - Azul representa hiperligação (externa ou interna) para leitura relacionada.
· AZUL - O Azul é também utilizado para anotações relacionadas com o tópico e serve
para chamar atenção.
· Vermelho – Aplicado a termos importantes. Serve apenas para quebrar a monotonia.
JavaScript é uma língua orientada a objetos mas baseada em protótipos. Não é uma língua
baseada em classes.
Ambas as línguas, as baseadas em protótipos ou as baseadas em classes têm um propósito comum:
a partilha de código para evitar redundância.
Nas linguagens baseadas em classes o programador cria um blueprint e a partir daí nascem
objetos baseados em tal diagrama.
Na linguagem baseada em protótipos o programador constrói um objeto e permite que esse
objeto se torne o protótipo de objetos futuros. Evita assim a repetição de código tornando a
execução mais eficiente. A partir deste ponto, a semelhança entre protótipos e classes começa a
perder validade pois as diferenças vão aumentando. E como o JavaScript permite a lincagem de
objetos entre eles numa herança prototipa, métodos podem ser reusados formando uma cadeia
associativa. Quando um objeto necessita de um método, começa procurando nele mesmo, e se não
encontra, vai subindo a cadeia associativa de protótipo em protótipo até encontrar o método que
busca. O primeiro método que encontrar com o nome associado à pesquisa será o que irá utilizar.
É importante manter este conceito em mente para evitar a chamada de métodos errados.
Este modelo sem classes, conhecido também como instance-based porque é baseado na criação
de instâncias do modelo mãe, funciona num processo conhecido como delegação, ou seja, um
objeto dependendo do processo metódico de outro objeto para criar sua própria funcionalidade.
Este paradigma resulta porque JavaScript depende do seu ambiente de real-time para fazer sua
magia. Este processamento a tempo-real é como água no rio, nunca é o mesmo quando olhamos
uma segunda vez. Estarmos a par deste ambiente em real-time é importante para compreendermos
como JavaScript funciona no que diz respeito a escopos ou ambientes de trabalho.
Só porque JavaScript pode imitar muitas das técnicas de línguas baseadas em classes utilizando
protótipos, não significa que seja uma linguagem baseada em classes. Alguém disse um dia que
familiaridade rouba as bordas da admiração. Vamos admirar o JavaScript pelo que ele é e não
com quem ele se possa parecer. JavaScript não é Java e nem é Lisp.
Haverá alguma vantagem em implementação prototipa versus línguas baseadas em classes?
Existe um artigo na Wikipedia que expande este assunto pelo que deixo como referencia.
O Conceito de Memória
A gestão de memória ou seja o ato de gerenciar memória, pode ser explicito na sua forma mais
simples como a alocação de blocos de memória e a reciclagem dos objetos que deixam de ter
validade. A reciclagem é essencial para poder dar espaço virtual a outros novos objetos. Esta
reciclagem é de certo modo gerenciada pelo browser e cada modelo de browser tem uma maneira
especifica de o fazer. Se estiver interessado em explorar este assunto para além do escopo do
livro, visite os seguintes links da Wikipedia: Gerenciamento de memória , Coletor de lixo.
Em termos gerais a memoria é subdividida em duas áreas distintas: o stack e o heap.
O stack é uma estrutura de data mais restrita. Somente um número limitado de operações atua
neste campo.
Quando cada operação finaliza, a data é automaticamente retirada para dar espaço a outra. De
certo modo o stack atua como um rascunho de operação de memória. A data aqui apresentada é
bastante curta, como por exemplo, apontadores (nomes de variáveis).
A seção heap é uma outra história. Para começar, no heap, data não é tão bem organizada como é
no stack. Aqui, blocos de data podem ser guardados de um modo ad-hoc porque no heap, se um
bloco de data necessita de ser aumentado, o heap tem permissão para o fazer conforme assim
precisar. Isto faz com que o heap seja relativamente mais vagaroso do que o stack. A memoria do
stack é bem mais rápida porque cada tipo de data tem um tamanho especifico e está sob uma
supervisão estrita.
Por falar em primitivos, o type Undefined é atribuído a qualquer variável que não tenha sido
declarado. Um variável que seja indefinido não tem a propriedade de length porque é exatamente
fixo num só valor chamado undefined. Não se preocupe se necessita de clarificação do que é
length. Falaremos sobre length mais tarde.
O type Null por outro lado serve geralmente como placeholder* para um futuro objeto e deveria
ter sido considerado um objeto em si (valor de referencia), mas houve um erro da parte da
implementação da ECMAScript Standard anterior e hoje continua a ser considerado type
primitivo por razões históricas. Tal e qual undefined, só tem um valor: null.
(*) Placeholer é um termo que significa “marcador de posição”, ou “lugar reservado”. Em programação placeholder refere-se a
símbolos temporários que guardam o espaço para outro símbolo ou data ainda por declarar, ou que será declarada em runtime.
O type Boolean representa uma entidade lógica com dois valores, respectivamente true e false, ou
seja “verdadeiro” e “falso”.
O primitivo String* é um tipo um pouco estranho! Normalmente em outras linguagens Strings são
consideradas valores de referencia mas em JavaScript a string é de type Primitivo.
(*) String significa cordel ou uma corrente. Na linguagem de programação, significa uma
sequencia de caracteres alfanuméricos. Strings são sempre envolvidas em aspas singulares ou
duplas. Mesmo números que estejam envolvidos entre aspas são considerados Strings por
JavaScript e não type Number.
Com é óbvio, strings podem variar em tamanho e então em JavaScript, quando adicionamos
palavras a um string, gera-se um novo string com a combinação do antigo e do novo. O string
anterior fica com uma bandeira assinalando que tem que ser apagado na próxima vez o browser
faz a sua limpeza através do coletor de lixo.
Um string pode ter zero ou mais caracteres (cada caractere é representado por um bloco de 16-
bits). Cada um destes 16-bits é referido por um número de posição. A primeira posição é zero.
Como exemplo, na string “Tony”, a posição do último caractere (y) é o comprimento da palavra
menos 1, ou seja length – 1. E porquê? Porque o length é de 4, mas como começa contando em 0,
a quarta posição é numerada como 3.
Quando um string não contem data é representado apenas por aspas: “ “ . Quando um string
contem data, cada elemento (ou caractere) é considerado um UTF-16 code unit.
Em suma, como será demonstrado mais tarde, JavaScript strings são guardadas no stack por serem
valores primitivos. Se o string se expande, ele é trocado por outro com uma posição de memória
maior e a data anterior é apagada.
Variáveis versus Valores de Referencia
Até agora temos falado em String, Boolean, Null e Undefined, mas nada dissemos sobre Number.
Números serão tocados em mais detalhe conforme vamos desenvolvendo este tema. Basicamente
os valores primitivos são tipos fornecidos pelo sistema JavaScript ou seja, primitivos são tipos
nativos da língua. Vêm já definidos com a ‘máquina”.
A) Constructor – Isto é uma “formula” automática que o novo objeto herda para poder duplicar
objetos semelhantes, através do operador new. Um constructor é uma função que tem certa
programação definida para construir uma replica do objeto. Como por exemplo eu poderia
dizer que joão = new tony( ) e depois joão passaria a ser uma replica de tony. Esta maneira de
criar objetos evita repetição de programação e poupa memória. Um constructor herdado é um
apontador para a fonte dos dados originais que permitem recriar um objeto contendo a mesma
funcionalidade do objeto mãe. Ele não têm função ativa no objeto que o herda, apenas serve
para este objeto poder criar outros objetos. Um construtor é o dom de poder ser mãe. Dá a
futuros objetos o direito de poderem nascer. O constructor tira proveito de um parâmetro
chamado this. “this” é um placeholder genérico que representará especificamente o objeto em
ação, no momento em que este objeto utilizado os métodos e propriedades do objeto mãe.
“this” é como o pronome “cujo”, ou diremos o “dito-cujo” embora se traduza como “este”.
Onde programarmos “this” como por exemplo this.livroTitulo, “this” será substituído pelo
título do livro a que me refiro quando JavaScript fizer a operação a que o chamamos. Isto
permite criar scripts genéricos que depois se poderão aplicar a vários objetos e não apenas a
um só. Tocaremos neste assunto frequentemente em outras ocasiões.
B) Prototype – Outra propriedade que um novo objeto herda é Prototype. Prototype é um
interface que atua como uma corda umbilical onde os vários objetos podem consultar e utilizar
os vários métodos e propriedades disponíveis vindos de objetos superiores, ou seja, aqueles
de quem o objeto presente foi modelado. ‘Protos” or pai, é a fonte de quem o objeto corrente
herdou sua funcionalidade, algures na cadeia prototipa superior. A utilidade deste link vai para
além do objeto corrente no sentido em que permite a outros objetos mais abaixo, o de poderem
utilizar os recursos de objetos acima, incluindo o objeto atual. Tal e qual o constructor, o link
prototype funciona apenas em uma direção, isto é, pode-se receber mas não modificar a
funcionalidade que vem de cima. Podemos também pensar que esta propriedade prototype é
uma lista de toda a funcionalidade que o objeto tem autorização de poder utilizar. Todos os
novos instantes de um objeto herdam esta lista de funcionalidade. Teremos oportunidade de
trabalhar com a propriedade prototype nos nossos exercícios de laboratório.
Figure 2
2. Métodos: No nosso exemplo anterior, tony herda também “aponta para” métodos
provenientes do objeto protos (seu pai ou mãe) que é o objeto “Object” . O objeto global
se chama Object com o maiúsculo. Não confundir este nome com o nome genérico objeto.
Ele é um objeto nativo, tal e qual Array, String (sim também há um objeto String), etc, mas no
fundo Object é superior a todos estes outros porque estes herdam funcionalidade de
Object que depois modificam para poderem dar suas própria entidades (seu type) a seus
filhos. Introduzirei esses outros objetos mais à frente.
O métodos que um new Object herda são os seguintes:
(Não memorize. Cobriremos este assunto naturalmente ao longo do caminho. Tente apenas compreender. Neste momento estamos
simplesmente enumerando as propriedades e métodos que são herdadas automaticamente quando se cria um novo objeto e servirá
para referencia.)
e) valueOf() - Tradução: valorDe. O valor que é retornado através deste método, é um valor
primitivo. JavaScript devolve o valor primitivo de um determinado objeto, isto é, se x =
[1,2,3], valueof() mostrará [1,2,3]; se y = "tony". valueOf() devolve "tony", etc. A sintaxe é
meuObjeto.valueOf();. JavaScript utiliza este método internamente conforme necessita, mas
também o podemos chamar se necessário (e chamaremos).
Imagine isto: Uma propriedade de um objeto pode ser ilustrada como um variável que está
ligado ao objeto. Uma propriedade é o variável e o valor juntos. cor = "azul" é uma propriedade
chamada cor.
Então, no nosso primeiro exemplo, será tony o objeto em si?
Vamos agora experimentar com typeof. Observe cada resultado e tente deduzir as razões porque o
resultado é assim mesmo. O operador typeof devolve um string indicando o tipo (type) do operando.
Nota preliminar: o ponto e vírgula “;” termina uma expressão. Vá verificando como ele é aplicado.
O “//” significa “comentário”. Tudo o que ler à direita de um // não é para escrever no Console.
Mesmo se escrevesse, JavaScript não processaria a linha porque sabe que // significa comentário.
Escreva o seguinte código (em baixo a verde) no seu Console e clique no botão Enter (o botão de mudança de
linha). Preste mais atenção aos exercícios 9 a 15:
1- typeof 1; (Nota: O console tenta acabar a palavra typeof automaticamente. Se quiser autorizar que o faça, carregue na
tecla de final de linha e o cursor passará para o final da palavra poupando-lhe algum trabalho).
2- typeof x;
// O resultado é “undefined”. x não existe e por isso é indefenido.
3- typeof tony;
// O resultado é “undefined”. JavaScript não brinca em ação!
4- typeof "tony";
// O resultado é “string”. Ah agora sim, estamos falando o lingo de JavaScript! Um texto entre aspas é um string para o JavaScript.
Uma palavra sem aspas seria um símbolo já declarado. Com no teste 4 tony não foi oficialmente declarado, JavaScript atribui-lhe o type
de undefined.
5- typeof true;
// O resultado é “boolean”. true é uma palavra reservada e não pode ser utilizada como símbolo.
6- typeof "true";
// O resultado é “string” Saberá porquê? Embora o termo true seja reservado, “true” em aspas pode ser utilizado como um string.
Mas sem aspas seria um símbolo. true e false não podem ser símbolos.
7- var y = 123; // Isto é uma declaração de um variável (y). O prefixo var inicia a posição de escopo de y e y por sua vez
recebe a atribuição de 123. Mais sobre isto à frente.
8- typeof y;
// O resultado é “number”.
10- typeof z;
// O resultado é “object”. z aponta para um objeto no heap.
11- var b = z; // Definimos b e atribuímos-lhe z. O operador = funciona no sentido direita para esquerda.
12- typeof b;
// O resultado é “object”. Agora ambos z e b apontam para o mesmo objeto. Vamos agora atribuir outro valor a z:
14- typeof z;
// O resultado agora é “number”. Z já não aponta para um objeto. Agora tem em sua possessão no stack, um primitivo (123).
15- typeof b;
// O resultado é ainda “object”. O objeto ainda existe porque ainda tem um símbolo apontando para ele.
No exercício 9 declaramos z como sendo um novo Object (isto é apontando z para um objeto).
Repare como no test 9 o O de Object é em maiúsculo. Todos os objetos nativos de JavaScript
começam com uma letra capital. String e string não são o mesmo símbolo. JavaScript é sensitivo para
com letras minúsculas e maiúsculas. Isto é importante manter em mente. Voltando ao nosso z, z é um
variável no stack que aponta para um objeto no heap, certo?
No exercício 11 declaramos o variável b e atribuímos-lhe o mesmo valor que z. Então b é agora um
símbolo que aponta para o mesmo objeto que z.
Em 13 nós mudamos de ideia e apontamos o z para o número 123. O z é agora de type “number’.
Deixou de ser um objeto.
No exercício 15 verificamos que b continua a apontar para o objeto que lhe foi atribuído
anteriormente por z. Isto faz-nos reparar que quando atribuímos um valor de um variável a outro, é
uma transação única. Tal como dizer “Amigo aponte sua pontaria para o mesmo objeto onde estou
apontando mas não se meta no meu caminho. Você é você e eu sou eu!” Se um dos variáveis é
depois atribuído a outro valor, o segundo variável não o segue. Mas por outro lado, se algum deste
variáveis modificar o objeto para onde mutuamente apontam, a modificação é refletida em ambos os
variáveis.
Vamos supor que reatribuímos ao variávle b (agora o único apontador para o objeto) um outro valor diferente,
como por exemplo b=345; Agora o type de b é também um number e o objeto fica sem apontador
que o liga ao stack. Qual será o futuro do objeto que se encontra no heap?
Resposta: O objeto fica com a bandeirinha assinalando que necessita de ser processado pelo coletor
de lixo e eventualmente será apagado quando o browser fizer a próxima limpeza.
Lab 2
Vamos ligar o nosso Console de JavaScript favorito para experimentar mais codificação (estou
usando o Chrome da Google):
NOTE: porque cada símbolo representativo (como por exemplo “cor”:) é tecnicamente um string, é
também uma boa prática escreve-los dentro de aspas na altura da declaração. Use de preferência
aspas dobradas porque, embora aspas singulares também funcionem, mais tarde quando você
programar em JSon, terá que utilizar aspas em dobro. Por isso é melhor criar já esse hábito. Não as
incluí agora nesta versão para poder simplificar, mas de futuro, apontadores de propriedades como
os nossos exemplos “cor”e “tamanho” serão escritos com aspas na sua declaração.
Tudo depende das nossas intenções ao programar e também das nossas preferências de sintaxe.
Sintax de colchetes requer a aspas à volta do nome da chave.
Resumo de ideias
Pense em variáveis como símbolos apontando para uma certa data.
Pense em stack apontando para o heap.
E pense também em pares associativos de chave-valor residindo no heap e atados ao
stack por um “cordel” que serve de apontador. Se não existe um cordel ou apontador,
não existirá nenhum chave-valor no heap. Tal e qual uma moeda, sem cara não existe
coroa.
Um variável a quem não tenha sido atribuído um valor, toma automaticamente o valor de undefined.
Mas ele continua a existir. Se quisermos realmente apagar este variável teremos que lhe atribuir o
valor de null quando já não necessitarmos do mesmo. Para verificar se um variável tem alguma
atribuição ou não, podemos utilizar o typeof. Mais tarde neste livro utilizaremos typeof outra vez
para verificar se um objeto foi instanciado. Continue sua leitura sem memorizar nada, apenas
compreenda e pratique os exemplos dados.
Pense em JavaScript como uma coleção de objetos herdando funcionalidade de outros objetos com
quem eles fazem interface.
Novos objetos podem também anular ou readaptar as propriedades e métodos originais, e além disso
podem até inventar outros novos métodos e propriedades.
Estas novas propriedades podem ser transmitidas para novos objetos que sejam hierarquicamente
inferiores ao objeto construindo a nova propriedade. Isto acontece via da propriedade de prototype
que este objeto delega ao outro.
Por falar em propriedade prototype, através dela podemos também adicionar diretamente nova
funcionalidade ao objeto mãe, incluindo aos objetos nativos se bem que nestes acontecerá apenas de
modo temporário e aplicado somente à execução atual. Veremos como isso funciona adiante.
11- estante; // resultado é Object {livros: 21, CDs: 57, Magazines: 19}.
13- estante;
//result: Object {books: 21, CDs: 57, Magazines: 19, audioBooks: 11}
Como vê, um variável pode apontar para uma coleção de data em formato de pares chave-valor,
formando um objeto. No entanto, mesmo até strings regulares, embora sejam valores primitivos,
herdam métodos e propriedades do objeto Global assim como do objeto equivalente a seu type.
Repare nos seguintes exemplos:
15- typeof x; // resultado é “string” mas repare em seguida que podemos utilizar métodos e propriedades de objeto:
Aprenderemos mais sobre estes métodos e propriedades na segunda parte deste livro. No entanto
para quem não está familiarizado com substring() aqui vai alguma informação preliminar: o
parâmetro 0 representa o primeiro caractere a ser incluído no resultado. Lembre-se que em
JavaScript a contagem começa em zero e não em um. O segundo parâmetro 3 representa o primeiro
caractere a ser excluído do resultado.
Note o ponto e vírgula no final. Isto é uma definição de função. Define o variável com nome de
mais100 como apontador para um objeto type function, que por sua vez adiciona 100 a qualquer
número introduzido em lugar do parâmetro1. (Nós ainda não passamos nenhum argumento (data)
para a função).
Funções são como caixas fechadas onde grupos de código coexistem para exercer uma determinada
função. Geralmente uma função tem um input e um output. O que entra é modificado, e o que sai é o
produto da modificação. Funções são bastante úteis para evitar redundância porque reutilizam o
mesmo script cada vez que são chamadas ao trabalho. As funções delegam seu código sempre que as
chamam. Soa familiar? Funções são objetos e por outro lado, objetos são geralmente coleções de
funções. Oh, será que objetos são coleções de outros objetos? Mas é verdade mesmo porque em
JavaScript tudo é objeto.
Uma coleção de funções desenhada para ter uma certa funcionalidade específica sob o comando de
um objeto, é chamada método. Por vezes um método só tem uma função, outras vezes um método tem
várias funções a seu dispor que fazem parte deste mesmo método. Por isso, não confundir métodos e
funções embora por vezes se misturem os termos. Eles são relacionados mas não são a mesma coisa:
O sentido semântico é diferente.
O script acima é representado da mesma forma que quando se declara um variável. O que quero dizer
com isto é que não existe valor no variável em si. O seu propósito é o de apontar para a função
(variável apontando para a função). Funciona tal e qual qualquer declaração de um variável, tal
comol var x; funcionaria. O browser manterá o símbolo mais100 como referencia, mas o
interpretador só atuará quando chamarmos a função, que se faz desta forma: mais100(3); (3) é o
argumento que passará (por cópia) para a função através do parametro1 que serve de interface. O
browser mantém depois controle desta execução como única e independente de outras execuções.
2- Vamos ao console (tenha a certeza de ter completado o primeiro exercício):
Por outro lado nós passamos data para fora da função através do mecanismo return. O return atua
também com um break (travão) que desativa a função uma vez que o return seja processado. return é
a última ação que acontece dentro de uma função. Se houver algum script por debaixo do return, a
função nunca lá chegará porque o interpretador já saiu da função.
Uma vez que return seja processado a função é destruída pelo browser e todos os variáveis dentro da
função são também destruídos, a não ser que tenham um apontador ainda ativado algures no script
(falaremos sobre isso um pouco mais adiante).
Funções podem ser chamadas do exterior (como nós fizemos com mais100(3)), ou podem ser
chamadas do interior, do seu próprio corpo (e isso chama-se recursão). Em JavaScript as funções
são objetos (mas você já sabia disso, certo?). Elas podem ser utilizadas em todo o sítio tal e qual objetos.
Podemos atribuir-lhes variáveis ou até incluir funções dentro de outros scripts. Funções em
JavaScript adaptam-se muito bem à situação onde são necessárias.
Em programas longos, grandes consumidores de memória, quando uma função acaba o seu processo e
sabemos que não irá ser reativada outra vez, devemos de-referenciar o seu apontador, neste caso, o
mais100. Quando de-referenciamos (tiramos a referencia) o mais100 é posto no coletor de lixo. Se
não de-referenciarmos o mais100, ele ficará no sistema o tempo inteiro da programação global,
diminuído o tamanho da memória aplicada ao browser, e isto pode aumentar com a adição de outros
variáveis não de-referenciados. De-referenciar significa remover a referencia através da atribuição
de "null“ ao variável.
5- E como de-referenciamos? Applicando-lhe Null:
mais100 = null;.
(Note: Depois de de-referenciar mais100 para null, se você verificar com typeof, verá que mais100 é agora um object . Null é um
objeto especial (Lembre-se da história porque foi considerado um objeto. Foi um erro que ficou sem mudança (capitulo 1)). De
qualquer modo, JavaScript apagará mais100 na próxima limpeza geral do browser porque o valor null inica que aquilo (o mais100)
não tem significado. Por outro lado a função também deixará de existir e já não pode ser reativada. Antes, a função tinha terminado
e apagado a data mas ainda estava ativa no sentido em que poderíamos chamar a função outra vez aravés do mais100. Mas uma
vez de-referenciado o símbolo que a chama, ela passará também para o coletor de lixo. Se tentar chamar agora a função outra
vez, receberá um erro a vermelho:
mais100(7); resultará em TypeError: object is not a function.
O tópico sobre funções continuará mais à frente num outro capitulo. Vamos continuar descascando
esta cebola.
Funções com Nome vs. Funções Anônimas
A nossa função anterior mais100 da seção Lab 3 é uma função anônima. Isto quer dizer que a
função é atribuída à declaração de um variável, e serve (a função) de expressão (a parte de
programação) do variável declarado.
Deixe explicar…
Figure 5
E vendo-se na figura a cima, variáveis podem ter como expressão, uma função, em vez de um
string. Um exemplo é o exemplo z a cima, um variável com uma função sem nome (anônima).
Quando declaramos um variável ou uma função em JavaScript eles são geralmente hoisted
(içados, pushados para) cima no topo do scope pelo interpretador. Esta lista no cimo é como um
menu que lista todos os variáveis ou seja apontadores, mas só as declarações. É como um índex
do que há disponível. Só a declaração é hoisted, não a expressão. Isto pode não fazer diferença
para o programa em si, exceto quando temos que fazer um debugging ao código. Quando
inspecionamos o código, é mais prático distinguir visualmente entre os variáveis e as funções da
lista mostrada no topo do scope. Simplifica a nossa compreensão do que existe no menu.
Eis o problema que temos quando fazemos um debug:
Nos exemplos de cima, a seguinte lista será vista(figurativamente falando) no topo do scope:
x, y( ), z
Note como y se mostra com identificação de função enquanto que z se parece como um simples
variável. Isto acontece porque só a declaração é que é içada para o topo. A parte da expressão é
ignorada até à altura de atividade da mesma. Isto é apenas um contratempo para alguém que queira
fazer uma inspeção, um debug ao código porque a resultado do processamento de JavaScript é
semelhante de uma forma ou de outra.
Para além deste possível contratempo, usar funções anônimas é perfeitamente aceitável e uma
maneira muito poderosa de codificar em JavaScript. Como exemplo, a famosa biblioteca jQuery
tira grande partido de funções anônimas (sem nome).
Primeira Revisão
JavaScript é uma linguagem baseada em protótipos.
Assim como classes, protótipos evitam a repetição de código tornando a capacidade de
memória muito mais eficiente e simplificando a codificação.
A memória de um sistema é subdividida em stack e heap. Stack atua como uma memória
de rascunho para pequena data que é geralmente temporária. Data mais extensa como por
exemplo objetos de todos os tipos, é guardada no heap.
Para acessar objetos, nós criamos apontadores no stack, conhecidos como variáveis. Uma
vez que de-referenciemos um apontador, o objeto, que é um valor de referência, é
colocado pelo interpretador no coletor de lixo. Este sistema de coleção de lixo é operado
pelo browser e cada browser tem a sua própria versão.
BOM - Browser Object Model: é uma coleção de objetos que define a janela do browser e o seu
conteúdo. Através da utilização do BOM, programadores podem customizar funcionalidade que
não é diretamente relacionada com a data do documento em si, como por exemplo mover a
window, ou mudar o texto na barra de status. Infelizmente o BOM não foi feito standard até à
chegada de HTML5 e cada browser usa sua própria implementação. Com o advento de HTML5 a
indústria de browsers está agora a implementar configurações standard e de futuro poderemos
tirar melhor proveito do BOM, mas ainda é cedo para isso.
DOM - Document Object Model: este é um objeto que define o documento quando se mostra no
ecrã. Dom é uma implementação standard e é um objeto dentro do escopo do objeto window ou
Global Object.
Para além de ser utilizado em browsers e servidores, interpretadores de JavaScript são também
embutidos num grande numero de ferramentas digitais. Cada uma destas implementações produz
seu próprio object model, que faz interface com ECMAScript. O base de JavaScript mantém-se
geralmente o mesmo para cada aplicação. O objeto Global toma uma nova forma em cada um
destes casos, servindo de liaison entre a linguagem e seu ambiente anfitrião. Um exemplo de tais
implementações é o Adobe Acrobat e Adobe Reader que suportam JavaScript em ficheiros PDF.
Outras ferramentas que utilizam JavaScript são Photoshop, Illustrator e Dreamweaver. Não
esquecer também Flash que implementa ECMAScript sobe o nome de ActionScript.
Figure 6 – Quando abrimos uma nova janela no browser, a janela pertence ao browser. Estamos utilizando sintaxe do
BOM quando abrimos uma nova janela. O objeto é window. Em window.open("url"); .open é um método que vem do
BOM.
Vamos então tirar mais uma camada da casca desta cebola…
O que é uma propriedade?
Em JavaScript uma propriedade é uma associação entre um nome (o variável) e um dos valores que
pertencem ao objeto em questão, o que significa que propriedades são os blocos de construção de um
objeto.
O que é um variável?
Um variável é uma “propriedade” atuando dentro de um contexto de execução.
O que é um contexto de execução?
Um contexto de execução é um escopo dentro do qual algo é executado. Um variável declarado
dentro de uma função, tem um escopo limitado à função em si, incluindo alguma subfunção que esteja
dentro da mesma (isto é uma subfunção tem acesso aos variáveis da função mãe). No entanto, em termos de contexto
de execução, cada vez que chamamos uma função, o escopo deste variável é diferente das outras
execuções anteriores, nenhuma tem acesso à outra execução. Isto é o que se chama contexto de
execução.
Quando o interpretador se inicia, só existe um contexto de execução, o contexto global. No contexto
global, podemos visualizar o escopo de uma forma estática e mais compreensiva. Depois, cada
execução de função cria seu próprio e único contexto, e como JavaScript só faz uma coisa de cada
vez, a cada momento o contexto de execução é diferente.
Quando JavaScript está sendo utilizado num browser, o contexto global é o objeto window.
Tudo começa com o objeto window (o objeto Object). Depois podemos subdividir os escopos em
funções, que são objetos que agrupam código funcional, que por sua vez também poderão ter os seus
próprios ambientes de escopo, se forem funções dentro de funções. O contexto de execução de uma
função atua cada vez que chamamos (executamos) a função, e depois, quando o programa sai da função,
este contexto é destruído pelo browser.
Quando por exemplo declaramos var x = "tony"; no ambiente de escopo global (do browser), x
torna-se uma propriedade do objeto Global, conhecido neste ambiente de browser como window. x
torna-se uma propriedade de window.
Figure 7
Agora o variável z é uma propriedade da função y e não de window. No entanto não podemos
esquecer que a função y é uma função do objeto global (window).
Permissão de acesso a elementos vem de dentro para fora e não de fora para dentro.
Desenvolveremos este tópico de escopo e contexto mais a fundo em breve.
Segunda Revisão
Valores de referência residem no heap.
Para podermos chamar ou modificar um objeto, teremos que ter um variável no stack
apontando para o objeto no heap.
JavaScript é a composição do seu núcleo (ECMAScript), mais os objetos do ambiente
onde o ECMAScript é implementado.
No ambiente browser, o objeto principal é window. ECMAScript faz o interface com o
DOM (modelo do objeto documento) e o BOM ( modelo do objeto browser).
O Document Object Model (DOM) já está standardizado. O Browser Object Model
(BOM) continua a ser único em cada marca de browser embora isso venha a mudar nos
próximos tempos.
ECMAScript vem com seus próprio objetos nativos. Estes objetos estão sempre
disponíveis. Os principais objetos nativos são o Object Global, Array, Boolean, Date,
function, Math, Number, RegEx e String.
O ambiente onde JavaScript é implementado (tal como no browser) adiciona seus
próprios objetos a que chamamos Objetos Hospedados, ou Hosted Objects. Num
browser, o Object Global transforma-se em objeto window. Depois temos o Document
Object (DOM) e uma coleção de objetos proprietários que fazem o grupo BOM.
Um variável é um nome dado a um apontador e dentro de um escopo específico.
Uma propriedade é um variável + a data para onde este variável aponta.
Ao contrário de algumas linguagens populares, não existe proteção de escopo dentro de
um bloco de programação, exceto se esse bloco é uma função. Isto poderá mudar no
EcmaScript 6 com a introdução do “let”. Em JavaScript as funções protegem seus
variáveis, mas só os protegem se forem predefinidos com o prefixo var na altura de sua
declaração.
Embora no escopo global, o elemento “var” possa ser omitido porque o variável está
disponível para toda a programação no interior do seu escopo, é uma boa prática sempre
incluir var na altura da declaração para evitar problemas secundários. var cola o
variável ao seu escopo imediato. Na última versão ECMAScript 5, a omissão de “var”
dará erro de sintaxe.
O Objeto Window – Intro
Como já foi mencionado antes o objeto Global torna-se objeto window quando JavaScript é utilizado
no navegador de internet, ou seja no browser.
Vamos fazer um pequeno teste para ilustrar este conceito.
Os variáveis no objeto window.
Ligue o seu JavaScript console (no Chrome é CTRL+SHIFT+j) e escreva o código que se mostra em
baixo a verde. Verifique cada resultado e leia os comentários:
12. window.texto2; // retorna undefined. Este é o mesmo problema que o teste anterior.
Embora window reconheça a função x porque lhe pertence, esse objeto não tem acesso aos
variáveis dentro de x. E então, como chamamos diretamente o objeto, em vez de nos dar
um erro com no exercício anterior, o objeto window criou uma nova propriedade
chamada texto2 que nada tem a ver com o nosso variável em questão.
Agora veremos outro aspecto da utilização de funções, não só para isolar código, como também para
facilitar a administração de memória.
1- Considere o seguinte loop onde i é usado como um contador temporário:
( ficheiro raw ) (icontemp.com/p3/f1.txt) coloque-o no console.
for (var i = 0; i<5; i++){ console.log(i); }
// retorna 0,1,2,3,4
Quando o ciclo termina e JavaScript sai da loop, o variável i deixa de ser necessário, certo?
2- Chame i:
i; retorna 5
Isto não é bom, não é? serve apenas para diminuir a capacidade de memória desnecessariamente.
Embora o loop já não exista, JavaScript continua a manter este variável que era suposto ser
temporário, vivo na execução do programa . E isto é porque i está no escopo global.
3- Agora tente codificar o mesmo loop mas desta vez coloque-o dentro de uma função:
Copie do ficheiro em raw.( icontemp.com/p3/f2.txt) Coloque no console. Aqui vamos utilizar y em vez de
i só para não nos confundirmos com o i anterior.
4- Uma vez introduzido o novo script chame a função x para correr o ciclo:
x( );
5- Uma vez que o loop esteja terminado, verifique se y ainda existe como fizemos da última vez
com i:
2- window.humor = "contente";
3- window.idioma = "Português";
7- this.cor; // retorna ”verde”. (O this substitui genericamente o objeto a que este variável
pertence. Continuaremos a explorar “this” mais a fundo).
A resposta é: Em JavaScript this refere-se sempre ao dono da função que está sendo
executada, a não ser que faça um redirecionar forçado. Quem é o dono desta função x? O
objeto Global, window. “this” substitui o objeto genericamente. Neste caso substitui window.
No entanto há que manter em mente que conforme vamos instanciando outros objetos, estes objetos
passarão a ser donos de funções em seus métodos e mesmo que estes objetos estejam sob
window, eles serão os donos se e quando forem eles a chamarem o variável de que têm posse,
isto é, veremos que não é assim tão simples como parece. Tudo depende do contexto de execução.
this poderá ter um dono diferente se o contexto de execução assim o determinar. Continuaremos a
explorar este assunto e como diz o ditado, “de vagar se vai ao longe”.
Mais Valores Primitivos e Valores de Referência
Agora como ilustração, vamos ver se podemos atribuir diretamente a um variável type string, uma
propriedade daquelas que se atribuem a objetos. Lembra-se como valores primitivos, tais como
um string são guardados no stack, e valores de referência são guardados no heap?
x.cor = "verde";
O console devolve “verde” (e por isso deve ter aceitado a propriedade, certo?) Vamos então
verificar se sim ou se não:
var y = { }; As chaves indicam a JavaScript que isto tem que ser um objeto.
5- Tente adicionar uma propriedade como fizemos antes: y.cor = "verde";
O nosso primeiro variável x contem o valor de “ninja”. Vamos atribuir este valor de “ninja” a um
outro variável:
8- Se está recomeçando de novo declare o variável x com “Ninja” outra vez:
var x = "ninja";
9- Em seguida declare um novo variável z e atribua-lhe o valor de x:
var z = x;
10- Teste z: z; // devolve “ninja”
Isto chama-se passar data por valor e é o que acontece com valores primitivos. Sempre que
reatribuímos um valor a outro variável, este valor é copiado para o próximo variável, porque no
stack, um variável e sua data é tudo a mesma unidade. Para ajudar a se lembrar deste termo
“passar por valor”, pense em “adicionar valor” porque quando copiamos estamos adicionando mais
data no sistema.
Por outro lado, no que diz respeito a valores de referência (os que estão no heap), a coisa funciona
de maneira diferente. A razão porque se chamam valores de “referência” é porque só existem se
algo se refere a eles. Neste caso é muito mais eficiente dar autoridade a outro variável para apontar
para a data no heap do que estar a criar uma segunda cópia do mesmo valor. Isto é obviamente
chamado passar data por referência (apontando em vez de copiando).
var b = y;
16- Teste b:
y.cor2 = "vermelho";
18- Teste y:
y = {"cor": "violeta"};
21- Teste-o:
22- Teste b:
b; // Object {cor: "verde", cor2: "vermelho"}. b continua apontando para o primeiro objeto.
23- Agora vamos praticar dereferenciar objetos, mas primeiro declaramos outro objeto c e
atribuímos-lhe b para que o objeto tenha dois apontadores em vez de um só:
var c = b;
24- Teste c; // devolve Object {cor: "verde", cor2: "vermelho"}
25- Agora dereferencie b (corte a corda umbilical):
b = null;
26- Teste c:
c = null;
28- Teste c:
c; // devolve null
E assim finalmente o objeto foi enviado para o Coletor de lixo ficando à espera de apagamento
pelo browser porque já não existe nenhum apontador no stack para o objeto.
Nota: Existem várias maneiras de nulificar variáveis ao mesmo tempo. Um exemplo é o seguinte:
x = y = z = null; //
Como passar data de stack para o heap
Uma das formas de passar data do stack para o heap é através de um argument que se atribui a
uma chamada de função.
Figure 11
Em JavaScript, data primitiva do stack pode ser copiada como argumento para uma função no
heap, através de um interface conhecido com o nome de parâmetro. Podem-se ter até 255
parâmetros e estes são programados dentro dos parênteses da função e separados por virgulas. A
ordem da data a copiar tem que corresponder à ordem de parâmetros, porque o programador
utilizará essa ordem para dar instruções do processamento de data à função.
Por outras palavras, a data do variável no stack é independente da data entregue à função, porque
uma é a copia da outra, ou seja uma duplicação.
Acima, x aponta para a função e (33) é um número que irá ser copiado pela função.
Note que embora a função resida no heap (o apontador x reside no stack), o interface parâmetro
num (nome arbitrário) tira um facsímile da data que lhe queremos fornecer. Esta é uma
característica dos parâmetros de função quando aceitam argumentos vindos do stack. Nunca tocam
no original. Por outras palavras, esta passagem de data é por valor, tal e qual quando um variável
é atribuído a outro.
Na verdade, estes parâmetros são variáveis temporários. E a atribuição de valores é através de
cópia, ou passagem por valor. Esta transação é singular e está independente do que possa vir a
acontecer ao remetente, ou ao destinatário.
Nota: Se por acaso o remetente (o argumento para uma função) vier do mesmo lado da memória,
isto é, proveniente de outra função ou objeto no heap, esta passagem de data é feita por
referencia. Isto é importante saber porque qualquer mudança feita dentro da função nesta data, irá
afetar a data original se ela vem do heap como por exemplo de um array ou outro objeto. A razão
é porque estamos a dar permissão à função para agir na data do objeto. Neste caso, o que entra na
função não é a data em si, é a autorização para servir de apontador para a data original.
Este contraste entre data vinda do stack para a função, e data vinda do heap para a função, tem
resultados de funcionalidade diferentes. O primeiro é uma cópia da data, o segundo é apenas uma
referencia com autorização para editar o original.
Parabéns na sua leitura e compreensão deste material.
Vamos seguir para a parte dois e desvendar mais mistérios de JavaScript.
A informação coberta até agora será bastante útil nas próximas sessões.
Cinturão negro à vista no horizonte…
agradeço o seu tempo despendido!
:)(-
PARTE 2
=====
Uma boa palavra deixada no Amazon sobre o que já leu até aqui
var x = "bananas";
Quando um primitivo é encapsulado num objeto String, herda automaticamente várias (a)
propriedades, e vários (b) métodos.
Vamos primeiro ver algumas das propriedades herdadas:
x.length; // devolve 7. (7 caracteres em banana). Note que as aspas não fazem parte do string.
2- prototype
A propriedade prototype é um comando que adiciona propriedades e métodos a um objeto já
existente. prototype pode também ser visto como um menu onde se listam todos os métodos
disponéveis ao objeto. Falaremos mais a fundo sobre isto nos próximos capítulos.
Vamos adicionar temporariamente uma propriedade ao objeto String para que nossos variáveis a
possam utilizar, pois eles herdam-na automaticamente. Estou assumindo que ainda tem o x
declarado no seu console:
Como lembrança da sintaxe eu utilizaria esta mnemônica: “Senhor String, prototype-me esta
propriedade chamada “gostos” com a seguinte informação “adoro bananas”. (E eu visualizaria
esta propriedade como um par de chave-valor, que no nosso exemplo seria x.gostos).
Agora testemos o nosso x:
var y = "Laranjas";
Chame a propriedade gostos para o variável y:
var z = 123;
Agora chame a propriedade gostos aplicada a z:
Depois de colar o script no console, chame o método ao variável x. Não se esqueça dos
parenteses porque um método é uma função:
3- Construtor
A terceira propriedade do objeto String disponível ao string primitivo, é o construtor. (As outras
propriedades eram prototype e length).
O construtor contem a referência para a função que constrói instantes (replicas) deste objeto.
O construtor cria objetos com suas propriedades e métodos, enquanto que o prototype só cria
propriedades e métodos. Por questões de eficiência, é melhor deixar a criação de métodos para o
mecanismo de prototype, porque o construtor duplica a data quando gera uma nova instância de
objeto. Teremos oportunidade de explorar este assunto mais a fundo.
Esta informação serve apenas como introdução e não é necessário memorizar os termos acima
mencionados. Apenas compreenda por alto e continue a ler pois tudo fará sentido mais à frente.
b) Métodos do objeto String tais como: charAt, charCodeAt, concat,
fromCharCode, indexOf, lastIndexOf, match, replace, search, slice, split, substr,
substring, toLowerCase, toUpperCase, trim, valueOf
2.1.2 charAt( ) e charCodeAt( )
Ambos os métodos têm a ver com o acesso aos caracteres de um string. O primeiro método devolve
o caractere em si, e o segundo método devolve o Unicode do caractere.
var x = "bananas";
2- Chame o método charAt( ):
String.fromCharCode(110);
// devolve o caractere n. Pode-se atribuir este método variáveis como por exemplo:
var x = String.fromCharCode(110); // x é agora “n”.
Podemos também chamar vários números de código Unicode ao mesmo tempo:
String.fromCharCode(109,110,111,112);
// devolve mnop.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método:
var x = "bananas";
2- Declare outro variável combinando dois strings (não se esqueça do espaço ante de são):
Exemplos de scripts:
1- Se o nosso velho x já não está definido, por favor defina-o outra vez como var x =
"bananas"; Depois experimente com os seguintes scripts:
x.indexOf("n"); // devolve 2 como número de posição.
x.lastIndexOf("n"); // devolve 4 como número de posição.
2- Vamos tentar com outro exemplo diferente:
var aba = "Verde amarelo branco azul marinho.";
Se a sua intenção é a de trocar a palavra em vez de pesquisar o termo, veja o método replace()
discutido um pouco mais abaixo.
Se a intenção é encontrar vários elementos iguais, utilize match() que será demonstrado em
seguida.
Por favor repare na sintaxe destes métodos que é do tipo “camelo” onde palavra ligadas começam
em maiúsculo a partir da segundo termo.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Outros exemplos:
xyz.match(/banana/g);
Poderíamos por exemplo atribuir a um variável um array com "maçãs e bananas":
var frutas = xyz.match(/maçãs e bananas/g);
Ou então adiciona toString() para coverter o variável a string em vez de um array:
var frutas = xyz.match(/maçãs e bananas/g).toString();
Chame frutas:
frutas; // devolve "maçãs e bananas".
Expressões Regulares são um assunto para além do intuito desta primeira edição. Para uma leitura
mais profunda por favor dirija-se aos seguintes links:
Wikipedia | Mozzila | Ferramenta de Regex.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
xyz = xyz.replace("deliciosas","saudáveis");
// devolve "As maçãs e bananas são saudáveis".
Repare que modificamos o nosso próprio variável. Poderíamos ter dado esta data a um outro novo
variável e ter mantido o original intacto.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
O método search( ) devolve um index negativo (-1) se não encontrar nenhum equivalente ao elemento
que procura. Se tal elemento existe, então o resultado será um index positivo indicando a posição
sequencial do elemento.
Exemplos de scripts utilizando o nosso exemplo anterior:
var xyz = "As maçãs e bananas são deliciosas";
Procurando bananas:
xyz.search("bananas");
// devolve 11 (começa contando de zero e b está na posição 11).
xyz.search("na");
// devolve 13 ( de 0, n está na posição 13).
search() é um método sensível a letras maiúsculas e minúsculas:
xyz.search("A");
// devolve position 0 de "As".
xyz.search("a");
// devolve posição 4 (de maçãs)
xyz.search(/a/i);
// devolve posição 0. Aqui utilizamos uma flag (marcador) da biblioteca de Expressões Regulares,
i, para indicar a JavaScript que não importa se o caractere que procuramos é maiúsculo ou
minúsculo, isto é insensível à capitalização. Assim, o primeiro caractere que for encontrado é o que
JavaScript deve devolver.
Para saber mais sobre Expressões Regulares pode visitar os seguintes sítios:
Wikipedia | Mozzila | Ferramenta de Regex.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
O método slice( ) copia um pedaço de texto de um string e devolve outro string com a cópia do
texto copiado. O string original não é afetado. No entanto se copiarmos o string para si mesmo,
perderá a data original que não fizer parte da cópia.
Este método aceita dois parâmetros. O primeiro parâmetro indica o primeiro caractere a ser
copiado (contagem a partir de zero). O segundo parâmetro indica o primeiro caractere a não ser
copiado.
Exemplos de scripts:
1- Declare um variável:
Se usarmos apenas um parâmetro com slice, o resultado entre um número positivo e um negativo
é inverso entre um e outro.
6- Número positivo: Deixa tudo até ao índice indicado e copia o resto do string.
Expressões Regulares tornam split num método muito poderoso. Vale a pena investir algum
tempo aprendendo RegEx. Aqui vai um link relacionado com esse assunto: Wikipedia.
Figure 14
O método toUppercase() transforma todos os caracteres para letra maiúscula. Desta forma,
“Maçã” transforma-se em “MAÇÔ.
Exemplos de código:
1- Declare e chame o seguinte variável txt:
Figure 16
Figure 17
6- Agora se fosse a chamar str1 outra vez os espaços teriam desaparecido.
Sample code:
1- Declare a variable: var ppp = "amazon";
Em JavaScript, arrays utilizam um número de índice que aponta para uma localização onde data é
guardada. Em contraste com outras linguagens, em JavaScript não existe algo chamado array
associativo onde o índice é substituído por uma etiqueta em string. Se tentarmos associar um nome
a um elemento de um array, JavaScript fará dessa data uma propriedade do objeto e não uma peça
de data contida no array. Essa data não será listada quando fazemos o output do array. Se
realmente quisermos uma associação entre um nome chave e sua data, devemos utilizar o
construtor de objetos em vez do construtor de arrays, isto é, usamos chaves em vez de parênteses
retos que identificam arrays.
Graças à rigidez ordenada de arrays, existem métodos específicos para inserir data no principio
ou no final do array, assim como em qualquer outra posição do meio.
O construtor do array é o seguinte:
ijk[0] = "vermelho";
// o primeiro elemento começa na posição zero.
ijk[1] = "branco";
ijk[2] = "azul";
3- Chame o array no console assim:
ijk;
// devolve-nos ["vermelho", "branco", "azul"].
4- Agora podemos também chamar elementos individuais, como por exemplo:
ijk[0];
// devolve "vermelho".
E tal como o objeto String, o objeto Array herda certas propriedades e métodos do objeto Object.
Vamos em seguida ver algumas das propriedades e métodos mais importantes.
a) Propriedades de um array: length, prototype e constructor.
O constructor já foi demonstrado qundo falamos em String. Cobriremos construtores mais a fundo
num dos próximos capítulos.
O comprimento do array, ou length (soa aproximadamente como lengf), funciona tal e qual o
length de um string. Como sabemos, um string não é mais nada do que um array de caracteres em
sequencia, certo? No entanto, existem razões para utilizar strings e razões para utilizar arrays. Nós
podemos mostrar ou imprimir um string, mas para fazer um output dos elementos do array
necessitamos de iterar por cada um deles. Por outro lado nós podemos mudar um elemento de um
array, mas para mudar um caractere de um string é um pouco mais complicado. E esta é uma das
grandes razões porque às vezes necessitamos de converter um array para type string e outras vezes
necessitamos de converter um string para type array. Se você se lembra, nós utilizamos split( ) para
converter um string para array. O oposto é conseguido através do método join() que será coberto
neste capítulo de métodos de Array.
A figura de cima utiliza o sintaxe de ponto para ‘colar’ o novo variável listArray à lista de
protótipos, que por sua vez está ‘colado’ ao objeto mãe Array. listArray aponta para uma função
anônima (que atua temporariamente). Este método utiliza um variável temporário, "i", que visita
iterativamente cada elemento do array representado pelo termo genérico “this”. ‘this’ é um termo
genérico que significa o array em questão. Codificando ‘this’ (este, cujo) em vez de um nome
específico do array, torna este script portátil ou universal. O variável “i” cicla através do array
até atingir o número de comprimento (length) do array onde está a atuar. Para cada iteração,
JavaScript imprimirá o elemento de "this" em foco na posição "i", isto é, o elemento do array que
chamou tal método. JavaScript automaticamente substitui o “this” pelo nome do array que está
chamando o método.
2- Copie o método prototípico que se mostra em cima (veja o ficheiro em raw) e coloque-o no
console. Isto adicionará o método listArray() a todos os arrays existentes ou ainda por existir
neste momento de execução.
3- Agora chame o método listArray() aplicado ao nosso array ijk:
2- Declare um segundo array abc2, atribuindo-lhe o resultado de join() com o array abc:
abc2 = abc.join(" "); // devolve "vermelho branco azul maçãs bananas uvas".
abc2 = abc.join("/"); // devolve "vermelho/branco/azul/maçãs/bananas/uvas". Etc.
Lembrar que o array abc continua intacto na sua forma original.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Reverse é permanente, isto é, para voltar ao original terá que aplicar o reverse outra vez.
1- Reverse abc:
Vamos remove-los outra vez com pop() ah, mas espere!!! Embora push() possa inserir vários
elementos de uma só vez, pop() só pode inserir um elemento de cada vez. Teríamos que fazer o
pop() três vezes, certo?
8- Para praticar um pouco, vamos fazê-lo então três vezes. Isto diminuirá o nosso array abc para
o seu conteúdo original. Repare na minha tentativa em remover os items:
abc.pop("pêssegos","cerejas", "peras"); // devolve “peras” ( o último da minha escolha).
Ignorou as minhas instruções como parâmetros e limitou-se a apagar apenas o último elemento do
array.
2- Vamos aplicar o método slice( ) criando um novo array onde excluímos os elementos
“bananas” e “uvas”, isto é, incluímos todos os outros elementos:
var abc3 = abc.slice(-2); // slice copiará os últimos 2 itens de abc e deixará tudo o resto
para trás. Um número negativo em slice seleciona elementos da direita para a esquerda. O número
de elementos selecionados corresponde ao número negativo apresentado. Neste caso copiamos
dois elementos do final do array.
Vamos experimentar com alguns exemplos para sintonizar bem com este critério.
· Removendo itens com splice( ):
1- Declare um novo array x, ficheiro em raw: icontemp.com/p3/21.txt :
var x = ["azul","vermelho","verde","violeta","castanho"];
2- Apague 3 elementos a partir da posição 0:
x.splice(0,0,"azul","vermelho","verde");
3- Chame o array x:
abc.splice(-3,3);
O número -3 conta três itens do final para o princípio. Estes três elementos estarão em foco para a
ação que será decida nos próximos parâmetros. O próximo parâmetro, o segundo número, diz-nos
para apagar este três itens. Neste exemplo não existem trocas.
Se desejar experimentar este critério, coloque o array abc no console (ficheiro em raw)
icontemp.com/p3/222j.txt. Depois aplique o script mostrado acima. Este splice() apagará "maçãs",
"bananas", "uvas".
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
A função anônima utilizada neste método pode conter até três parâmetros, sendo os primeiro dois
os mais populares. O primeiro parâmetro (como no nosso exemplo), aponta para cada valor de
elementos no array. Ainda se lembra quando no tópico Mais Valores Primitivos e Valores de Referência
mencionamos como poderíamos apontar para um valor de type referência como por exemplo um
dos valores de tipo array, através de um parâmetro de função? Reveja a lição desse capítulo se
necessita de se recordar.
Este “macro” forEach() facilita a nossa codificação de scripts, tal como o que costumávamos
fazer com o seguinte for loop:
for (var i = 0; i < array.length; i ++)
No exemplo de forEach( ) na figura de cima, parâmetro i apontará para cada valor existente no
array, e o método forEach fará leitura do valor em si que coincide com o apontamento de i e de
acordo com as instruções no corpo do nosso método. Neste exemplo as instruções são para enviar
cada valor ao console.log em cada posição i. E o resultado é: vermelho, azul, branco (em linhas
separadas porque cada console.lo é independente).
3- Vamos experimentar o mesmo script com dois parâmetros em vez de um só:
Figure 23 ficheiro em raw. icontemp.com/p3/23.txt
Como dantes, o primeiro parâmetro aponta para o valor da data. O segundo parâmetro aponta
para o index em si (ou seja o valor numérico do sítio onde i está visitando no momento). O termo
utilizado como parâmetro não faz diferença, pode utilizar Fernando e Filomena em vez de i e
índice que vai dar ao mesmo; o importante é a posição de sequência de cada um destes dois
parâmetros.
No nosso exemplo console.log fará o output do segundo parâmetro, seguido de um :, e seguido
pelo valor do primeiro parâmetro a cada iteração do forEach.
Notas: forEach chama a função anônima callback uma vez por cada item do array. E chama em
ordem ascendente. Por vezes um terceiro parâmetro é utilizado para especificar o nome do
Array onde o script está sendo implementado.
O método forEach é intencionalmente genérico; não é necessário que o terceiro parâmetro onde
vai atuar seja do type Array. Por isso o método pode ser aplicado a outros tipos de objetos mas
não é tão simples assim: a funcionalidade do método forEach em outros tipos de objetos é
dependente da plataforma onde JavaScript está sendo utilizado.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Tal e qual em forEach( ), utiliza-se aqui uma função anônima tipo callback mas com só um
parâmetro que aponta para cada elemento do array a que é chamado, o primeiroArray. Sendo
map() e não forEach(), cada elemento foi copiado para um outro array a que chamamos
segundoArray. A magia desta função está no script interno que JavaScript tem na sua biblioteca
quando endereçamos o mesmo com este macro map(). Tudo o que nós fazemos é simplesmente
escrever os parâmetros que nos interessam e o JavaScript faz o resto.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Geralmente utiliza-se every( ) para inspecionar o array e tomar decisões baseado na informação
recebida para trás: true ou false.
Eis um exemplo de script que verifica se os elementos são par ou ímpar. Se par, return true, else,
return false:
1- Declare um novo array com números consecutivos:
Lembre-se dos outros métodos anteriores: “item” é um nome genérico para representar cada
elemento do array. Este parâmetro serve de interface entre o array e o script que busca
informação, isto é aponta para cada elemento do array (coloco ênfase no cada (em Inglês each) porque todos
estes métodos são versões modificadas do método forEach( )). Conforme item aponta, e a função faz a sua magia
em classificar quem é par e quem não é, every( ) auxilia a função apanhando todos os elementos,
um a um. No final, every() faz o return baseado no facto de todos qualificarem ou nenhum
qualificar.
Como se vê, every() age no princípio de cada iteração e depois age no final de todas as iterações.
São vários mecanismos trabalhando em conjunto num script da biblioteca de JavaScript.
3- Eis um exemplo de um array que resultará em true:
var xyz = [2,30,40,60];
4- Depois de introduzir o array xyz no console, modifique o método every() para acomodar xyz
em vez de abc. Se tudo correr bem o resultado será true.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
3- Chame o array abc2 e verifique o resultado que deve ter sido [2, 4].
Temos agora dois arrays abc e abc2. O abc array não foi afetado porque filter() fez simplesmente
uma cópia de elementos escolhidos sob o critério que estipulamos.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Figure 28
Informação preliminar
Antes de mostrar algum exemplo destes métodos vamos dispender um pouco tempo tentando
compreender a sintaxe destes dois mecanismos.
Parâmetros que modificam o resultado dos métodos reduce/reduceRight:
O valorInicial – Sendo este valor opcional, a iteração da função começará no valor de valorInicial
se for apresentado.
Nos métodos de redução, quando o valorInicial é dado, o primeiro parâmetro da função anônima a
que eu chamarei nos nossss exemplos de valorPrevio, começa no valor de valorInicial. Neste caso,
o segundo parâmetro da função a que eu chamarei nos nossos exemplos de valorCorrente, começa
na posição do primeiro elemento do array.
Figure 29
Normalmente o valorInicial não é incluído. Neste caso, o primeiro parâmetro, valorPrevio,
começa a atuar na localização do primeiro elemento do array onde estamos a aplicar este método,
e o segundo parâmetro, valorCorrente, começa no segundo elemento do array.
Nos próximos dois exemplos veremos como tudo isto funciona.
2.4.2 reduce( )
O método reduce( ) atua baseado em uma função de tipo callback que é inserida pelo
programador e que aceita até 4 argumentos em seus parâmetros. Os argumentos, meus nomes
aleatórios, são:
a) valorPrevio – O valorPrevio (nome aleatório) é o valor do cáculo anterior, ou o valor do
primeiro elemento antes da atuação do método, ou o valor do valorInicial se este existir.
b) valorCorrente – O valor do elemento na iteração atual (posição atual) que irá ser processado
com o valorPrevio, ou o valor do segundo elemento do array no princípio do algoritmo (mas
aqui, quando o valorInicial está presente, o valorCorrent passa a ser o primeiro elemento
porque o valorPrevio assume o valor da declaração valorInicial).
c) index (índice corrente) ou localização atual da função no objeto sendo iterado. Pode excluir
este parâmetro porque não é essencial, ou incluir se o necessita para o seu output.
Vamos ilustrar estes conceitos com um exemplo, declarando um array numérico. Depois
declaramos um variável para guardar o resultado da multiplicação de todos os números do array
utilizando o método reduce( ).
1- Declare um array com nome de meuArray:
1- Use o meuArray anterior. Se está a recomeçar o seu console de JavaScript, declare o array
indicado a baixo:
x1.indexOf("nozes", -2); // devolve a posição 4 para ‘nozes’. O critério é que -2 irá iniciar a
pesquisa no elemento ‘castanhas’ (segundo a contar do fim), seguindo depois para a direita e
descobrindo ‘nozes’ na última posição do array.
· Critério de pesquisa para o método lastIndexOf( ) em relação ao fromIndex:
a) O argumento fromIndex é opcional e é representado como segundo parâmetro. Se ele não for
incluído, JavaScript assume a posição última do array, isto é o número assumido é o
comprimento do array menos 1 (devido à base zero). Isto faz sentido porque o lastIndexOf()
começa a pesquisa no último elemento e move-se da direita para a esquerda.
Num array com 5 elementos, o fromIndex assume automaticamente a posição no.4 que
corresponde ao quinto elemento: 0,1,2,3,4. Se por acaso o fromIndex for maior que o
comprimento do array, isso não fará diferença pois é como se não tivesse sido incluído. Num
array de 5 elementos, um fromIndex de 5 ou de 6, etc. terão o mesmo efeito como se fossem
um fromIndex de 4 (o normalmente assumido).
b) Um método lastIndexOf( ) com um fromIndex negativo começará a pesquisa numa posição
offset baseada no comprimento do array mais o número negativo (como descrevemos para o
outro método) e depois move-se da direita para a esquerda. Um array com 5 elementos e um
fromIndex de -1 começará como seria normalmente na posição 4 que corresponde ao ultimo
elemento do array.
2- No exemplo em baixo, dado este array ["amendoim", "amendoas", "nozes", "castanhas",
"nozes"], a busca escolherá o último ‘nozes’:
Como exemplo, vamos utilizar alguns dos conhecimentos já adquiridos para multiplicar todos os
argumentos inseridos, utilizando o método de Array reduce( ). Para isso teremos que copiar a
data do objeto arguments para um array utilizando slice(), e depois aplicar ao array o método
reduce() porque é uma das ferramentas disponíveis a arrays:
4- Escreva (ou copie) a seguinte função no seu editor (lines 1 – 8), e depois cole no console.
Chame a função x(2,3,4,5); como se mostra na linha 10:
Figure 35 ficheiro em raw. icontemp.com/p3/35.txt Trabalhando com o objeto arguments. Introdução a call().
· Se se sentir perdido nesta próxima explicação não se preocupe, leia até ao fim e volte
outra vez numa outra ocasião para reler uma vez mais, especialmente depois de ter acabado
a leitura do livro. Compreensão vem em camadas, pelo menos é como me aconteceu a mim
e continua a acontecer todos os dias.
· Na linha 1 a função x foi declarada sem parâmetros formais.
· Na linha 3 declaramos um variável com o nome aleatório de gts. A este variável
atribuímos os elementos contidos no objeto arguments. Mas antes disso, copiamos e
convertemos a data do arguments para um array através do método slice( ). Se não
tivéssemos convertido gts a um array e tentássemos o próximo método diretamente
utilizando arguments, teríamos tido este erro:
TypeError: Object #<Object> não tem método 'reduce'.
Para poder converter gts a um array tivemos que chamar o path completo até ao objeto
Array porque estamos a chamar um método externo, não um método do objeto Function.
Nota: como atalho poderíamos também ter programado da seguinte forma:
var gts = [ ].slice.call(arguments);
· A linha 3 introduz algo novo neste livro: .call( ). call() é um método do objeto Function.
Discutiremos este mecanismo um pouco à frente num outro tópico mas deixamos aqui uma
introdução. O método call() é uma maneira de chamar um objeto que não podemos chamar
diretamente porque JavaScript pode ficar confuso e chamar um outro objeto (nem sempre temos
um ambiente ideal). call() é utilizado quando o placeholer "this" pode ser ambíguo dando um
resultado que não era nossa intenção. Ao utilizar call(isto), fazemos o binding (colagem)
entre o que está à esquerda de call() e o objeto que se encontra dentro dos parênteses (isto).
É como dizer: “JavaScript, desta função onde me encontro inserido, chame o objeto
arguments”.
No nosso caso, o que significa .call(arguments) ?
“Senhor JavaScript, desta função onde me encontro inserido (eu o ‘call’), chame o
objeto arguments desta função”.
· Agora que já temos um array (gts, linha 3) podemos chamar métodos pertencentes ao type
Array.
· Na linha 5 chegamos finalmente à nossa intenção original, chamar o método reduce() para
multiplicar todos os elementos do array gts, devolvendo o resultado do cálculo através do
mecanismo return. Para fazer uma revisão deste método pode seguir o seguinte atalho: 2.4
Novos Métodos de Redução e Localização para Arrays em JavaScript.
· Linha 10 (não está no ficheiro em raw) chama a função com alguns argumentos como exemplo.
Estes argumentos são copiados pela função x e processados pelos scripts das linhas 3 -6
para nos dar um resultado de 120.
O método call() será explorado mais a fundo na secção 2.6 Propriedades e Métodos de Função.
5- Se necessita de mais um exemplo de cal( ) tente o próximo exercício.
Figure 36 ficheiro em raw. icontemp.com/p3/36.txt Propósito: compreendendo call().
aba(7,11); // devolve "x= 7, y= 11". Data in, mesmo resultado para fora.
aba(7); // devolve "x= 7, y= undefined". x foi devolvido, y não existe mas não foi definido.
aba(7,11,9); // devolve "x= 7, y= 11". O último parâmetro não foi mostrado porque o
mecanismo return não tem maneira de o agarrar, mas existe.
8- Agora vamos mudar a função para que o terceiro e outros argumentos possam ser processados
embora só existam 2 parâmetros formais:
Figure 38 ficheiro em raw. icontemp.com/p3/38.txt Propósito: Como objeto arguments processa output, em comparação
com argumentos formais.
Mudando a linha 2 para return arguments como se mostra no ficheiro em raw e na imagem de
cima, podemos fazer um output de qualquer número de argumentos (até 255).
9- Faça um teste:
11- Chame a função com frutos (o ficheiro em raw já contem esta chamada na lihna 7):
f("banana","maçã","uva","pêssego");
// devolve banana é um fruto, maçã é um fruto, uva é um fruto, pêssego é um fruto.
Em sumário, o novo objeto de função arguments abre imensas portas para novas possibilidades.
Use parâmetros formais quando necessitar de uma estrutura onde os inputs têm que ser
consecutivos, e utilize o objeto arguments quando quiser ir para além do formato standard.
Os últimos dois exercícios podem ter sido um pouco avançados para alguns leitores mas por
favor não pare por aqui. Tudo fará mais sentido nos próximos tópicos. Dê uma oportunidade ao
tempo.
2.5.2 Funções dentro de Funções
Funções dentro de funções são conhecidas popularmente como funções aninhadas. Estas funções
internas têm acesso a toda a data da sua função mãe, o que lhes oferece um poder imenso. O
propósito deste tópico é o de desenvolver uma compreensão básica de algumas das características
de funções internas e externas.
2.5.3 Uma Introdução Gentil a Closures
1- Suponha que temos um array atribuído a um variável de nome ara:
ara = null;
7- O mesmo array continua a existir sob o nome de ujo e tony, certo?
Explicação:
A função papagaio() tem uma função interna, menina(), e dependendo do argumento que chama a
função, papagaio() devolve quem segura papagaio: menino da função externa, ou menina da função
interna.
Porém, como sabe, quando normalmente uma função faz o return, a sua execução atual é apagada
instantaneamente. Neste nosso caso isso não acontece quando a função devolve a opção “else”, que é
uma outra função e que usa valores contidos na função externa. No seu processo de apagar,
JavaScript decide manter os valores e esperar até a função interior fazer o seu próprio return. Só
depois é que ambos os contextos de execução são apagados para dar lugar a outros.
A pergunta é: Como é que isto funciona?
Funções de JavaScript não são só pedaços de código que pode ser executado, elas são closures.
Closure, tal como dreamcatchers, simbolizam união. Um inventário tipo closure mantém não só o
código que uma função tem que processar, mas também o ambiente onde ele foi gerado. Uma
função interna tem acesso à sua função externa, e à função externa da sua função externa, e assim
por diante até chegar a topo onde o objeto Global existe.
Figure 42
Às vezes lemos na web como closures têm “free variables”. Isso está a se referir aos variáveis
declarados noutros sítios externos mas que pertencem ao elo de execução da função que faz closure.
Na ilustração de cima, func2 tem os seguintes free variables: A, B, e C. ( D, não é free ou seja,
grátis, porque pertence realmente à func2).
Em baixo temos uma outra maneira de ver esta ilustração baseado no conceito de duas pessoas
segurarem um cordel de balões. Aqui demonstra-se quem tem acesso aos balões na altura de
execução da function1 (quando a função é chamada):
Figure 43
Levando o conceito das ilustrações 42 e 43 como exemplo, podemos aproveitar para fazer dos
variáveis da função externa func2 uns variáveis mais privados, isto é, sem acesso direto da parte
exterior, a parte global. Isto é um técnica comum e merece ser vista. Nós podemos criar este
ambiente privado fazendo com que todo o output seja processado através da função interna, isto é, a
função interna serve de intermediário entre a função externa e o mundo exterior. Considere o exemplo
seguinte:
Figure 44 ficheiro em raw. icontemp.com/p3/44.txt
bankCheck(2000);
// devolve "Perdão, não tem fundos suficientes!"
bankCheck(500);
// devolve "aqui está o seu dinheiro: 500"
Toda esta funcionalidade e valores encontram-se na função externa, mas são fornecidos ao
público pela função interna. Isto é um método de prevenção. Como não há acesso público direto
ao variável clienteBalance, ninguém poderá modificar este valor porque não é visto de fora
devido ao var que o faz um variável privado. A única maneira de acessar a este variável é
criando-se um método interno que o faça indiretamente, o que é o caso da função
bankTransaction. Veremos outros métodos semelhantes mais à frente.
2.5.5 Escopo Léxico
Léxico simplesmente dito significa “palavras que se referem a coisas” ou seja, um “menu de
elementos”. Em linguagens sem closures, a vida de variáveis acaba quando a execução do escopo
onde o variável está declarado, termina. Mas em linguagens com closures, tal como JavaScript, os
variáveis continuam a existir muito para além da vida de execução, desde que hajam closures
apontando para eles.
E porque o escopo em JavaScript é desenhado e determinado por funções, funções são deste modo
os ambientes que criam closures, que agem como pontes ou ligamentos entre variáveis e
argumentos referentes aos vários processos de execução.
Como se deve lembrar do tópico anterior, o fenômeno a que chamamos closure acontece quando
uma função interna aponta para data num escopo exterior (outra função acima desta ou mesmo o
ambiente global), o que evita com que JavaScript apague os valores de uma determinada execução
onde existe data que pode ser potencialmente necessária. Este método de persistência é
normalmente executado pelo mecanismo conhecido como coletor de lixo. O coletor de lixo é
implementado pelo navegador, o browser. O desenho deste mecanismo varia entre os vários
browsers e continua a ser aperfeiçoado com cada nova versão proprietária dos fabricantes de
navegadores.
A imagem em baixo ilustra uma coleção de funções aninhadas e os seus escopos iniciais
conhecidos como escopo léxico (ou estático) , o menu de ponto de arranque. Se reparar, e como
já sabemos do que lemos em outros tópicos, cada nível interno de função tem acesso a tudo que
que se encontra no seu exterior:
Figure 45
Na figura a cima, se chamarmos o return diretamente da function3 (a função mais interna) a partir
de function1 (a função mais externa), receberemos o seguinte erro: “ReferenceError: Function3
is not defined”, ‘function3 não está definida’. É que embora function3 veja function1, function1
não vê function3.
Para function1 ter acesso à function3, temos que fazer o return de 3 para 2, e finalmente de 2 to 1.
Só assim é que function3 poderá ser lida por function1. A razão disto é porque o output de uma
função interna só pode ser lido pela próxima função acima dela. O mesmo se aplica quando uma
função é externa, isto é, o seu output só pode ser lido por mecanismos que se encontrem no
ambiente global. Não confundir ler com utilizar. A utilização só acontece de dentro para fora,
não de fora para dentro.
No entanto e ao contrário (como foi dito na última linha anterior), Function3 tem acesso a todo o
seu ambiente externo que vai até ao objeto global Window. É uma estrada com uma só direção.
Funções apontam (veem) de dentro para fora.
Eis um conceito chave: Sempre que chamamos uma função para processar um argumento,
estamos a criar uma nova função com uma nova lista completa de variáveis disponíveis e
outros recursos relacionados. Quando esta função faz o seu return, esta lista é normalmente
apagada (a não ser que hajam closures relacionados).
Pesquisa de variáveis: Para cada escopo diferente, uma lista é gerada. Esta lista é um objeto que
se chama activation object. Um objecto de ativação é uma lista de todos os variáveis disponíveis
para a execução. Esta lista é guardada no heap em vez de no stack que serve apenas para data
temporária. Isto permite-lhe ter uma vida tão longa como necessário que seja.
Eis a sequência de cadeia que o interpretador atravessa ao procurar um variável:
Começa no escopo local que é o escopo mais interno de onde o script está atuando; se o variável
não se encontra no escopo interno, o interpretador sobe um nível e procura outra vez; depois sobe
outro nível e assim por diante até encontrar o que busca ou chegar ao topo do escopo global. O
primeiro variável que encontrar com o nome dado, é o variável que utilizará. Mesmo que hajam
outros variáveis com o mesmo nome acima deste escopo, o interpretador nunca lá chegará porque
pega no primeiro qualificador que encontra.
Todas as funções podem atualmente ser consideradas funções de closure porque todas as funções
têm acesso a variáveis externos a si mesmas. O que é um closure se não a combinação de uma
função e todos os variáveis disponíveis na totalidade de seu escopo? Tenha a certeza de fazer o
return de suas funções (closures) quando já não as necessitar, para que o coletor de lixo possa
limpar resíduos que preenchem a memória do sistema. De qualquer modo todas as funções fazem
um return automático ao acabarem, se não houver um return especificamente formulado. A única
pergunta é, quando é que JavaScript decide que uma função já não tem validade se não houver um
return no seu script? Programar é parte ciência parte arte e por isso existem sempre duvidas no
que diz respeito à melhor maneira de se codificar algo em referencia ao que rodeia o script em
questão. Quando mais cientes estamos de como tudo isto funciona, melhor chance temos de fazer
um trabalho mais perfeito.
Nota de lembrança: Num outro capítulo foi mencionado que quando a data de um variável é
atribuída ao parâmetro de uma função, esta data é copiada e por conseguinte, independente do
variável original em questão. Isto nada tem a ver com closures porque o ‘elo’ de closure para
com o variável não acontece via parâmetros de função. Funções têm closure em variáveis
externos porque estes estão automaticamente disponíveis às ordens da função. Existe um elo de
parentesco entre uma função e todos os variáveis do seu escopo total. E porque será isto
importante? Uma das razões é que uma função tem poderes para modificar o valor de um
variável global, ou de variáveis em funções onde esta função se encontra aninhada. Em suma,
existe diferença entre data que seja passada (do stack para o heap) a uma função como argumento
via seus parâmetros, e data apresentada a uma função através de uma chamada direta da função
para o variável externo. O primeiro exemplo é uma cópia da data, o segundo exemplo é
simplesmente um apontamento e quem pode apontar tem poderes para modificar.
1- Eis um exemplo para ilustrar. Teste os exercícios da imagem e leia os comentários para uma
explicação do que se passa. Apanhe o ficheiro em raw por debaixo da ilustração.
Figure 46 ficheiro completo em raw. icontemp.com/p3/46.txt Ver resto da imagem a baixo.
Figure 47
Nota:
Se o variável x dentro da função na linha 27 tivesse sido codificado como var x = x * num, o var
teria criado um novo variável interno totalmente alheio ao x externo e o x externo (global) nunca
teria sido afetado. Por outro lado também não teríamos o valor do x global pois ambos os
variáveis seriam independentes.
2.5.6 O objeto arguments versus funções internas
Uma função interna tem acesso aos variáveis e parâmetros de uma função externa a que esteja
aninhada e tem também acesso aos variáveis globais. No entanto, esta função interna não tem
acesso direto ao objeto arguments da função mãe. É certo que uma função interna tem acesso aos
argumentos estipulados nos parâmetros da função mãe (os que vêm dentro dos parênteses), mas
não ao objeto arguments em si. Isto faz sentido e a razão será mencionada em seguida, mas pense
um pouco no assunto antes de ler o que vem a seguir, para ver se lhe faz sentido também a si. Isto
é importante manter em mente porque pode-o confundir se não estiver ciente deste conceito.
Porque será que uma função interna não tem acesso ao objeto arguments da função mãe?
A razão é que a função interna vem com o seu próprio objeto arguments, mesmo quando não o
utilizamos diretamente. Então quando chamamos arguments qual deles estaremos nós a chamar, o
de dentro ou o de fora? Quando dizemos a uma função interna para chamar arguments ela devolve
os seus próprios arguments, mesmo que seja um objeto vazio.
Se desejar rever arguments, refira-se ao tópico 2.5.1 O objeto arguments.
Vamos então criar um script para provarmos que isto é verdade, e para ver como se pode chamar
o objeto argumentos externo adicionando uma pequena modificação à chamada.
1- Crie uma função chamada mx que devolve o seu arguments e teste-a chamando-a com (88) e
depois com (88,89,100) (ver linhas 5 e 6 da imagem).
Como se vê, tudo funciona bem porque não há obstáculos no script a cima.
2- Em seguida crie uma função my dentro da função mx que faz o return do objeto arguments.
Isto devolverá um objeto vazio porque a função interna não tem valores neste momento. Não se
esqueça de codificar uma chamada para a função interna a partir da função externa, caso
contrário não poderemos testar my. Veja a imagem em baixo e o ficheiro em raw para que isto
faça mais sentido.
Figure 49 ficheiro em raw. icontemp.com/p3/49.txt
NOTA: Este conceito é simples mas não é tão simples como parece porque o variável “this” só é
atribuído ao objeto que o chama quando o script entra em ação, isto é, na altura de execução.
Isto pode-se tornar um pouco mais complexo porque tudo dependerá da origem de tal chamada, ou
seja, em que contexto de execução está o script a ser chamado. Como podemos movimentar
funções aplicando-as em pontos e situações diferentes, um ‘this’ que normalmente se refere a
window pode vir a se referir a um outro objeto na altura de execução. Então a pergunta é: Quem é
que está chamando o script? Esse alguém é o objeto que será implementado no ‘this’ corrente.
Este processo é conhecido como “late binding”, colagem atrasada, isto é, não acontece na altura
do primeiro passe do interpretador. Esta atribuição acontece quando o interpretador passa uma
outra vez para executar o script. Até agora nos nossos exemplos tudo funcionou porque a nossa
função y está colada ao objeto window. Se formos a analisar o path completo da nossa chamada
de função , ela é a seguinte:
Figure 56
A opção no.2 a cima vem ao encontro da maneira como temos iniciado todas as nossas funções até
agora, embora já tivesses discutido a primeira opção. Pessoalmente prefiro a segunda opção para
funções normais mas neste caso prefiro seguir a primeira opção porque é a mais comum como
construtora, e também porque me relembra que estou a implementar um construtor. Seguirei em
frente com a primeira opção.
Note que a primeira opção não utiliza uma declaração de variável e por conseguinte o ponto e
vírgula final não é necessário.
Só para revisitar este tópico, mantenha também em mente que esta construção será hoisted como
função para o cimo do menu de escopo (veja Funções com Nome vs. Funções Anônimas ).
Aplicação é o nome aleatório da função que acabamos de criar. É também o nome do protopype
deste objeto.
Ao declaramos a função a cima, acabamos de criar um modelo ou padrão para este formulário a
que chamamos Aplicação. Como ainda não temos nada dentro deste construtor vamos continuar
com a construção do formulário modelo para que faça mais sentido. Neste momento o nosso
construtor de objetos é representa um bloco de páginas em branco com apenas o título, ou seja, um
contentor vazio.
1- Vamos reconstruir a nossa aplicação de emprego com uma das propriedades que queremos
obter do aplicante (a sua posição de emprego desejada). O aplicante representa o objeto a se
instanciar:
Repare como o “this” cola cada propriedade vinda do input parâmetro ao dono do novo objeto
(tonyDeAraujo).
2- Copie o script para o seu console e volte a criar um novo instace da aplicação de
tonyDeAraujo. Repare que agora temos que dar mais informação para além da posição
desejada:
var tonyDeAraujo = new Aplicação("programador", "Tony", "deAraujo", 99);
3- Chame o aplicante:
tonyDeAraujo; // devolve
Aplicação {posição: "programador", primeiroNome: "Tony", ultimoNome: "deAraujo", idade:
99}.
4- Vamos introduzir outro novo aplicante criando um variável laurindoAlmeida a quem
atribuímos um new Aplicação:
var laurindoAlmeida = new Aplicação("guitarrista", "Laurindo", "Almeida", 99);
5- Chame o aplicante:
laurindoAlmeida; // devolve
Aplicação {posição: "guitarrista", primeiroNome: "Laurindo", ultimoNome: "Almeida", idade:
99}.
6- Não se esqueça de se introduzir a si próprio como aplicante só para praticar um pouco.
E agora vamos criar alguns métodos para os nossos aplicantes. Sabemos do que já cobrimos no
passado com prototypes, que se misturarmos os métodos com as propriedades do objeto
construtor, duplicaremos estes métodos cada vez que instanciámos uma réplica de objeto com o
símbolo new. Isto não é ideal por causa da eficiência de memória.
Nós queremos criar métodos que se apliquem a todos os instances sem haver duplicação, isto é,
queremos aceder a métodos por referência.
Em ordem para que estes métodos sejam apenas criados uma vez, temos que os incluir no menu
protótipo, isto é, adicioná-los à propriedade prototype. Nós já o fizemos antes mas iremos agora
revisitar o assunto.
Para adicionar nossos métodos à lista prototype não temos que os incluir dentro do construtor,
isso seria duplicação fatal. Podemos criar estes métodos fora do construtor. Mas se insistir
devido ao hábito de outras linguagens, não se preocupe porque mais tarde mostrarei também
maneiras de os incluir dentro do construtor em caso que assim o deseje.
Uma vez criados, estes métodos estarão sempre disponíveis à mercê de cada novo aplicante que
preencha uma nova Aplicação. É quase como dizer a um novo aplicante: “Grato pelo seu
registro. Como aplicante pode agora usufruir destes privilégios. Veja a nossa lista
prototípica”.
7- Vamos criar um método para ver como isto funciona. Criemos um método chamado
introdução que automaticamente mostrará uma mensagem cada vez que chamemos um aplicante
e sua introdução.
Aqui vão dois exemplos da nossa introdução:
Meu nome é Tony deAraujo. Tenho 99 anos de idade e estou a aplicar para a posição de programador.
Meu nome é Laurindo Almeida. Tenho 99 anos de idade e estou a aplicar para a posição de guitarrista.
8- Como faremos isto? Nós fazemo-lo criando o seguinte prototype (copie a ficheiro em raw
se assim o desejar):
tonyDeAraujo.introdução(); // devolve
Meu nome é Tony deAraujo. Tenho 99 anos de idade e estou a aplicar para a posição de
programador.
laurindoAlmeida.introdução(); // devolve
Meu nome é Laurindo Almeida. Tenho 99 anos de idade e estou a aplicar para a posição de
guitarrista.
10- Vamos adicionar mais um método a que chamaremos listeAplicante: Uma maneira de listar
uma aplicação completa do aplicante.
tonyDeAraujo.listeAplicante();
// devolve
Nome: Tony deAraujo
Idade: 99
Posição requerida: programador
Meu nome é Tony deAraujo. Tenho 99 anos de idade e estou a aplicar para a posição de programador.
Sabemos agora o poder e conveniência de “this” quando usado em funções. Nos exemplos de
cima nós criamos uma aplicação de emprego que poderá ser utilizada por um número infinito de
aplicantes. Espero que tenha tido a chance de praticar com o seu e outros nomes, ou adicionado
novas possibilidades. Se não o fez, por favor pratique um pouco. Só se aprende uma linguagem
praticando a mesma como se estivesse conversando com alguém. Vamos rever os pontos chave
deste tópico:
· Sabe como declarar um novo método no protótipo de um objeto existente?
nomeDoObjeto.prototype.nomeDoMetodo =
· Um método é composto de uma ou mais funções. Nós incluímos “this” em vez do nome do
novo objeto para que o script seja genérico e se aplique a todos os objetos.
· Embora possamos criar métodos protótipos dentro do construtor de objetos, escolhemos
não o fazer para evitar duplicação de métodos idênticos.
Vamos então seguir em frente, desbravando mais caminho e descobrindo mais magia no mundo
de JavaScript.
2.6 Propriedades e Métodos de Função
2.6.1 apply( ) e call( )
Muitas vezes desejamos chamar um objeto externo à execução atual de uma função e como o
“this” se refere ao objeto a quem a função pertence nesta altura de execução, não nos permitirá
chamar diretamente o objeto externo de nossa intenção. É nestas ocasiões que os métodos call( ) e
apply( ) se tornam bem úteis. Estes dois mecanismos de função permitem-nos pedir objetos
emprestados, o que nos facilita a vida pois não temos que os reprogramar porque já existem
algures no sistema. É quase como importar funcionalidade de uma fonte externa para o contexto
de execução atual.
Prelúdio: O primeiro parâmetro incluído em call( ) ou apply( ) funciona da mesma maneira em
ambos os métodos e é o que se utiliza mais frequentemente. Se por outro lado temos que incluir
vários parâmetros, então call( ) aceitará argumentos individuais que afinam a especificidade do
método, enquanto que apply( ) aceitará um array de argumentos que pode ser mais apropriado em
certas ocasiões.
Vamos começar por um exemplo simples para ilustrar como isto funciona.
Neste exemplo teremos dois objetos e um método externo que não pertence a nenhum destes dois
objetos. Os dois objetos são, ClientePorCorreio e ClienteLocal. O nosso exemplo de método é
uma mensagem que enviaremos a todos os clientes que tenham feito ordens por correio ou em
pessoa.
1- Vamos criar o objeto ClientePorCorreio:
3- Crie uma pessoa diferente para cada tipo de cliente (isto é, instancie um novo objeto para
cada objeto previamente criado):
4- Crie um método independente de nome mensagem que envia uma nota de agradecimento ao
cliente:
Nós não podemos chamar diretamente de cada tipo de cliente o método mensagem porque o “this’
de cada cliente não aponta para a mensagem. Tal tentativa resultará no seguinte erro:
Figure 66
Para isso poderemos utilizar os métodos call( ) ou apply( ) para colar (bind) o “this” do método, ao
objeto que representa o nosso cliente.
Utilizando call( )
5- Chame o método mensagem para cada cliente, usando call( ):
Figure 67
Repare que call() foi mesmo ler os dados do objeto tony porque o nome que colocou tem o t em
maiúsculo, o que corresponde ao nome dentro o objeto tony.
Faça o mesmo para a Alice:
mensagem.call(alice); // devolve "Olá, Alice! Muito obrigado pela sua escolha dos nossos
produtos."
Utilizando apply( ):
mensagem.apply(tony); // "Olá, Tony! Muito obrigado pela sua escolha dos nossos produtos."
7- Faça o mesmo com Alice.
Como foi visto, quando utilizamos só um argumento, ambos os métodos call( ) e apply( )
funcionam identicamente. Os exemplos a cima indicados são suficientemente simples para se ter
uma ideia básica sobres estes métodos.
Ambos os métodos podem aceitar mais do que um argumento:
call( ) aceita argumentos individuais.
apply() aceita um array de argumentos.
NOTA: O próximo tópico será baseado nos scripts que acabamos de colocar no Console. É
preferivel seguir para o proximo tópico sem apagar o nosso último trabalho.
2.6.2 Adicionando mais argumentos a call( ) e a apply( )
Vamos adicionar mais funcionalidade ao nosso método mensagem introduzindo a classificação sexo
que representa masculino ou feminino e que permitirá tratar cada nome de senhor ou senhora quando
enviamos mensagem a clientes.
Vamos modificar a função mensagem para acomodar um segundo argumento adicionando o
parâmetro sexo. Dispense alguns segundos analizando o script e depois coloque-o no console:
8- Agora podemos chamar o método mensagem para cada cliente de uma maneira mais formal.
Ao escrever o script abaixo, repare na diferença entre este call() e a maneira como chamamos
call() no exemplo anterior. O primeiro parâmetro aponta para o objeto onde se irá aplicar o
método, e o segundo parâmetro aplica-se ao argumento sexo:
mensagem.apply(tony,["Senhor"]); // devolve
"Senhor Tony! Muito obrigado pela sua escolha dos nossos produtos."
Sumário: O primeiro argumento aponta para o objeto que está sendo processado (tony ou alice). O
segundo e consecutivos argumentos têm como alvo as propriedades do método (mensagem). A
sequência dos argumentos tem que condizer com a sequência dos parâmetros da codificação no
método. Ao aplicarmos call() ou apply() estamos a fazer um ligamento temporário entre o método
(mensagem) e o objeto em alvo (tony ou alice), permitindo que “this” seja aplicado corretamente de
acordo com nossas intenções.
Exercício opcional.
E se quiséssemos simplificar a implementação do método mensagem para que uma pessoa não
técnica a pudesse processar?
Como exemplo poderíamos simplificar o processo criando uma função que faria a implementação
automaticamente. Isto é, em vez de se ter que escrever
mensagem.apply(tony,"Senhor");
poderíamos criar um formulário onde o nosso assistente entrava alguns dados e JavaScript faria o
resto:
Nota: Para que este exercício funcione deverá ainda ter no console um dos objetos (figura 62 ou 63),
e um dos clientes aplicado a esse objeto (figura 64). Deverá também ter o método da figura 68 para
ser chamado a partir do script abaixo indicado. Como exemplo utilize um dos clientes que ainda está
declarado (figura 64).
Tudo o que o nosso assistente teria que fazer agora era chama a função:
novaMensagem();
JavaScript perguntaria o nome: tony
Depois pedia para escrever m ou f para masculino ou feminino.
Em seguida o script decidiria o que colocar em cal( ) quando fizesse a chamada de n e s.
A linha 2 atribui a entrada do nome escolhido a quem enviaremos mensagem. prompt( ) é um método
que processa input do teclado. Este imput vem em forma de String e por vezes tem que ser
convertido para o type correcto que pretendemos.
Na linha 3, devido ao facto de que o nome viria em forma de string e por isso entre aspas, isso não
funcionaria porque JavaScript não iria atribuir o nome "tony" ao objeto tony. Devido a isso
atribuímos ao variável n o valor de uma propriedade de window com o mesmo nome que, como
sabemos existe um objeto chamado tony. Para isso utilizamos window[propriedade] que devolve o
nome de tony.
Na linha 5 escolhemos a opção de "senhor" ou "senhora" dependendo se a escolha foi m ou f.
Na linha 6 convertemos a entrada da linha 5 para letra minuscula para que tenhamos a certeza de que
o variável s seja em minúsculo na próxima etapa de processo (linha 7 a 12).
Linhas 7 a 12 servem para escolher o output que desejamos: "senhor " ou " senhora ".
E finalmente na linha 14 devolvemos o formato correto para chamar o método mensagem.
Se tentou este mini projeto mas não funcionou, verifique o sintaxe. Tenha também a certeza de que os
dados dos exercícios anteriores ainda estão no console. Caso contrário este script não servirá de
nada.
2.6.3 Mais prática com call( ) e apply( )
Devido à importância destes métodos, vamos praticar mais alguns exemplos para cimentar o conceito
destes dois mecanismos.
1- A função que se segue faz o output de um número de identificação baseado nos parâmetros
que lhe introduzimos (o nome da pessoa que substituirá ‘this’, e o número de identificação que
substituirá ‘id’). Como verá em seguida, podemos chamar este método aplicando-o a quem
desejarmos e sem restrições seja um objeto ou um string (nome), utilizando o auxilio do
método call( ):
2- Chame a função aplicando-a ao nome de uma pessoa como "Tony" em type string (com
aspas), e com identificação 34567:
print_id.call("Tony","34567");
// devolve "Tony tem a identificação No. 34567".
Com vimos a cima, “this” foi aplicado ao primeiro argumento do método call(), e o número de
identificação “id” foi aplicado ao segundo argumento.
Claro que o script é simples e poderíamos utiliza este método para algo muito mais complicado
não é?
3- Vamos adicionar um terceiro parâmetro para aceitar outro argumento, validade, que
representa o dia em que a identificação expira. Repare que toda a nossa data tem sido em type
string incluindo o número de identificação porque não existem cálculos a fazer e serve apenas
para mostrar no monitor do computador. Mais tarde visitaremos o objeto Date onde veremos
como utilizar datas em JavaScript:
Figure 71 ficheiro em raw. icontemp.com/p3/71.txt
print_id2.call("Tony","34567","Janeiro 5, 2016");
// devolve "Tony tem a identificação No. 34567 que expira em Janeiro 5, 2016 ".
Podemos deduzir desta experiência que call( ) aplica os argumentos na sequencia em que são
apresentados à função e temos que codificar o output de acordo com esta sequencia.
Os argumentos apresentados a call() podem ser objetos, strings ou até funções.
Adicione ao console esta função que chama um nome interno qualquer, só para ilustrar o conceito
de incluir uma função em call():
Agora chame o script da figura 70 ( o mais simples dos dois últimos script apresentados
anteriormente) mas em vez de “Tony” como nome, substitua com uma chamada para a função z:
print_id.call(z(),"34567");
// devolve "Tony de Araujo tem a identificação No. 34567"
Antes de passar para outro tópico vamos visualizar uma função call() sob um ponto de vista
diferente do que temos mencionado mas bastante importante.
Considere o seguinte script que será explicado a baixo:
Figure 73
· Na linha 11 chamamos a função x outra vez aplicando-lhe o método call() mas desta vez
preenchemos o primeiro parâmetro com o objeto global (window) a quem a função x
pertence. Deste modo, o número 3 foi positivamente aplicado ao parâmetro ‘num’ por ter
sido o segundo argumento apresentado no call().
Conclusão: Desta experiência vimos que x(3) é atualmente um atalho para a expressão
x.call(window, 3). Melhor ainda, relembrando as nossas primeiras lições e para ser mais específico,
x(3) é um atalho para a seguinte expressão mais completa:
window.x.call(window, 3);
Nos últimos exercícios experimentamos com funções para testar o método call( ) onde o objeto era
um string (“Tony”), uma função, e também o objeto window (o valor natural por defeito).
Em suma, as nossas experiências deste capítulo mostram que funções não têm uma consciência fixa
sobre quem é o “this” pretendido, porque o “this” é determinado na altura de chamada da função,
baseado em quem (ou o que) está chamando essa função. Esta flexibilidade dá grandes
possibilidades a JavaScript. Nós poderíamos ter chamado x deste modo: x.call("y", 3) para ter a
certeza de que 3 era aplicado ao segundo parâmetro da função. Neste caso especifico "y" serviria
apenas para preencher um vácuo e não teria valor algum para além de validar a sintaxe.
Revisitaremos este ponto outra vez mais à frente.
Abaixo poderá encontrar uma ilustração de sumário sobre this aplicado a call( ) em diferentes
cenários:
Figure 74 SUMÁRIO
2.7 O objeto Math
O objeto Math é um dos objetos mais úteis da biblioteca de JavaScript. Ele é predefinido e estático,
contendo vários métodos e constantes que podem ser chamados a qualquer altura. Estático neste caso
significa disponibilidade na sua forma original, pronto a ser chamado à ação.
A biblioteca de Math não é só conveniente mas também muito mais rápida do que criar outras
alternativas porque é programada em linguagem mais chegada à máquina.
Diz-se que o objeto Math de JavaScript é mais poderoso do que uma calculadora de bolso. No
entanto temos que ter em consideração a precisão da mesma porque é limitada a 16 dígitos, o que é
mais do que suficiente para a maioria do trabalho que poderá ser feito em JavaScript.
Vamos praticar com exemplos no nosso laboratório para nos familiarizarmos com os métodos mais
comuns desta biblioteca. Depois deixarei um atalho para uma lista de todos os métodos disponíveis,
para referência futura.
Recomece o seu console de JavaScript e experimente com os exemplos que serão explicados
individualmente.
2.7.1 Math.random( ), floor( ), ceil( ), round( )
Random significa ‘acaso’. Experimente o seguinte método no console. Repare que o objeto Math
vem em primeiro lugar, seguido do método pretendido e colados por um ponto:
Math.random( );
// Devolve um número ao acaso entre zero (inclusivamente) e 0,9999 (isto é, 1 exclusivamente). A
base deste número aleatório é o relógio do computador.
1- Escreva no console (se ainda não o fez) Math.random( ); Uma vez que obtenha um resultado,
pressione a tecla com a seta indicando para cima, a fim de duplicar o método outra vez. Tente
por várias vezes este método e observe os resultados.
Devido ao facto de que Math.random( ) nos dá um resultado abaixo de 1, se necessitarmos
números maiores que 1 teremos que adicionar um número base ao resultado do método, para que nos
dê um resultado acima de 1. Por exemplo, se adicionarmos 5, teremos números entre 5
(inclusivamente) e 6 (exclusivamente), tal como 5.0767:
Math.random()+ 5; // teste - o umas poucas de vezes. Conseguimos números como por exemplo
5.2907, etc.
E se quiséssemos um número aleatório entre 1 e 10? Multiplicaríamos o resultado de Math.random()
por 10:
Depois de copiar o ficheiro em raw, chame o jogo uma variedade de vezes para testar os resultados.
Exemplo:
Math.round( ).
Na verdade como Math.floor arredonda sempre para baixo e Math.ceil arredonda sempre para
cima, por vezes necessitamos de um terceiro método que arredonde para cima ou para baixo baseado
na parte decimal do número.
Em JavaScript existe o Math.round( ). Não confundir o termo round com random, pois embora
tenham o mesmo som, eles nada têm um a ver um com o outro.
Math.round( ) arredonda para cima se o valor for >= .5, ou para baixo se o valor for menor que
meio.
2.7.2 Math.max( ) e Math.min( )
O Math.max( ) que é abreviatura para máximo, devolve o número maior de uma lista de números
introduzidos nos parâmetros do método, como por exemplo:
Math.max(6,9,23,57,3); // devolve 57
Math.max(5,10); // devolve 10
Math.max(-5,-10); // devolve -5
Poderemos sacar o número maior de um array se utilizarmos a nossa ferramenta favorita apply():
Codifique o seguinte array x:
var x = [3,6,9,1];
Math.max.apply(null,x); // devolve 9. Null foi só para preencher o primeiro parâmetro para
que JavaScript conseguisse ler o segundo. Como estamos debaixo do objeto window, em vez de
(null, x), poderíamos ter escrito (window, x) ou mesmo (x,x). O resultado seria o mesmo. Em resumo,
é importante incluir um primeiro parâmetro de forma a que JavaScript consiga aplicar o segundo
parâmetro que é o que nos interessa neste caso. A condição é que o primeiro parâmetro terá que ser
algo válido para evitar um erro de sintaxe. Como por exemplo, y não funcionaria, mas "y" já
funcionaria porque o console faria de "y" um string válido. É importante reparar que isto é um truque
para enganar JavaScript e força-lo a ler o segundo parâmetro sem morrer pelo caminho.
Math.min(6,9,23,57,3); // devolve 3.
Math.min.apply(null,x); // devolve 1 ( do nosso array x anterior)
Estes métodos são muito úteis. Eles podem substituir scripts muito mais complexos.
Um exemplo seria o script a baixo indicado onde um loop faz uma iteração para conseguir o valor
maior do meuArray:
Figure 76 ficheiro em raw. icontemp.com/p3/76.txt
Nós podemos simplificar este script utilizando Math.max com o auxílio de apply().
O seguinte script substitui o loop de cima a partir das linhas 4 a 8), aplicando max () ao variavel
maior (ficheiro em raw - icontemp.com/p3/76b.txt):
Math.pow(2,2);
// devolve 4. Porque (2 x 2 = 4).
Math.pow(2,3);
// devolve 8. Porque (2 x 2 x 2 = 8).
Math.sqrt(num) devolve a raíz do número apresentado como num:
Math.sqrt(9);
// devolve 3.
Math.abs(num) devolve o valor absoluto de um número:
Math.abs(-2);
// revolve 2.
Math.abs(null);
// devolve 0.
2.7.4 Constantes no Math, PI
Math.PI( ) devolve um constante de 3.14159 que é o quociente do valor de uma circunferência pelo
raio.
Um exemplo do uso de PI é quando calculamos a área de um circulo:
minhaData.getDate( );
// devolve o dia do mês. Exemplo: 22, se hoje é o vigésimo segundo dia do mês.
minhaData.getDay( );
// devolve o dia da semana. 0 para Domingo e 6 para Sábado.
Veja este ficheiro em raw para um exemplo em como redirecionar estes números para dias de
semana por extenso. (icontemp.com/p3/DateSemana.txt)
minhaData.getMonth( );
// devolve zero para Janeiro e até 11 para Dezembro.
Se desejar praticar um pouco, poderá criar um script para o dia do mês baseado no exemplo anterior.
minhaData.getFullYear( );
// devolve o ano de criação deste objeto (Exemplo 2014).
minhaData.getHours( );
// devolve a hora (0 – 23).
minhaData.getMinutes( );
// devolve o minuto exato do momento em que este instance de Date foi criado (0 – 59).
minhaData.getSeconds( );
// devolve o segundo exato de quando minhaData foi criado 0 – 59.
Em baixo encontrará um exemplo de script sobre como utilizar Date baseado nos métodos que
acabamos de cobrir:
Lines 1 - 11 representa uma função com nome aleatório de escrevaData que por sua vez chama o
objeto Date representado por minhaData na linha 5.
Depois a função vai concatenando os vários métodos de Date para completar um frase no final onde
mês, dia e ano se mostram em console.log.
Para aplicar este mecanismo chamamos a função escrevaData() na linha 13.
2.8.1 Convertendo objeto Date para string
Existem vários métodos para converter o formato Date de objeto para formato string. Vamos ver alguns destes métodos para ter um
ideia como eles funcionam.:
Vamos criar um novo instance do objeto Date . Para isso escolhi o mesmo nome que utilizamos nos exemplos anteriores:
minhaData; // devolve Wed Jan 22 2014 14:07:03 GMT-0500 (Eastern Standard Time).
E agora que temos um instance de Date em minhaDate podemos testar alguns dos métodos de
conversão a string:
Lab Work:
1- Vamos cria um novo objeto ou seja, uma lista não ordenada.
Desta vez vamos adicionar cada item em linha separada o que torna tudo mais agrável à vista. Isto
é uma técnica comum para criar objetos mais complexos porque simplifica a compreensão do
código. (copie o ficheiro em raw se lhe é mais conveniente):
Esta maneira de criar um objeto é prática mas mantenha em mente que, se tiver intenções de
duplicar (instanciar) este objeto aplicando-lhe outro nome, será melhor criar uma função
construtora que servirá de protótipo como já tivemos oportunidade de ver quando programamos o
formulário de aplicação de emprego aqui:
2.5.8 Funções como Construtores de Objetos, lembra-se? Revisitaremos este assunto outra vez.
Existem muitas maneiras de se criarem objetos. Mencionar todos estes caminhos para Roma é
uma fonte desnecessária de confusão e atrasa o entendimento e a adquirição de experiência
prática. Não sobrecarregue sua mente, aqui o menos representa mais!
2- Se ainda não o fez, passe o script acima mencionado para o seu console. Vamos testar alguns
conceitos importantes antes de iniciarmos a construção de algo mais complicado.
3- Chame o objeto:
y.Frutas = "limão";
Então um dia um amigo meu veio-me visitar e eu com orgulho lhe mostrei a minha lista de frutas
para indicar que "maçã" é realmente o meu fruto predileto,
5- e então chamei meu fruto da lista...
Agora se chamarmos o objeto y receberemos uma visualização da imagem vista acima. As nossas
frutas favoritas, os vegetais e grãos estão todos lá, mas existe um problema, nós também não
temos acesso a cada elemento da nossa lista. Privatizamos as propriedades do nosso objeto mas
não criamos nada para as poder utilizar. Nós queremos encapsular os nossos objetos, mas não é
necessário nos sentirmos claustrofóbicos.
Agora que o nosso objeto tem seus valores encapsulados (lembre-se deste termo) necessitamos
de criar um método que nos permita ler os valores a partir do exterior.
Como podemos nós expor para o mundo de fora o valor de um variável que seja dentro de
uma função e de forma privada?
Nós expomos uma propriedade que tenha um escopo de var, criando uma função interna que
devolva a informação. Se se lembra, já tínhamos feito isso antes, certo? Ainda se lembra de
closures, e o papagaio ou os balões com dois cordéis? Funções internas têm acesso a funções
externas que sejam suas mães. Podemos utilizar esta técnica para expôs data da função mãe ao
mundo de fora. As funções internas podem agir como assistentes de balcão num banco, são os
intermediários entre o dinheiro no cofre (função mãe) e a pessoa que requisita seu dinheiro.
2.9.1 Métodos acessores
Analise a função interna na linha 6 a 8 da figura de baixo. Depois leia os comentários:
Nas linhas 6 a 8 implementamos um método aleatoriamente chamado getFrutas, que nos devolve o
valor do elemento Frutas. Porém existem alguns problemas com esta implementação:
a) Devido ao facto de que estamos a utilizar y como um construtor de objetos, não podemos
chamar os seus métodos nesta sua forma corrente porque um construtor é apenas um template
ou seja, um formulário para objetos. Faz-me lembrar o objeto Date. Necessitamos de
instanciar um novo objeto para poder utilizar o seu método getFruta().
b) Uma vez instanciado um new object, necessitamos de ter a certeza de que o método getFruta
é aplicado ao this correto (ao este do novo objeto) e não a outro objeto. Então necessitamos de
adicionar o prefixo this como guardador do objeto que irá tomar posse quando duplicamos y
com outro nome. Algo que será assim: this.getfruta = .
Nota: Existem outras notas secundárias sobre a figura de cima que podem ser lidas no fim deste
tópico ou aqui: [1], [2].
No script acima resolvemos o problema mencionado em (b). Para resolver o problema de (a) temos
que instanciar um novo objeto.
9- Copie o script de cima para o seu console se ainda não o fez. Agora vamos instanciar um
novo objeto a partir de y a que chamamos meuY (ou outro nome qualquer):
[1]
Notas: Outro ponto a considerar é o que embora não seja mandatório, o nome y deveria começar em
letra maiúscula porque este é um construtor e não um objeto instanciado. É apenas uma convenção
mas merece ser mantida. No entanto, não quero que esse problema abafe os outros dois problemas
que estou cobrindo neste momento que são a) e b).
[2]
Notas: Poderá estar pensando qual será a razão porque não inserimos um return na função mãe (y)
para resolver o nosso problema de apanhar ‘maçãs’, isto é return getFrutas a partir da função y. Sim
poderíamos ter feito isso se a razão desta função y fosse apenas a de produzir o valor de frutas, mas
não é. Como resolveremos um return de outros elementos? Qual dos elementos queremos devolver
quando chamamos a função? Esta função é um objeto de onde podemos tirar informação mais
específica baseado na sintaxe de objetos ou seja listas não ordenadas.
2.9.2 Métodos de mutação
E se desejarmos modificar uma das propriedades privadas que esteja dentro do nosso objeto?
Antes de ter protegido as propriedades do nosso objeto, nós podíamos modificar essas propriedades
atribuindo-lhe outros valores (lembra-se maçã e limão?). A sintaxe seria assim: objeto.propriedade
= "outro valor";
Agora que as nossas propriedades têm o prefixo var e estão dentro de uma função, nós não temos
acesso direto às mesmas a não ser indiretamente, isto é, utilizando um método intermediário.
11- Crie um segundo método que faz o set (aplica, modifica) da propriedade Frutas para um
valor diferente se alguma vez a quisermos modificar, isto é, criamos um método que nos
oferece tal opção:
O método setFruta aceita um argumento no seu parâmetro novaFruta (ou outro nome dado) e atribui à
propriedade Frutas um novo valor que lhe passemos.
12- Agora que modificamos o objeto construtor e devido ao facto de que esta mudança foi feita
internamente (hardwired) e não em protótipo externo (mais sobre isso no próximo tópico),
temos que reinstanciar o nosso objeto meuY para que seja modelado a este y em vez do y
anterior:
Figure 83
Neste código teste, vamos desenhar uma aplicação fictícia para empréstimo onde revisitaremos
alguns dos conceitos importantes deste capitulo.
· Nome do objeto: emprestimoAp.
· Propriedades de input da aplicação: aplicanteNome, salario, emprestimo.
· Propriedade privadas: bankFilial, aprovStatus.
· Métodos públicos: submeter.
a) Uma pessoa preencherá a aplicação para empréstimo declarando seu nome, salário e quantia
de empréstimo desejada. Esta aplicação é bem simples. Salário apenas reflete um número
inteiro para facilitar o nosso cálculo. Não são necessários outros detalhes.
b) Haverá um método (submeter) que calculará a relação entre o (salário) da pessoa e a quantia
de empréstimo desejada (emprestimo). Se o salário da pessoa for pelo menos o dobro do
empréstimo desejado, o empréstimo será aprovado instantaneamente com a seguinte mensagem:
"Parabéns! Seu empréstimo de " + emprestimo + " foi aprovado." Por outro lado e ao
mesmo tempo, o valor do variável aprovStatus mudará de "invalido" para "Aprovado".
Else, se o salário da pessoa é menor do que a qualificação automática, a pessoa receberá a
seguinte mensagem: "Obrigado por ter submetido uma aplicação de empréstimo. Sua
aplicação está sendo processada. Entraremos em contato consigo brevemente." Ao mesmo
tempo o valor do variável aprovStatus mudará de "invalido" para "Em processo".
Para já é tudo. Mais tarde melhoraremos o script para tocar em outros tópicos importantes.
Você pode seguir as minhas direções abaixo indicadas ou codificar seu próprio script se assim o
desejar, comparando depois com as minhas notas. Eu comentarei cada etapa conforme vamos
avançando no processo. Será talvez melhor seguir os meus passos e mais tarde refazer o script outra
vez à sua maneira. Você é que sabe!
Irei começar pela secção (b) criando um script independente do projeto global. A razão é para que
possa testar cada etapa conforme vou codificando. Em seguida criarei o objeto, e então copiarei (e
adaptarei) o método submeter da secção (b) para que faça parte do objeto. (Um ficheiro em raw está por
debaixo da imagem).
Iniciar (parte b).
var aprovStatus = "invalido"; // invalido é apenas um string, não é nenhuma palavra chave. O
termo inválido ajuda-me a saber que este variável não foi ativado.
2- Agora criamos uma função para podermos testar o script. Mais tarde modificaremos a função
para que a possa integrar no resto da programação. Esta função a que chamarei para já de
submeter aceita dois argumentos através dos parâmetros salario e emprestimo. Estes
parâmetros serão mais tarde transferidos para o construtor do objeto emprestimoAp mas
agora servem de rampa de lançamento para o nosso teste:
submeter(5000, 1000);
// devolve " Parabéns! Seu empréstimo de 1000 foi aprovado. ".
submeter(1500,1000);
// devolve " Obrigado por ter submetido uma aplicação de empréstimo. Sua aplicação está sendo processada. Entraremos
em contato consigo brevemente. "
this.aplicanteNome = aplicanteNome;
this.salario = salario;
this.emprestimo = emprestimo;
3- Declare a propriedade privada bankFilial e atribua-lhe uma localidade:
this.submeter = function(){
Copie o resto do script desta função como o tínhamos anteriormente. Veja imagem e ficheiro em raw.
6- Teste: Coloque o script no console e veja se existe algum erro. Corrija os erros existentes e
depois tente a próxima etapa.
7- Instancie um objeto que qualifique para um empréstimo. O meu objeto chama-se tony com um
salário de 5000 e um pedido de empréstimo de 1000:
tony; // devolve emprestimoAp {aplicanteNome: "Tony", salario: 5000, emprestimo: 1000, submeter:function}.
9- O cliente tony preencheu a aplicação mas o pedido de empréstimo ainda não foi submetido.
Submeta esta aplicação:
tony.submeter(); // devolve " Parabéns! Seu empréstimo de 1000 foi aprovado. "
10- Chame a propriedade aprovStatus de tony:
tony.submeter();
// devolve " Parabéns! Seu empréstimo de 1000 foi aprovado. "
15- Verify se o status de aprovamento de tony foi realmente alterado:
Nós ainda não testamos o “else” do método submeter. Temos que o testar para ter a certeza de que
tudo funciona como deve de ser.
16- Crie uma outra aplicação para Maria como objeto maria. O seu salário é também de 5000
mas ela quer aplicar para um empréstimo de 3000 o que não será aprovado instantaneamente:
var maria = new emprestimoAp("Maria",5000,3000);
17- Submeta a sua aplicação:
maria.submeter();
// devolve " Obrigado por ter submetido uma aplicação de empréstimo. Sua aplicação está sendo processada. Entraremos em
contato consigo brevemente. "
18- Algum tempo se passou e agora Maria quer verificar o status do seu pedido:
function ( ) { } ;
Isto é uma função que se chama a si própria:
function ( ) { }( );
Os últimos parênteses à direita fazem com que a função se chame a si própria.
A função de cima como está escrita funcionará perfeitamente assim. Ela se chamará a si própria
quando o programa começa a executar. Porém, é comum embrulhar toda a função em parênteses
externos, para dar sinal visual a quem inspeciona o script, de que esta função irá se chamar a si
própria.
Como por exemplo assim:
(function( ){ })();
// devolve function ( ) { }
Voltando ao nosso projeto anterior o objeto emprestimoAp, poderíamos implementar esta técnica
no método submeter. Isso faria com que o método submeter fosse executado imediatamente após um
instanciamento da aplicação emprestimoAp, o que por sua vez modificaría automaticamente o
variável aprovStatus. para “aprovado” ou “em processo”.
A seguinte imagem parcial mostra onde os parênteses de chamada da função devem ser colocados:
Poderíamos abrir o objeto e atar um novo método ao seu mecanismo. Porém, muitas vezes esta não é
a maneira mais conveniente de o fazer. Por outro lado, métodos dentro de construtores não os mais
eficientes porque, se se lembra do que leu anteriormente, cada vez que instanciámos um novo objeto,
estamos a duplicar a data do construtor. Métodos são sempre idênticos, não é necessário duplicá-los.
Poupa-se muita memória ao programar uma só vez e depois apontar para essa programação quando
necessitamos de utilizar tal método.
Uma maneira mais eficiente é apontar para métodos já existentes a fim de os poder utilizar quando
necessário.
É uma boa prática adicionar métodos separadamente, colocando-os em elos prototípicos. No entanto,
se você prefere encapsular métodos em seus construtores em vez de os manter externos, existem
maneiras de os implementar dessa forma sem o problema de aumento de memória desnecessária.
Veremos algumas dessas implementações um pouco mais tarde, mas primeiro vamos introduzir (ou
reintroduzir) a maneira mais recomendável.
Pergunta: Baseado no que foi escrito acima sobre eficiência de memória, porque é que no nosso
projeto anterior, incluímos os métodos submeter e getAprovStatus dentro do objeto construtor
emprestimoAp, em vez de os colocar fora? Não é isso uma má prática? Sim, parece que é. A razão
porque assim o fizemos é porque estes dois métodos agem como getters para acessar data privada.
Não podemos utilizar métodos externos para ir buscar data privada a um objeto. Devemos sempre
tentar ser eficientes na nossa codificação. Neste momento, essa foi uma das maneiras mais corretas
de fazer tal implementação.
No entanto, na maioria dos casos, novos métodos serão implementados separadamente.
Vamos adicionar um novo método ao nosso emprestimoAp como prática e ilustração de conceitos.
A nossa administradora vem ter conosco um dia e pergunta se existe uma maneira de ela conseguir
um sumário sobre cada aplicante do programa emprestimoAp. Respondemos que ainda não existe
um método para isso, mas que nada é impossível desde que ela aprove implementações no programa.
A administradora aprova a mudança e nós regressamos ao nosso cubículo, arregaçamos as mangas e
lançámo-nos ao trabalho.
1- Crie um método de protótipo com nome de sumario que imprima o nome do aplicante e o
empréstimo pedido: (Ver ficheiro em raw na etapa No. 5).
emprestimoAp.prototype.sumario = function(){
2- No corpo da função codifique o seguinte:
delete this.x;
Agora podemos adicionar nova funcionalidade ao objeto. A razão para não o ter feito antes de
atribuir emprestimoAp a x foi para prevenir que a funcionalidade herdada apagasse a funcionalide
existente. Daí só implementar nova funcionalidade a baixo do delete x.
5- Adicione o nome da nova filial:
this.getBankFilial = function() {
return bankFilial;
};
7- Feixe o objeto emprestimoAp2:
}
Em baixo poderá ver a imagem do novo objeto. Com vê ela é mínima porque muita da funcionalidade
é herdada do objeto original emprestimoAp.
Figure 88 ficheiro em raw. icontemp.com/p3/88.txt
14- Se copiou o método externo da etapa No9 terá também disponível o método sumario. Vamos
ver se é verdade:
tony.sumario();
// devolve " TypeError: Object #<emprestimoAp2> has no method 'sumario'"
Ooops! A razão porque não temos acesso à lista de protótipos de emprestimoAp é porque o objeto
construtor emprestimoAp2 tem a sua própria lista de protótipos. Cada Objeto tem a sua lista a não ser
que sejam instanciados de outros objetos, isto é, automaticamente herdam a lista do objeto mãe.
Quando um objeto herda funcionalidade de outro objeto via masquerading temos que adicionar uma
linha de código que redirecione também a lista prototipa, isto é, se assim o desejamos.
Temos que ligar a lista prototipa de emprestimoAp a emprestimoAp2. Depois, objetos instanciados
de emprestimoAp2 funcionarão perfeitamente:
15- Copie o seguinte script para o console. A cadeia protótipa de emprestimAp ficará disponível
de imediato para novos aplicantes. No entanto, aplicantes existentes não terão este privilégio e
por isso teremos que reaplicar tony para que ele tire proveito dos métodos na cadeia
prototípica:
tony.sumario();
// devolve " Tony aplicou para um emprestimo de 1000 ".
“Eureka!!!”.
Pergunta: O que acontecerá se agora declaramos null o nosso emprestimoAp (original)? Lembra-se
do papagaio que estava sendo segurado pelo menino e pela menina? Se declaramos o velho
construtor como null, o novo construtor ainda mantém sua garra na funcionalidade que herdou. Mas o
mundo não é perfeito. Se emprestimoAp deixar de existir, o variável x não poderá ser implementado
em novos instanciamentos, isto é, não será possível preencher uma nova aplicação de empréstimo
porque o intermediário x não tem acesso a emprestimoAp.
Mas a ideia não é de destruir o primeiro objeto construtor, a ideia é apenas a de não repetir
funcionalidade que já exista no sistema.
Sumário: Usar a técnica Masquerade (que se poderá traduzir por “esconder o objeto existente a trás
de uma máscara”) é uma boa maneira de delegar funcionalidade para evitar repetição. Mas temos que
ter a certeza de redirecionar a cadeia de protótipos para poder utilizar métodos externos do objeto
construtor original, porque mascarading só faz a herança de propriedade e métodos internos.
O administrador da filial de Los Angeles ficou muito contente com o resultado e nós também!
Neste tópico nós visitamos o processo de se criarem objetos ou seja, listas não ordenadas. Tivemos
oportunidade de verificar como encapsulamento torna o código mais eficiente, e ponderamos sobre
maneiras de poupar memória através de heranças, assim como a vantagem de utilizar a cadeia de
protótipos. Estes conceitos são importantes no paradigma de JavaScript e todos eles bastante úteis.
Antes de abandonar este tópico, existe um outro assunto de grande importância sobre encapsulamento
que poderá ter interesse, especialmente se vem de outra linguagem de programação:
Como podemos incluir métodos dentro de um construtor sem criar duplicação e ineficiência? Até
agora vimos como incluir métodos de protótipo em scripts externos ao construtor, o que é uma
prática recomendável. No entanto é possível incluir estes métodos dentro do construtor e ao
mesmo tempo evitar duplicação. No próximo tópico iremos ver uma maneira de se conseguir tal
proeza. Até depois.
2.9.7 Como incluir métodos protótipos dentro de um construtor
Às vezes queremos manter propriedades e métodos juntos dentro do construtor por qualquer razão,
seja ela por conveniência, questões de preferência estética, ou porque assim estamos habituados de
outras linguagens. Não porque existe algo errado no que temos feito até agora, o que até é
recomendável; mas vou incluir uma outra opção aqui em caso que esteja interessado em a utilizar.
Vamos criar um objeto simples para ilustrar como incluir métodos dentro de um construtor de
maneira que não sobrecarregue a memória (a outra alternativa é programa-los no exterior do objeto,
colocando-os na lista da cadeia prototipa como fizemos até aqui).
Este objeto será chamado Musico e contem duas propriedades: nome e instrumento. Depois criamos
um método que nos mostre um sumario sobre o músico. Simples, certo?
2- Instancie um Musico:
O operador typeof permite verificar o type do elemento a que o aplicamos, mesmo que a data seja
undefined. É este último que nós queremos encontrar.
Convencionalmente, quando utilizamos um caractere _ (underscore) como prefixo do nome de um
variável, exemplo _abc, significa que este variável é um variável interno ou especial. Isto claro
serve apenas para ilustrar a quem inspecione o script que este variável é fora de comum, porque
JavaScript não presta atenção a essas coisas.
Nós vamos implementar um passe mágico no nosso objeto. Vamos pedir a JavaScript que siga em
frente com o instanciamento de métodos só se tais métodos não existem já em memória. Conseguimos
esta proeza ao implicitamente fazer tal pergunta, isto é, perguntar a JavaScript se “variável x” é
undefined, siga em frente. Mas claro que x está indefinido porque é a primeira vez que mencionamos
tal variável. JavaScript nunca tinha ouvido falar nele até nós fazermos essa pergunta. Depois
codificamos os nossos métodos e finalmente atribuímos ao variável x o valor de true, tornando-o
defined. Ao tornar x defined, na próxima vez que instanciámos um objeto dentro do mesmo ambiente
de execução, a pergunta a fazer a JavaScript será false e então estes métodos não serão duplicados.
Tantas palavras para tão pouca coisa! Vamos ver como isto funciona em prática:
5- Adicione o seguinte condicional if mesmo antes da definição dos métodos do objeto:
Na linha 5 incluímos uma condição onde se pede a JavaScript para seguir em frente se um tal
variável _xyz ainda não foi definido (ler preâmbulo a cima da imagem). Depois na linha 9
atribuímos a _xyz o valor de true para que na próxima vez o tal condicional seja definido e assim
JavaScript saltará o script de implementação de métodos. Repare que aqui estamos a inserir o
método sumario na lista prototipa, caso contrario não será visto por objetos instanciados depois do
primeiro instanciamento. O ‘this’ é então aplicado ao output em si (veja conosle.log) porque não
existe ‘this’ ligado ao nome do método como tinhamos feito na figura anterior.
Claro que as propriedades serão sempre diferentes e por isso não nos temos que preocupar sobre
elas. A nossa preocupação é apenas sobre os métodos pois esses serão sempre iguais e queremos
evitar duplicação desnecessária.
6- Teste o nosso instance de objeto outra vez:
Pode-me encontrar como moderador no Codecademy, um sítio que visito todos os dias sem
exceção.
Meu profile no Amazon é: www.amazon.com/Tony-de-Araujo/e/B00D7V08WY/
Obrigado,
Tony de Araujo
--- New Jersey, USA
Copyright
Amazon, Google, Mozilla, ECMASCRIPT and all other famous names used in this book are of
their respective owners and being named only to illustrate the concepts written by the author.
All rights reserved © Tony de Araujo
"Be sure you put your feet in the right place, then stand firm". - Abraham Lincoln
Gentle Rain
Gentle rain, soothing me
Drops of liquid life
Wind and thunder
Wooing man and mice
Tony de Araujo