Concepts and Techniques, is a mini tutorial written and prepared by Elize, I described the foresight and techniques to be developed it, is written in Portuguese in BrazilDescrição completa
Contextualização da deficiência visual
Geografia - Conceitos e TemasDescrição completa
Concepts and Techniques, is a mini tutorial written and prepared by Elize, I described the foresight and techniques to be developed it, is written in Portuguese in Brazil
Descripción completa
Descripción completa
Descripción: Javascript presentation in Spanish
Descripción completa
JavaScriptDescripción completa
Descripción completa
a
Contiene todo la infromacion referente a la programación de javascriptDescripción completa
JavaScriptDescrição completa
Introducere in JavaScriptFull description
Description : Cours de javascript par Cabaré
Descrição completa
UFCD_0440_E-marketing - conceitos e fundamentos
Prévia
Descrição completa
JavaScript Objetos Arrays e Funcões Conceitos Importantes Tony de Araujo, Technical Writer New Jersey, Janeiro 2014
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 Representação de Cores Neste Livro Protótipos versus Classes O Conceito de Memória Variáveis versus Valores de Referencia Em JavaScript Tudo Vem de Objetos Lab 1 Lab 2 Mais Variáveis , Introdução de Funções Lab 3 Funções com Nome vs. Funções Anônimas 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 O Objeto Window – Intro Um exemplo de escopo Mais Valores Primitivos e Valores de Referência Como passar data de stack para o heap PARTE 2 2.1. Strings como Objetos em JavaScript 2.1.2 charAt( ) e charCodeAt( ) 2.1.3 fromCharCode( ) 2.1.4 concat( ) 2.1.5 indexOf() e lastIndexOf( ) 2.1.6 match( ) 2.1.7 replace( )
2.1.8 search( ) 2.1.9 slice( ) 2.1.10 split( ) 2.1.11 substr( ) 2.1.12 substr( ) vs substring( ) 2.1.13 toLowerCase() and toUpperCase() 2.1.14 trim( ) 2.1.15 valueOf( ) Sumário: métodos do Objeto String 2.2 Array como Objects em JavaScript 2.2.1 concat() 2.2.2 join( ) 2.2.3 reverse( ) 2.2.4 sort( ) 2.2.5 pop( ) e push( ) 2.2.6 shift() e unshift() 2.2.7 slice( ) 2.2.8 splice( ) 2.3 Novos Métodos de Iteração com Arrays em JavaScript 2.3.1 forEach() 2.3.2 map( ) 2.3.3 every( ) 2.3.4 some( ) 2.3.5 filter( ) 2.4 Novos Métodos de Redução e Localização para Arrays em JavaScript 2.4.1 O que é Redução 2.4.2 reduce( ) 2.4.3 reduceRight( ) 2.4.4 Métodos de Localização 2.4.5 indexOf( ) versus lastIndexOf( ) 2.5 Funções de JavaScript e Argumentos como Objetos 2.5.1 O objeto arguments 2.5.2 Funções dentro de Funções 2.5.3 Uma Introdução Gentil a Closures 2.5.4 O princípio de closure 2.5.5 Escopo Léxico 2.5.6 O objeto arguments versus funções internas 2.5.7 O variável “this” 2.5.8 Funções como Construtores de Objetos
2.6 Propriedades e Métodos de Função 2.6.1 apply( ) e call( ) 2.6.2 Adicionando mais argumentos a call( ) e a apply( ) 2.6.3 Mais prática com call( ) e apply( ) 2.7 O objeto Math 2.7.1 Math.random( ), floor( ), ceil( ), round( ) 2.7.2 Math.max( ) e Math.min( ) 2.7.3 Math.pow( ), Math.sqrt( ), Math.abs( ) 2.7.4 Constantes no Math, PI 2.7.5 Referências úteis: Objeto Math 2.8 O objeto Date 2.8.1 Convertendo objeto Date para string 2.8.2 Bibliotecas úteis de Date em JavaScript 2.9 Listas Associativas como Objects 2.9.1 Métodos acessores 2.9.2 Métodos de mutação 2.9.3 Revisão e prática: Criando uma aplicação para empréstimo 2.9.4 Como executar um método automaticamente 2.9.5 Prototipagem de novos métodos 2.9.6 Objetos que herdam de outros objetos 2.9.7 Como incluir métodos protótipos dentro de um construtor Em Conclusão Mais uma coisa… 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.
·
Lilás – O lilás é aplicado a títulos de capitulo e esporadicamente a frases de interesse.
Para conveniência do leitor, imagens de scripts terão links para um script real. O script será em .txt e no livro terá o nome de arquivo raw. Neste livro o autor utiliza o Console de JavaScript da Google Chrome. Uma vez no Chrome browser, pode acessar o Console clicando na sequencia de teclas CTRL+SHITF+j para as Windows ou CMD+OPT+j para Mac. Quando abrir pela primeira vez o Console, separe-o do browser carregando no quadradinho ao fundo na esquerda. Se não tem Google Chrome no seu computador, pode-o baixar através do seguinte endereço: Google Chrome. Não existe razão para memorizar absolutamente nada neste livro. Por favor siga as instruções dos exercícios discutidos em cada capitulo. Repetição dos exercícios é o segredo para dominar o assunto em questão. Cada tópico é curto e direito ao assunto. Muitas das questões serão repetidas conforme seja necessário e com uma explicação mais profunda na segunda ocasião. Uma cebola descasca-se em várias camadas. Os links externos serão apenas para sua conveniência e não para promover qualquer sítio que seja. Métodos terão link direto para a fonte original da Standard ECMA-262. Por vezes esses links levamnos para a página principal da ECMA-262. Isto é devido a algo errado no sítio em si. Geralmente se clicar uma segunda vez será redirecionado para o tópico em questão. “Esvazie seu copo, aceite uma nova ideia”, como todo o cinturão negro faz pela manhã em cada dia.
Protótipos versus Classes Porque o JavaScript é tudo sobre criar e destruir objetos, compreender a peculiaridade dos objetos de JavaScript é o passo mais importante para poder dominar esta língua. Vamos então revisitar JavaScript entrando pela porta do lado, embora a entrada menos utilizada nestas andanças. 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.
Figure 1 A picture is worth a gazillion words
Em JavaScript, valores primitivos são guardados no stack e valores de referencia são
guardados no heap. Lembre-se de “valores de referencia”, falaremos muito sobre esses valores. Não tente memorizar a informação que se segue. Apenas leia e compreenda. Nós revisitaremos todos estes conceitos em mais detalhe durante o processo de descasque desta cebola a que chamamos JavaScript. Valores primitivos em JavaScript refere-se ao type (tipo) de valores como Number, Boolean, String, Null e Undefined. Estes são valores simples e eles são guardados diretamente no sítio do endereço do variável (no stack). Variáveis são simplesmente rótulos ou símbolos que apontam para a data mais extensa no outro lado da memória, no heap. É tal e qual em álgebra em que x e y são variáveis apontando para um numero real. Valores de referencia por seu lado, têm o seu nome de variável guardado no stack (servindo de apontador para a verdadeira data). Esta data (valores de referencia) é guardada no heap. 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 16bits). 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”. Numbers, Strings, Booleans => JavaScript primitivos. Por outro lado quando falamos em valores de referencia, aqui estamos falando de definições de objetos. Outras linguagens utilizam classes. O ECMAScript Standard (lê-se ékma script) não tem classes na sua definição de sintaxe. ECMAScript é o nome do padrão da língua JavaScript. A versão corrente é a ECMA-262. JavaScript é a combinação do standard ECMA-262 mais o ambiente onde ECMAScript é aplicado, como por exemplo em cada browser. Browser é o leitor ou interface também conhecido por navegador que nos permite acessar a internet. Quando o ECMAScript se junta a um browser, o objeto principal conhecido por Global Object faz de interface entre as duas plataformas e essa combinação de objeto passa a ser conhecido como objeto window (nada a ver com Windows). Este é o pai ou a mãe de todos os outros objetos e é daqui que muitos métodos utilizados em outros objetos têm a sua origem. Veremos como isto funciona incrementalmente. Então no exemplo abaixo, se nós declararmos tony como um Object (o O é maiúsculo):
var tony = new Object(); Estamos a ceder ao variável tony certas ferramentas predefinidas para este objeto utilizar. Quero dizer com isto que tony herda um número standard de propriedades e métodos provenientes do biblioteca interna de JavaScript, ferramentas estas que estão na lista prototipa do Objeto Global nativo. Então, o que herda exatamente este novo objeto tony do Objeto Global de JavaScript? 1.
Propriedades: Uma vez declarado como objeto, o nosso novo objeto tony “herda (apontando para)” propriedades do objeto Global tais como:
(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.)
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.)
a) hasOwnProperty – Tradução: temSuaPropriaFuncionalidade? Este método verifica se o objeto em questão tem uma certa funcionalidade nativa dele próprio. Não serve para verificar se este objeto tem uma certa funcionalidade proveniente do pai objeto, apenas funcionalidade local. (por exemplo, se desejarmos saber se alguém criou uma propriedade chamada “nacionalidade” escrevemos assim (veja script em vermelho): será que nomeDoObjeto.hasOwnProperty(‘nacionalidade’); ? returns (devolve resultado) true (verdade) se a propriedade existe e é do próprio objeto. Se perguntarmos se tony.hasOwnProperty("toString");? returns false porque toString existe mas este é um método e propriedade do objeto mãe, o Object. Note: Nós experimentaremos com toString e todos os outros métodos mencionados muito em breve. Para já, siga em frente com sua leitura preliminar. b) isPrototypeOf - Tradução: éProtótipoDe? O método isPrototypeOf permite-nos verificar se o objeto em questão recebe seus métodos do protótipo (menu) de outro objeto, ou não. Temos que incluir o termo “prototype” para além da mesma palavra já incluída no sintaxe do método em si. Por outras palavras, “meuObjeto.isPrototypeOf(aqueleObjeto)” não funcionará porque JavaScript não compreende o que queremos dizer. Necessitamos de ser mais específicos da seguinte forma: meuObjeto.prototype.isPrototypeOf(aqueleObjeto); ? O que significa “ Pertence o protótipo utilizado pelo meuObjeto, aqueleObjeto?” Se o valor devolvido for true (verdade) o meuObjeto utiliza o protótipo do aqueleObjeto. É o mesmo que dizer que meuObjeto modela-se do aqueleObjeto. “Modelar-se de” poupa memória porque o meuObjeto não tem que recriar novas propriedades porque já existem em outro objeto a que meuObjeto tem acesso. (Note: verificaremos mais tarde que isto depende de como codificamos os nossos objetos). c) propertyIsEnumerable - Tradução: propriedadeÉenumerável? Este método é utilizado para determinar se uma certa propriedade é visível ao ponto de a podermos numerar (listar) com um script de listamento como por exemplo o ciclo for...in (loop). O for...in lista as propriedades do próprio objeto, não do seu ancestral, porque só as próprias propriedades podem ser
enumeradas por um script. d) toString( ) - Tradução: converterAString. Todos os objetos têm um método toString(). Este método é automaticamente chamado quando um objeto tem que ser representado com um valor de texto (string). Um exemplo é quando utilizamos o famoso console.log(), console.log(nome do objeto), ou então alert(nome do objeto). Conversão a String acontece quando uma representação em texto é necessária. Esta conversão não afeta o original. 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? A resposta é Não é. tony é uma etiqueta, um símbolo. Tecnicamente o símbolo tony é classificado como um variável (um apontador) que temporariamente reside no stack e aponta para um objeto instanciado (clonado mas com funcionalidade preexistente) de um modelo. Este objeto reside no heap e se alguma vez apagarmos ou redirecionarmos o símbolo tony, este objeto será enviado para o coletor de lixo onde esperará pela próxima lavagem feita pelo browser. Cada browser faz a limpeza de modo diferente. Esta limpeza melhora com cada nova versão do browser. Em JavaScript, quase tudo é um objeto. Todos os types primitivos, exceto null e undefined, são na realidade tratados como objetos. Eles são internamente convertidos a objetos cada vez que JavaScript necessita de um método para os processar. Isto acontece porque primitivos não têm métodos por questão de memória, mas cada type de primitivo tem um espelho, um tipo objeto no outro lado que lhes empresta o método temporariamente requerido. Quando a ação termina, o primitivo volta ao seu estado normal de primitivo deixando de ser objeto outra vez. E para resumir porque a repetição é a mãe do aprender, um primitivo pode ser convertido temporariamente em objeto e assim pode utilizar propriedades e métodos, uma regalia de objeto, com os seus próprios valores primitivos, mostrando todas as características de objeto. E é para esse efeito que JavaScript tem também valores de referencia equivalentes a cada primitivo, como objeto Boolean, objeto Number e objeto String que são automaticamente chamados quando necessário. Ver e estudar a foto embaixo, onde se visualiza o que acontece quando requeremos o método length aplicado a um primitivo:
Figure 3 (ver tabém Strings como Objetos em JavaScript)
Em JavaScript Tudo Vem de Objetos Lab 1 É altura de ligar o seu console favorito de JavaScript e praticar um pouco. Neste projeto utilizarei o console da Google Chrome. Quando chegar ao browser, abra o Console clicando no atalho de combinação das seguintes teclas CRTL+SHIFT+j para Windows e CMD+OPT+j para Mac. Uma vez no Console, se for a primeira vez, solte-o do browser carregando no quadradinho ao fundo esquerdo. A partir de agora ele estará sempre solto até você voltar a carregar no quadrado outra vez. Verá alguns erros no monitor mas esses erros não têm nada a ver com o nosso trabalho. Simplesmente ignore-os. 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). // O resultado é “number”. Claro que sim, não é?
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. // depois de codificar a etapa 7 tente o seguinte:
8- typeof y; // O resultado é “number”.
9- var z = new Object; // O é em letra maiúscula porque Object é um nome próprio que existe na biblioteca de JavaScript. Clique no ENTER e depois escreva o seguinte na próxima linha: 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.
// Mude de linha e depois entre:
12- typeof b; // O resultado é “object”. Agora ambos z e b apontam para o mesmo objeto. Vamos agora atribuir outro valor a z:
13- z = 123;
// z passou agora a ser do type number.
// Veja agora o type de 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. Note: A discrição mencionada acima só se aplica quando atribuímos valores de referencia a variáveis. Se por outro lado variável a é um string ou um number e nós atribuímos o valor a a b, como b = a, string a é copiado para b. Ambos variáveis e ambos valores são independentes e nunca partilharam nada em comum. Eles são ambos valores primitivos. Apontamentos só acontecem quando o valor está no heap e o apontador está no stack. Pergunta: 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): 1-
var x = "chapeu"; // Depois deste à esquerda declare mais outro variável:
2-
var cor = "verde";
3-
typeof x; // resultado é “string”. (Nada de novo até aqui!).
4-
typeof cor; // resultado é “string”. (o mesmo que em cima).
Você já ouviu dizer que em JavaScript quase tudo é um objeto, certo? Vamos então verificar! 5-
Introduza um outro variável. Desta vez iremos adicionar mais propriedades do que apenas uma propriedade singular no mesmo variável. Repare no ponto e vírgula à direita da chaveta de fechamento }; no primeiro exercício. Estamos declarando um variável com nome de camisola. Declarações sempre terminam com ponto e vírgula, mesmo quando depois de chavetas como é este caso. É uma forma de dizer a JavaScript que não existe nada mais a declarar:
var camisola = {cor: "branca", tamanho: "M", material: "algodão"};ro 6-
Veja o type de camisola:
typeof camisola; // resultado é “object” O que aconteceu aqui? JavaScript é inteligente e assim que reparou em chavetas decidiu logo que isto iria ser mais complicado e passou os valores para o heap fazendo deste string um objeto, e mantendo no stack apenas o apontador, o símbolo camisola. 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.
Ok, agora temos um objeto real, sim? Então vamos acessar os seus valores: 1-
camisola.cor; //resulto é “branca”.
2-
camisola.tamanho; //resulto é “M”.
3-
camisola.material; //resultado é “algodão”.
Este variável tem métodos e propriedades herdadas do modelo Object. Nós acabamos de utilizar um tipo de sintaxe conhecido como sintaxe de ponto porque liga os objetos a seus valores colocando um ponto entre eles e hierarquicamente da esquerda para a direta. No fundo, um objeto é um agrupamento de elementos mapeados por uma associação de chave-valor e com um apontador comum (o variável) que os liga a todos formando um grupo. Poderiamos também utilizar uma sintaxe de colchetes [ ] em vez de sintaxe de ponto, com esta: 4-
camisola["cor"]; //resultado é “branca”.
5-
camisola["material"]; //resultado é
“algodã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. Vamos então em frente? Escreva o seguinte no seu console (só o que está a verde): 6-
var estante = {"livros": 20, "CDs": 57, "Magazines": 19};
7-
estante.CDs; // resultado é 57. O número de CDs é 57.
8-
estante.livros; // resultado é 20. Temos 20 livros na estante.
9-
estante.livros = 21; // aqui adicionamos mais 1 livro
10- estante.livros; // resultado é 21. Agora temos 21 livros. Em cima adicionamos mais um livro à estante ao atribuir 21 livros em vez do original 20. Cool, certo? Este tipo de sintaxe de ponto permite a manipulação e controle do objeto. Chame agora o objeto para verificar a nossa mudança: 11- estante; // resultado é
Object {livros: 21, CDs: 57, Magazines: 19}.
Podemos também adicionar novos elementos ao objeto: 12- estante.audioBooks = 11; Chame o objeto: 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: 14- var x = "tony"; // e agora veja o seu type: 15- typeof x; // resultado é “string” mas repare em seguida que podemos utilizar métodos e propriedades de objeto: 16- x.length; // resulto é 4 caracteres. (de onde surgiu a propriedade length?) 17- x.substring(0,3); // resultado é
“ton”.
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. Voltando aos exercícios 18 e 19, o que aconteceu aí? Lembra-se da Figura 3 ? Parece que embora JavaScript não tenha mudado o string “tony” para o heap, fez uma conversão temporária a objeto, utilizando o objeto String, para se poder servir da propriedade length (comprimento do string) e o método substring().
Mais Variáveis , Introdução de Funções O que acontecerá se atribuímos uma função a um variável?
Lab 3 1- Escreva esta função num editor simples (Estou usando Notepad das Windows. Pode também utilizar um editor de texto na internet como por exemplo: editPad de Tom Churm). Copie o script para o Console de JavaScript (ou então pode copiar o meu ficheiro carregando no link raw file por debaixo da imagem):
Figure 4 arquivo raw. icontemp.com/p3/4.txt
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):
mais100(3); // resultado é 103
3- Entre: typeof mais100; // resultado é “function”. 4- mais100.length; // resultado é 1. Este número representa o número de parâmetros codificados entre os parênteses da função. Estes parâmetros servem para introduzir data à função. Embora length (comprimento) seja um método do objeto Object, neste caso é também uma modificação adaptada ao objeto Function e serve para determinar quantos argumentos esta função está programada a receber. Existe uma outra maneira de enviar argumentos à função mas cobriremos esse tópico mais tarde. Então nós passamos data para uma função através de placeholders* dentro dos parênteses ( ) e a que chamamos parâmetros (podemos ter até 255 parâmetros). Se o placeholder se chama parâmetro(s), a data em si chama-se argumento(s). (*Placeholder: aquilo que reserva um lugar para algo que virá depois).
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… Em JavaScript podemos ter declarações de variáveis tais como:
var x; // Uma declaração por si, só introduz o apontador. E do mesmo modo, podemos também ter declarações de funções tais como:
function y( ) { }; // Uma função com nome e corpo em branco. Por outro lado, declarações podem incluir expressões à sua direita, tais como:
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. Valores de type primitivo residem no stack e valores de referência residem no heap. Para de-referenciar um objeto nós declaramos o apontador (variável) como null. Para criar um objeto, nós instánciamos-o a partir do objeto Object (Global Object) com o operador new Object. Uma vez declarado, um objeto herda propriedades e métodos do protótipo que o criou assim como do protótipo do protótipo, numa cadeia que vai até ao objeto Global. Quando declaramos um variável que contem strings em pares de chave-valor, a data é guardada no heap em vez de no stack como se faz com um string simples. Esta data é acessível a partir do Stack e através de um símbolo (um variável) que aponta para a localização da data. Depois de feita esta ligação, podemos chamar cada valor utilizando uma sintaxe de ponto ou a sintaxe de colchetes [“data”]. Porque tudo em JavaScript é objeto, funções e arrays também o são, e por conseguinte todos estes elementos herdam funcionalidade do objeto Object. Quando declaramos variáveis ou funções, o seu nome é içado (hoisted) para o cimo do Scope dando sinal de vida ao interpretador de JavaScript. Mas as suas expressões não são hoisted, elas apenas serão avaliadas na altura de runtime quando o script lá chegar, embora JavaScript já saiba que elas existem devido à lista do topo. Uma função anônima é uma função atribuída como expressão a um variável. Funções anônimas são muito úteis porque podem ser colocadas em qualquer lado do script para enriquecer a funcionalidade do mesmo. Uma função nomeada é uma função declarada como function x() em vez de declarar primeiro um variável e atribuir uma função ao mesmo. Ela é listada no topo do scope como função, e não simplesmente como um variável que aponta para algo desconhecido.
O ato de uma função se chamar a si própria é conhecido como recursão. Muitas vezes recursão substitui mecanismos de ciclo (loops).
O Intocável Object Lembra-se onde reside um objeto? Objetos residem no heap porque são valores de referência.
E como acessamos a um objeto?? Nós acessamos a um objeto atribuindo-lhe um símbolo no stack que aponta para o objeto.
O que acontece ao objeto quando lhe removemos o variável? O objeto é posto automaticamente no coletor de lixo para ser removido pelo browser.
Poderemos tocar e modificar o objeto diretamente sem usar o apontador? Um grande Não! Temos que realmente utilizar o apontador (variável) para poder acessar e manipular o objeto. Através do apontador e de várias técnicas a partir daí, podemos manipular ou modificar o objeto. Neste momento já sabemos como um novo objeto herda certa funcionalidade de seu pai objeto, o modelo Object. Mas podemos também adicionar novos métodos ou modificar para nosso proveito os métodos já existentes. Teremos oportunidade de praticar técnicas diferentes nas nossas sessões de laboratório. Em revisão, objetos são criados por JavaScript quando atribuímos um variável a uma instância do Object. O variável reside no stack e aponta para o endereço no heap onde se encontra o objeto. Sem esta corda umbilical que conduz o programa para o objeto, não existe objeto. Se cortarmos esta corda umbilical fazendo um de-referencing ao variável (declarando-o null), o objeto deixa de existir. Mas espere! O objeto deixa de existir se este objeto foi realmente criado do nada. E o que acontece a objetos permanentes, tais como objetos nativos, objetos construídos pela plataforma anfitriã ou o Objeto Global em si? Estes objetos existirão sempre. Eles estão em standby esperando por uma chamada direta ou através de cálculos de script. A biblioteca permanente nunca vai embora. O seguinte conceito é importante: JavaScript não é só ECMAScript. JavaScript é a combinação de ECMAScript com o ambiente onde está sendo aplicado. Um dos exemplos é o browser onde ele faz interface com objetos do navegador, tais como o DOM e o BOM. O ECMAScript é implementado pelo motor de JavaScript, o interpretador. Existem vários motores a serem utilizados neste momento. Uma curta discussão sobre motores ou engines de JavaScript pode ser lido no Wikipedia. E uma versão mais completa pode ser lida aqui: Versão Inglesa.
Objetos Nativos Objetos nativos são os objetos que vêm de fábrica, isto é, do standard ECMAScript. Eles estão sempre presentes, prontos a serem chamados. Objetos nativos ajudam a especificar e a definir outros objetos. Estes objetos são chamados Objetos Globais Standard. Alguns dos objetos standard prefabricados e os mais conhecidos são os seguintes: Array, Boolean, Date, Function, Math, Number, Object, RegEx, String. Uma lista dos Objetos Globais Standard pode ser vista no Mozzila Developer Network. Infelizmente esta lista está em Inglês mas poderá também ir ao índex deste livro e selecionar seu método em Português com uma explicação muito mais acessível, produto de muitas horas meditando em cada tópico. É preciso não confundir o termo Objetos Globais Standard com o termo Global Object (o pai de todos os objetos, o objeto Object). Este objeto é o modelo ou protótipo que já mencionamos anteriormente no capítulo Variáveis versus Valores de Referencia. Na verdade nós nem podemos nos referir a esse nome diretamente porque o nome muda dependendo do ambiente onde ECMAScript está sendo utilizado. Por exemplo, no browser ele assume o nome de window. window é o primeiro objeto, (a raiz de JavaScript) e todas as declarações globais (no topo do escopo) como por exemplo var x;, são ao nível de escopo de window, isto é, sobe o ambiente de window ou seja ao nível do objeto Global. Experimentaremos com isto no próximo trabalho de laboratório. Alguns dos objetos nativos, tais como String e Math, permitem acesso aos seus métodos e propriedades instantaneamente sem ter que instanciar um novo (new) objeto. Outros objetos nativos, como por exemplo Date, requerem que criemos uma instância do objeto, antes de podermos utilizar seus métodos e propriedades.
Objetos Hospedados, Hosted Objects Outros objetos são provenientes to ambiente que implementa ECMAScript, o host environment que faz interface com ECMAScript para completar o que chamamos de funcionalidade JavaScript. No browser, ou navegador de internet, outros objetos são por exemplo o DOM (o ambiente do documento) e o BOM (o ambiente do browser). 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. Se no nosso console depois escrevermos x; o resultado será “tony”. Se em vez de x; escrevermos window.x; o resultado continuará a ser “tony”. “tony” é um valor do objeto window e x aponta para ”tony”. Se por outro lado tivermos uma função como por exemplo:
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:
1. var meuTexto = "Olá Mundo!"; 2.
meuTexto; // retorna "Olá Mundo!"
3.
window.meuTexto; // retorna "Olá Mundo!" Isto resulta porque meuTexto é um variável que pertence ao objeto window.
O this substitui (aponta para) o objeto de quem o variável está sobe controle (em escopo). Neste caso em especial, substitui o objeto window. Falaremos mais sobre isso em breve. 7.
As várias possibilidades acima demonstradas ilustram a relação entre um variável ao nível de escopo global e o objeto a quem ele pertence, neste caso window. As funções no objeto window. 8.
Codifique a seguinte função no seu editor de texto ou utilize este (editpad.org) editor online, e depois coloque no console. (Pode copiar também, através do link de ficheiro raw colocado no rodapé da figura de baixo:
Figure 8 arquivo raw. icontemp.com/p3/9.txt
Depois de colocar o script no seu console, experimente as seguintes opções: 9.
x; // retorna function(){var texto2 = "Olá JavaScript";}; isto acontece porque não chamamos a operação da função que seria com o parentêses à direita, mas sim o conteúdo de x.
texto2; // retorna texto2 is not defined. (o variável texto2 não existe no escopo global. Apenas existe no escopo da função. O prefixo var dentro de uma função, faz com que este variável seja privado e só a função e seu escopo têm acesso ao mesmo).
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:
y; // retorna ReferenceError: y is not defined. A função efetivamente isolou e destruiu o variável temporário. Nós ainda temos o variável x para nos preocuparmos porque aponta para uma função e se não a iremos utilizar é um desperdício de memória, mas x é mais óbvio para nós e poderemos (se quisermos) atribuir-lhe o valor de null que o enviará para o coletor de lixo. Mas o variável i ficou perdido no tempo. Uma outra questão relacionada: Se tivessémos declarado y dentro da função mas sem utilizar o prefixo var, esse y seria global em vez de privado e nesse caso, teríamos o mesmo problema que estamos a ter com i. O y não seria destruído quando a função fizesse o seu return.
6- Chame i uma vez mais só para verificar:
i; // retorna 5. Continuará a lá estar até ao fim ou até nós o desqualificarmos manualmente. Em todo o caso, a ideia aqui é a de ilustrar como uma função pode isolar variáveis e pode ser aplicada não só para funcionalidade, como também para eficiência programática.
O objeto window não só é o Object Global que vem da ECMAScript, como também é o objeto central do browser, o BOM. Cada window representa uma instância do browser em si. O objeto window é o interface entre o núcleo da língua JavaScript e o objeto browser. Ele, o window, representa o escopo global para cada variável e função de uma página web. Nós podemos adicionar propriedades ao objeto window à vontade. Mas como window é um objeto interno estas propriedades adicionadas só têm validade durante a execução do nosso programa. Por outras palavras, não podemos modificar permanentemente objetos nativos ou hospedados, apenas podemos modificar objetos que criamos nós próprios ( mais sobre isso em outros capítulos).
Tente o seguinte para ver como funciona: 1- window.cor = "verde"; Note: Acima estamos declarando um variável com nome de “cor”, o que é o mesmo que quando declaramos um variável só por si sem incluir window porque window é assumido pelo JavaScript. No entanto, porque estamos simplesmente a falar de conceitos por favor escreva como está indicado para cimentar a nossa compreensão do tópico. 2- window.humor = "contente"; 3- window.idioma = "Português"; 4- cor;
// retorna “verde”.
5- humor;
// retorna “contente”.
6- idioma;
// retorna “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). 8- window.cor; // retorna ”verde”. Estudaremos as várias propriedades e métodos de objetos mais à frente. Esta informação serve para facilitar a compreensão do que virá a seguir sem ter que assimilar tudo ao mesmo tempo.
Um exemplo de escopo Só para ter a certeza de que compreendemos o conceito de escopo no ambiente de objeto window (o escopo global) e de escopo de função (o escopo local), vamos programar um pouco mais. Existem conceitos importantes neste pequeno exercício. 1- Declare um variável com nome carro e uma função x onde existirá um outro variável com o mesmo nome de carro (soa a conflito, não é?):
Figure 9 ficheiro em raw. icontemp.com/p3/10.txt
2- Chame a função: x(); Deverá devolver o seguinte output: meu carro é um Subaru "O outro carro é um Ford" Porque foram ambos os variáveis processados sem haver conflito? Ambos os variáveis têm o mesmo nome mas estão em escopos diferentes. O primeiro variável carro está no escopo global (window). Como declaramos outro variável com o mesmo nome dentro da função (o que não é uma boa ideia mas serve para ilustrar), JavaScript não poderá utilizar o variável que se encontra ao nível global porque utiliza apenas o primeiro que encontrar e como o interpretador lê de dentro para fora, o carro de dentro é o que JavaScript utilizará. Por outras palavras, normalmente este seria o resultado: meu carro é um Subaru "O outro carro é um Subaru" A maneira para poder utilizar ambos os variáveis do mesmo nome, como fizemos no nosso script acima, é chamar o variável exterior com o seu “path” completo, isto é, ligando o objeto através de sintaxe de ponto, ao variável que queremos apontar para o exterior (ver linha 6). Assim não haverá confusão e desta vez o JavaScript utiliza Ford em vez de Subaru. Poderíamos ter utilizado o this em vez de window? Assim: this.carro?
3-
Tente este script para ver o que acontece:
Figure 10 ficheiro em raw. icontemp.com/p3/11.txt
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? 1- Declare um variável: var x = "ninja"; 2- Adicione-lhe uma propriedade chamada cor:
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: 3- Entre x.cor; devolve “undefined”. O que aconteceu? Não a guardou, fingiu que sim mas não o fez! Na verdade, para se poder adicionar e guardar uma propriedade em JavaScript, nós temos que a enviar para o heap como referência. No stack não existem propriedades, só existem valores primitivos. Vamos então ver como isto se faz. 4- Declare outro variável mas desta atribua-o a um objeto:
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"; 6- Chame a propriedade: y.cor; // devolve “verde”. Ok, agora sim!! 7- Chame-a outra vez com o “path”completo que inclui window só para ter certeza:
window.y.cor; … // devolve “verde”. O que pensa que aconteceu? Se apontarmos um variável para um valor de referência, podemos adicionar propriedades ao variável, isto é, ao objeto para quem o variável aponta. Quando, ou se, apagarmos ou dereferenciarmos tal variável, o objeto e suas propriedades serão colocados automaticamente no coletor do lixo implementado pelo browser. Em suma, acabamos de criar um objeto dentro do escopo do objeto window. Este objeto herda todas as propriedades e métodos disponíveis por window, mas podemos adicionar nossas próprias propriedades que serão numeráveis. Veremos mais sobre este assunto à frente. Não podemos adicionar novas propriedades a um variável primitivo, mas podemos adiciona-las se esse variável apontar para um valor de referência.
Vamos agora considerar um outro conceito importante:
Passar data por valor, versus passar data por referencia. 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” 11- Agora mude x para samurai: x = "samurai"; 12- Teste x: x; // devolve “samurai”. 13- E teste z: z; // devolve “ninja”. O que pensa que aconteceu nesta troca de valores? Se em vez de string primitivo, x fosse um apontador para um objeto, quando atribuíssemos x a z (z = x), ambos x e z ficariam apontando para o mesmo objeto. Mas como x é um variável type string localizado no stack, z não ficou fazendo referencia para o mesmo valor porque z copiou o valor de x duplicando valores primitivos. Quando atribuímos variáveis primitivos a outros variáveis primitivos, nós estamos copiando valores e cada um é independente do outro. Assim, quando finalmente mudamos o valor de x para samurai, z ficou com o seu próprio ninja e não foi afetado pela mudança. 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).
Vamos ver como isto funciona: Você deve ainda ter o objeto y no console (se não tem porque já limpou, crie um novo objeto y seguindo as etapas 4 & 5 acima indicadas). Em seguida faça o número 14:
14- Teste y para ter a certeza de sua existência:
y; Deve devolver Object {cor: "verde"} 15- Atribua y a um novo variável b (assim teremos dois apontadores para o mesmo objeto):
var b = y; 16- Teste b:
b; devolve Object {cor: "verde"}. Agora tanto y como b apontam para o mesmo objeto, certo? 17- Adicione uma nova cor a y para ver se b também a recebe por referência:
b; // devolve Object {cor: "verde", cor2: "vermelho"} Como vemos, valores de referência não são copiados quando atribuímos o valor de um variável a outro. Porque são de referencia, cada apontador pode modificar o valor do objeto em comum. Aqui o variável atua como apontador e um valor de referência pode ter muitos apontadores. Se um dos apontadores modificar a data do objeto, os outros apontadores refletirão a mesma data modificada porque apontam para o mesmo objeto. Isto é muito eficiente mas devemos também ter cuidado se desejarmos dereferenciar o objeto (cancelar apontamento) porque não podemos apenas anular um apontador, temos que anular todos os apontadores que mantêm o objeto vivo se queremos cancelar o objeto. O que acontecerá a b se nós reatribuirmos y a outro objeto? Vamos experimentar: 20- Atribua um novo objeto a y:
y = {"cor": "violeta"}; 21- Teste-o:
y;
// devolve Object {cor: "violeta"}
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):
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 ===== “Aquilo que temos de aprender a fazer, aprendemos fazendo. " --- Aristóteles, Da ética a Nicómaco
Uma boa palavra deixada no Amazon sobre o que já leu até aqui é um bom incentivo para o autor continuar a escrever. . Por favor faça-o agora
2.1. Strings como Objetos em JavaScript Até este ponto, temos estado a cobrir strings de JavaScript (como palavras e frases) como valores de type primitivo. Sabemos também que os strings são imutáveis (valores fixos) e residem na seção de memoria a que chamamos o Stack. Se editarmos um string, o conteúdo do variável é trocado por uma versão mais nova e o string anterior é automaticamente apagado. No entanto, existe realmente um objeto chamado String. O protótipo está lá na biblioteca de JavaScript, esperando que seja chamado a qualquer hora. Na verdade JavaScript chama muito frequentemente o objeto String quando necessita de o utilizar para processar os strings primitivos. Lembra-se da Figura 3? Todos os métodos e propriedades do objeto String estão automaticamente disponíveis às ordens dos valores primitivos. Nem é necessário instanciar um objeto para os poder utilizar. Antes de continuar em frente, vamos definir dois termos no nosso vocabulário. O termo “literal” é utilizado para designar valores simples que podem ser expressos como texto do programa. Um exemplo seria “maçãs e bananas”. Um literal de type string é composto por zero ou mais caracteres encapsulados entre aspas (simples ou dobradas). Embora em JavaScript não exista diferença entre aspas simples ou dobradas, as aspas dobradas são mandatórias numa subseção de JavaScript conhecida como JSon (JavaScript Object Notation) e muitos desenvolvedores começam a estandardizar este formato em tudo o que programam com JavaScript. É um hábito que eu tenho tentado adotar. O outro termo é método. Um método é o que programadores chamam a um agrupamento de funcionalidade específica que atua em nome de um objeto. Um método é normalmente composto por uma função, ou uma coleção de funções que têm uma finalidade definida dentro do objeto. Em JavaScript um literal type string é normalmente um primitivo. Quando um método é chamado para atuar num string literal primitivo, o string é temporariamente e automaticamente encapsulado num objeto para que possa ser processado pelo método. Em baixo introduzo um exemplo de um string primitivo para ser utilizado nos nossos exercícios que se seguem, onde iremos demonstrar algumas propriedades e métodos existentes no objeto String. Como na primeira parte deste livro, a seção em verde corresponde ao que escrevemos no console de JavaScript: Ligue o seu console de JavaScript, eu estou utilizando o Google Chrome, e introduza o nosso primeiro variável, x:
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:
a) Propriedades de String tais como length, prototype, construtor: 1- length O length significa comprimento e devolve o número de caracteres do string, incluindo espaços em branco:
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:
String.prototype.gostos = "adoro bananas"; (note que o S do String é capitalizado. Isto devido ao facto de que objetos nativos têm nomes próprios). Aqui a ordem de esquerda para a direita é com de pais para filhos. String é o topo da árvore, prototype vem a seguir e finalmente escrevemos o nome do novo método. 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:
x.gostos; // devolve "adoro bananas". A propriedade gostos tornou-se parte do string x e pode ser chamada utilizando a sintaxe de ponto. O variável x continua a ser um string primitivo, e você podemos confirmar que o é chamando o typeof, que nos devolve o type string (em letra minúscula), não o objeto.
typeof x; // devolve "string". Então resumindo, quando olhamos para a nossa sintaxe, nós vemos uma propriedade chamada “gostos’ que foi prototipada (criada) no protótipo do objeto String. “gostos” faz agora parte de
uma lista de propriedades disponíveis a todos os strings primitivos desta seção atual do programa, ou seja neste contexto de execução.
Se eu declarar outro variável, será que herdará automaticamente a nova propriedade? Vamos verificar. Declare o seguinte variável:
var y = "Laranjas"; Chame a propriedade gostos para o variável y:
y.gostos; // devolve “adoro bananas” Então a resposta é sim, qualquer variável tipo string herdará esta propriedade.
E se o variável for de type number em vez de string? Introduza um outro variável mas de type number:
var z = 123; Agora chame a propriedade gostos aplicada a z:
z.gostos; // devolve undefined. Com certeza que já desconfiávamos disso porque esta propriedade pertence ao objeto type String, não ao objeto type Number. Mas cuidado porque esta nova propriedade é temporária, só se aplica a esta execução. O objeto String, que é nativo de JavaScript, não pode ser permanentemente modificado. Isto é feito de propósito por questões de fidelidade programática e segurança. Métodos herdados serão cobertos mais abaixo. Mas por outro lado, e se quisermos adicionar um novo método ao objeto String para podermos utilizar nos nossos variáveis atuais? Vamos criar um método insignificante só para ver como isto functiona. Este método duplica o string (veja o ficheiro raw se não quiser escrever manualmente):
Figure 12 ficheiro raw. icontemp.com/p3/13.txt
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:
x.repetir(); // devolve "bananasbananas". Agora chame-o ao variável y:
y.repetir(); // devolve "LaranjasLaranjas". Com vemos, também é possível adicionar métodos a objetos de String e aplicá-los a string primitivos. Nós podemos ver a lista de propriedades e métodos, utilizando um truque que não é standard. Esta lista não mostra métodos internos, apenas os que nós desenhamos:
x.__proto__; // qualquer variável dá para ver a lista. Pode tentar y também: y.__proto__; devolve " String {gostos: "adoro bananas", repetir: function} ". O termo a utilizar é proto e mais 2 subtraços na esquerda depois do ponto e 2 subtraços na direita. __proto__ não foi estandardizado. No novo ECMAScript 5 este método será trocado pelo seguinte método que pode ainda não estar disponível no seu browser: Object.getPrototypeOf(x); // onde x é o objeto que estamos a investigar (o nosso x). Para praticar, adicione algumas novas propriedades e métodos ao objeto String através da propriedade prototype e chame x para cada um deles. Pratique, pratique e pratique. E não esquecer também que x é uma propriedade do objeto window. Bem sabemos que x é um string que herda métodos do objeto String, mas x é também uma propriedade do objeto window. Poderiamos ter codificado todos os nossos scripts utilizando window como prefixo. Assim: window.x.gostos;. Antes de deixar este tópico vale a pena manter em mente o seguinte: Quando criamos um método de protótipo para um objeto desenhado por nós, este método é guardado na lista de protótipos do objeto. Isto significa que qualquer instaciamento deste objeto hoje, amanhã ou depois, terá acesso ao novo método. Isto não se aplica a objetos nativos porque nós não podemos modificar permanentemente um objeto nativo. O que prototipamos manualmente num objeto de fábrica (nativo), apenas se aplica ao momento atual. Uma vez que o programa deixe de executar, o método desaparece do sistema. Isto é feito por razões de segurança. 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. charAt( ) significa o caractere na localização(número da localização);. charCodeAt( ) significa o código de caractere na localização(número da localização);. Vamos ver alguns exemplos: 1- Declare a variável x:
var x = "bananas"; 2- Chame o método charAt( ):
x.charAt(2); // devolve “n” ( começa contando do zero à esquerda para a direita). 3- O método acima demonstrado dá o mesmo resultado que quando chamamos a posição do caractere utilizando a sintaxe de chaves como em arrays. Tente:
x[2]; // devolve “n” . charAt( ) é um novo método e pode não funcionar em browsers com versão já expirada. 4- Tente o outro método de String:
x.charCodeAt(2); // devolve o Unicode de “n” que é 110. Os pontos em Unicode variam de 0 a 1.114.111, e os primeiros 128 pontos de Unicode coincidem com os caracteres em ASCII. pt.wikipedia.org/wiki/ASCII Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre estes métodos: ·
2.1.3 fromCharCode( ) O método fromCharCode( ) é o inverso do método charCodeAt(). Este método é bem simples. Devolve o caractere baseado no código de Unicode que lhe dermos como argumento:
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: ·
2.1.4 concat( ) O método concat( ), que significa concatenar ou juntar, substitui por vezes o operador “+” para completar frases ou gerar um novo string mais completo. Eis um exemplo: 1- Declare o seguinte variável se já não o tem co console:
var x = "bananas"; 2- Declare outro variável combinando dois strings (não se esqueça do espaço ante de são):
var w = x.concat(" são boas para si!"); 3-
Chame w:
w; // devolve " bananas são boas para si!". Concat( ) não altera o valor do variável original “x” a não ser que chamemos o método ao próprio x, como por exemplo (não necessita fazer) este script: x=x.concat(" são boas para si!"); o que é o mesmo que utilizar += em si próprio em vez de concat(). 4- O método concat é pratico em situações como por exemplo quando temos que juntar vários variáveis para completar um outro string:
var h = "Note: "; h = h.concat(x,w); // devolve “Note: bananas são boas para si!”. A maneira mais eficiente de concatenar em JavaScript é utilizando os operadores + ou += No entanto, o + é mais vagaroso do que += porque o interpretador de JavaScript cria temporariamente um novo string para cada dois strings que estejam separados por um + e isso leva o seu tempo. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.1.5 indexOf() e lastIndexOf( ) Tradução: índice de e último índice de. Por esta lógica, o indexOf deveria ter sido chamado primeiro índice de. O método de String indexOf, assim como o método lastIndexOf, devolvem a localização numérica de um item no string, baseado no valor especificado ao aplicarmos o método. O primeiro método começa a sua pesquisa no primeiro valor à esquerda do string, isto é, valor na localização zero, e devolve o primeiro exemplo que coincide com o que pesquisamos. O segundo método começa a pesquisa no último valor à direita e avança da direita para a esquerda, devolvendo o primeiro exemplo que se aplica ao critério de busca. Porquê a diferença dos dois métodos? Em strings extremamente longos, começar pela direita é por vezes mais rápido do que começar a busca pela esquerda, dependendo onde se encontra o que procuramos. Pontos a considerar. Se o caractere é único, ambos os métodos resultarão na mesma resposta. Se perguntarmos pelo ‘índice de “n” em “bananas” onde vários “n”s coexistem, indexOf devolverá a posição 2 indicando o primeiro n que encontra. Por outro lado, o resultado de lastIndexOf será diferente, devolvendo a posição 4 porque é a primeira que encontra vindo da direita para a esquerda. O número de posição cronológica é o mesmo para ambos os métodos. Quer isto dizer que tanto a posição 2 como a posição 4 têm o mesmo significado para ambos os métodos embora pesquisem em direções opostas. O caractere b é posição zero para ambos os métodos, assim como o caractere s que é posição 6 para ambos os métodos. Para uma busca mais completa pode-se também utilizar o método search() que falaremos mais à frente. No entanto, indexOf é mais simples e mais rápido.
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.";
aba.indexOf("amarelo"); // devolve 6 que é a posição onde a palavra amarelo começa contando de zero.
3- Se não existe a palavra que pesquisamos, ambos os métodos devolverão -1.
aba.indexOf("amarelos"); // devolve -1. Este último exemplo pode vir a ser útil para decisões condicionais: if(aba.indexOf("amarelo") !== -1) Se o termo amarelo não existe em aba, siga este caminho. else, siga aquele caminho. Ver exemplo aqui: ficheiro em raw. icontemp.com/p3/indexof.txt 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 :
2.1.6 match( ) Tradução: corresponde a ( ), igual ou semelhante a. Este método pesquisa um string `a procura de um elemento que corresponda ao critério a pesquisar. match() utiliza expressões regulares para auxiliar a busca e devolve um array com o resultado. Exemplo de scripts: var xyz = "As maçãs e bananas são deliciosas";
xyz.match(/n/g); // devolve ["n", "n"] Os símbolos / / delimitam a expressão a pesquisar. O g indica que a pesquisa é global, isto é, procura todos os caracteres que correspondam a n e não apenas um só. 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 :
2.1.7 replace( ) Tradução: troca replace() é outro método de String da categoria pesquisa. replace() devolve-nos uma cópia do string mas modifica (troca) o termo indicado na especificação do primeiro parâmetro pelo que é especificado no segundo parâmetro. Expressões Regulares podem também ser utilizadas na pesquisa. Do nosso exemplo anterior: var xyz = "As maçãs e bananas são deliciosas"; Podemos trocar deliciosas por saudáveis:
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 :
2.1.8 search( ) Tradução: busca, pesquisa 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 :
2.1.9 slice( ) Tradução: cortar pedaço. O termo em si deveria ser copiar pedaço em vez de cortar. 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: var cores = "preta, vermelha, amarela"; 2- Atribue a outro variável a cor "vermelha" do variável cores:
var corPreferida = cores.slice(7,15); 3- Chame corPreferida; // devolve "vermelha" O v de vermelha está na posição 7 (inclusivo), e a virgula no final da palavra está na posição 15 (não inclusiva).
Variações do método slice(). a) Quando o Segundo índice é negativo. Se inserirmos um índice negativo no segundo parâmetro, JavaScript cortará respectivamente um número de caracteres da direita para a esquerda baseado no índice, e copiará tudo o resto. 4- Por exemplo, para remover a última cor amarela, assim com o espaço e a vírgula à sua esquerda, utilizaríamos um índice negativo de -9:
var duasCores = cores.slice(0,-9); 5- Chame o variável duasCores:
duasCores; // devolve "preta, vermelha".
Figure 13
b) Quando se usa um só índice. 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. var x = cores.slice(3); // devolve "ta, vermelha, amarela". Deixou o "pre". 7- Número negativo: copia o número de caracteres indicado no índice, do fim para o principio, isto é, da direita para a esquerda, e deixa tudo o resto. var x = cores.slice(-3); // x devolve "ela" de amarela. Extraindo caracteres de uma palavra com slice(): 8- Extrair o e de ecmascript (esta técnica é bem útil para capitalizar a primeira letra):
"ecmascript".slice(1); // devolve "cmascript". Nota: Ver também mais abaixo o método substr. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.1.10 split( ) O método split() é bastante útil e por isso devemos ter a certeza de que compreendemos o seu mecanismo. Split converte um string em um array. Arrays serão cobertos no próximo tópico. Nota: Para converter um array para string veja join( ). 1- Como exemplo, vamos declarar um novo variável com palavras separadas por um espaço (sem vírgulas).
var abc = "verde amarelo vermelho preto"; Vocabulário: Em informática, um delimitador é um símbolo (ou sequência de caracteres) que marcam o principio ou o fim de uma unidade de data, ou separação de tokens (símbolos). A primeira decisão a tomar quando queremos quebrar um string tornando-o num array, é a escolha do tipo de delimitador que iremos utilizar para criar a separação de termos. Isto é, na palavra fictícia de stuibacaitumako, se decidirmos separa em cada i, teremos um resultado de stu baca tumako. Cortando todos os is criamos a separação desejada. Então os is desaparecem nesta quebra. No nosso caso utilizaremos um espaço em branco para separar as palavras. O que isto quer dizer é que o método split( ) irá excluir todos os espaços em branco (os delimitadores). Tudo o resto no string será convertido em um array de palavras. 2- Vamos fazer o split ao string abc convertendo-o em um array onde a separação do string é feita nos espaços em branco (espaços são os delimitadores). Vamos colocar este novo array num variável com nome de abc2:
var abc2 = abc.split(" "); 3- Chame o variável: abc2; // devolve ["verde", "amarelo", "vermelho", "preto"]. O espaço em branco serviu de delimitador e cada palavra foi convertida em elemento individual num novo array. Lembre-se que o delimitador é excluído do novo array. Por outro lado, o método split() aceita também um segundo parâmetro. O primeiro parâmetro é o delimitador, o segundo parâmetro determina quantos elemento queremos transferir para o novo array. Isto é, se o segundo parâmetro é 1, apenas a primeira palavra será incluída no novo array. Vamos experimentar: 4- Declare o variável: var abc3 = abc.split(" ",3); 5- Chame abc3:
abc3; // returns ["verde", "amarelo", "vermelho"], não incluiu o “preto”. Note: Quando utilizamos split() temos que passar pelo menos um argumento, o delimitador. Se não declararmos um delimitador na função split, o resultado será a totalidade do string mas sem
separação de elementos, isto é, tudo será uma só unidade. De certa forma isto é uma maneira de converter um string para array, mas provavelmente não será muito útil sem a separação dos elementos 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. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.1.11 substr( ) O método substr() é um alternativo do método slice(). A diferença está no significado dos parâmetros indicados. Enquanto que slice() aceita o índice da posição de começo e o índice da primeira posição a ser excluída, os dois parâmetros do substr() indicam o começo e o numero de caracteres a serem incluídos, isto é, o segundo parâmetro é o comprimento, ou length do string a ser incluído no extrato.
Quando utilizar slice versus quando utilizar substr? Utilize slice() quando sabe de antemão a posição do primeiro caractere a incluir e a posição do primeiro caractere a excluir. Utilize substr() quando sabe o comprimento (length) do string a ser incluído no extrato. Com ambos os métodos necessita de saber a posição do primeiro caractere. Num substr(), os parâmetros (4,9) indicam uma extração que começa na posição 4 (a contar de zero e contando espaços em branco, isto é, 4 é o quinto caractere). A extração acaba no caractere 12 (inclusivo) o que significa o décimo terceiro caractere: 4,5,6,7,8,9,10,11,12.
Exemplo (o mesmo exemplo utilizado anteriormente em slice): 1- Declare o seguinte variável: var cores = "preta, vermelha, amarela"; 2- Vamos extrair (copiando) a cor vermelha que se encontra na posição 7 e tem um comprimento de 8 caracteres:
var corPreferida = cores.substr(7,8); // A posição 7 é realmente o oitavo caractere porque posições começam a contar de zero.
corPreferida; // devolve "vermelha". Note: Em substr, o segundo digito, 8, representa o numero de caracteres a serem extraídos. Em slice, o segundo digito representa a posição do primeiro caracteres a ser excluído do extrato.
Figure 14 Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Substr não tinha sido publicado no ecma-262. Mas hoje em dia já se mostra num anexo da publicação: Annex B (link acima)
2.1.12 substr( ) vs substring( ) Aqui é onde existe alguma confusão porque os nomes substr e substring são bastante parecidos. Na verdade os métodos não são idênticos e hoje em dia este último método, substring() tem sido substituído pelo novo método slice. A certa altura programadores de JavaScript queriam implementar números negativos como maneira de se contar da direita para a esquerda, ou seja do ultimo elemento para o primeiro. O problema que se fazer tal mudança era a compatibilidade com programação já implementada em sistemas existentes e então resolveu-se inventar um novo método, slice(), mantendo o método antigo, substring() intacto. Além de não aceitar números negativos, substring não liga importância à ordem dos parâmetros. O que significa que assume sempre o numero menor como sendo o pimeiro parâmetro. Assim se codificarmos (5,4) significa o mesmo que (4,5). O método slice() é mais estrito, a ordem é significante e aceite tal como ela se apresenta. O que fazer? Utilize slice() mas esteja ciente da existência de substring() para que não seja confundido com substr(). Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.1.13 toLowerCase() and toUpperCase() O método toLowerCase() transforma todos os caracteres de um string para letra minúscula. Assim “ÁFRICA” transforma-se em “áfrica”. 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:
var txt = "ECMASCRIPT"; 2- Converta a letra minúscula: txt.toLowerCase(); // devolve "ecmascript". 3- Converta a letra maiúscula: txt.toUpperCase(); // devolve "ECMASCRIPT". 4- Eis uma maneira de converter só o primeiro caractere baseado em técnicas aprendidas até agora: txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase(); // devolve "Ecmascript". O primeiro caractere é selecionado com charAt(0) e convertido a maiúsculo com toUpperCase(). Depois concatenamos a string através do método slice (que retira o primeiro caractere, slice(1) e em seguida aplicamos toLowercase() transfomando o resultado de slice para letra minúscula. Lembre-se que se utilizarmos slice só com um parâmetro, slice retira os primeiros caracteres baseado no numero do parâmetros e devolve todos os outros da esquerda para a direita. 5- Por outro lado, em vez de utilizar charAt poderiamos chamar o primeiro caractere utilizando a syntaxe de chavetas: txt[0].toUpperCase() + txt.slice(1).toLowerCase(); // devolve "Ecmascript". Não esquecer que podemos também utilizar CSS para capitalizar a primeira letra de um parágrafo: p:first-letter { text-transform:capitalize; } Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método : ·
2.1.14 trim( ) O método trim() que significa podar ou aparar, foi introduzido no último standard ECMAScript 5. Este método poda os espaços em branco do principio e do final do string, criando uma cópia mais limpa. Como o método cria uma cópia, se nós desejarmos modificar o original em vez de atribuir o resultado a outro variável, teremos que aplicar o resultado ao próprio variável. Exemplos: 1- Declare um string com bastante espaço no principio e no final do mesmo:
Figure 15 ficheiro em raw. icontemp.com/p3/15.txt
2- Chame o variável só para ver como se mostra no monitor:
str1; // devolve o string com espaços no princípio e no final, certo? 3- Declare um segundo variável e aplique-lhe o primeiro variável filtrado com trim():
Figure 16
4- Chame o variável str2 para ver o resultado:
str2; // devolve “teste 123” sem espaços laterais, certo? 5- Chame str1 para ver como se parece.
str1; // devolve o original com espaços em branco. Como foi dito mais acima, se quisessemos limpar o original teriamos que atribuir o resultado ao próprio variável como indica na imagem a baixo:
Figure 17
6- Agora se fosse a chamar str1 outra vez os espaços teriam desaparecido. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.1.15 valueOf( ) O método de String valueOf() converte um objeto para um primitivo. Isto é feito frequentemente e automaticamente pelo JavaScript. No entanto por vezes necessitamos de chamar este método de propósito embora não aconteça muitas vezes. Vamos ver como ele funciona. Todos os objetos, incluindo String, descendem do objeto Object, e todos os objetos herdam propriedades e métodos do Object.prototype a não ser que estes sejam trocados por métodos ou propriedades locais. O método valueOf() é herdado do Object mas todos os objetos nativos de JavaScript tal como String, substituem ou modificam este método para que devolva um resultado mais apropriado ao type que representam. Quando chamamos valueOf ele devolve um valor da seguinte forma: Num Array, um instante de array; Num Boolean, um valor Boolean; Em Date, a data preguardada de meia noite em Janeiro 1, 1970 UTC; Em Function, a função em si; Em Number, o número numerico; Em String, o valor de string; Em objetos Math e Error nada é devolvido porque estes dois objetos não têm método valueOf; Por último, em objetos com valores primitivos, o objeto em si como um objeto Object. O sintaxe é o seguinte: nomeDoObjeto.valueOf( ). Quando chamamos valueOf em um variável tipo string, JavaScript encapsula o variável em um objeto e dá-nos o seu valor primitivo que é um string. Isto é mais utilizado para consumo interno do JavaScript do que para consumo propositado.
Sample code: 1- Declare a variable: var ppp = "amazon"; 2- Call valueOf: ppp.valueOf(); // returns “amazon”. Recapitulando este conceito, o valueOf() devolve-nos o valor primitivo de um objeto. Se um primitivo não existe, devolve-nos o próprio objeto referente. Cada objeto nativo modifica o método valueOf para condizer com o o type que representa. No caso de um String, devolve-nos o valor do string. Este método é frequentemente utilizado por detrás dos bastidores quando JavaScript necessita do valor do objeto em si. valueOf() é o método oficial que se utiliza para extrair um valor singular de dentro de um objeto. Outro método semelhante é o toString() que embora não seja necessário no caso de um valor type String, torna-se útil para podermos visualizar no output elementos de outros types de objetos, é uma forma de exibir o valor em si. Como se disse anteriormente, num objeto type Boolean, o valor será convertido para a palavra em string true ou false. Em alguns dos outros types, o resultado de valueOf() não terá grande utilidade para nós e não será aplicado. Este tópico será revisto em outros capítulos mais adiante. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :.
Sumário: métodos do Objeto String Em ECMAScript, o construtor de Objetos pode encapsular todos os seus equivalentes primitivos tornado-os temporariamente objetos com finalidades especificas. É como dizer que, quando um Object vê um primitivo a tentar ser utilizado como objeto, agarra-o e veste-lhe a camisa de objeto para que o método que está sendo aplicado funcione com sucesso. No fim da transação, tira-lhe a camisa outra vez e o primitivo passa uma vez mais a primitivo. Isto pode ser visualizado assim: new Object([primitivo]). Mais tarde, retirando a transformação da seguinte maneira: [primitivoComoObjeto].valueOf() (valueOf devolve o primitivo para trás). Iremos utilizar métodos de String mais adiante porque estes métodos são utilizados em todo o lado. Terá oportunidade de os revisitar mais tarde.
2.2 Array como Objects em JavaScript Arrays são objetos de listas ordenadas. Em javaScript, a ordem é que faz com que estes objetos sejam arrays porque os objetos no seu sentido mais comum são tipicamente não-ordenados, isto é, compõem-se de pares associativos sem ordem especifica. (Para listas não ordenadas veja o capítulo
2.9 Listas Associativas como Objects.)
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:
var bcd = new Array("verde","vermelho","amarelo"); O script acima, declara um variável de nome bcd que aponta para uma construção de type array constituída por três elementos. Repare no termo new. Embora pudéssemos deixar de escrever “new” quando declaramos um objeto array nesta forma de sintaxe, é recomendável incluir sempre a termo new quando utilizamos esta forma de expressão declarativa. Repare também na capitalização da letra A de Array tal e qual Object e objeto String. Em JavaScript, objetos nativos assim como objetos próprios da plataforma anfitriã como num browser, têm os seus nomes capitalizados porque são nomes próprios. Como JavaScript é sensível a letra maiúscula e minúscula, objetos têm que ser chamados tal e qual foram criados, isto é, neste caso a primeira letra é em maiúscula. JavaScript tem uma maneira mais rápida e prática de criar arrays, que é a sintaxe de parênteses retos. Em vez de var ijk = new Array( );. 1- É mais comum criarem-se arrays desta forma: var ijk = [ ]; // cria um array vazio. Os parenteses retos são um macro que substitui o new Array. Ao contrário de outras linguagens, não há necessidade de declarar o número de elementos do array porque JavaScript pode variar o número dinamicamente qando adicionamos novos elementos, e tal e qual como acontece com strings, JavaScript copia a data existente mais a nova data, criando um novo array como o mesmo nome. O array antigo fica sem validade e é apagado automaticamente.
2- Depois de ter declarado o array acima indicado no seu console, ijk, comece a adicionar elementos tais como:
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. 1- Num string var xyz = "bananas"; // xyz.length; // devolve 7. 2- No nosso array anterior, ijk.length; // devolve 3 por é ["vermelho", "branco", "azul"]. Length é muito útil quando temos que iterar por um array, o que significa inspecionar cada um dos elementos para poder fazer algo específico. O que acontece é que na maioria dos casos não sabemos o comprimento do array, e a propriedade length dá-nos esse valor automaticamente. Daí a razão de utilizar length em vez de um número especifico. O script torna-se mais portátil. No que diz respeito a length temos que ter cuidado com o seguinte: O length e a posição de cada elemento não coincidem porque a posição começa a contar em zero e o length começa a contar em um. A última posição de um array é sempre o seu length -1. Por vezes este conhecimento é-nos bastante útil na nossa programação. Outra propriedade de arrays é prototype. O construtor prototype facilita a adição de novas propriedades e métodos para um objeto de type Array. Lembre-se que tal e qual o prototype de String, o prototype de Array adiciona novos métodos e propriedades ao objeto Array() em si, não a um instante singular de array. Até este ponto e porque ainda não chegamos ao objeto Object, temos estado a estudar objetos nativos de JavaScript, não objetos criados por nós próprios. Continuaremos a discutir estes objetos já existentes por algum tempo ainda, porque eles são os mais importantes na utilização de JavaScript. Depois de cobrir os objetos nativos essenciais falaremos em criar os nossos próprios protótipos de objetos. As adições de métodos ou propriedades que criamos nos objetos nativos não são permanentes. Estes nossos métodos só se aplicam ao momento atual, dentro deste ambiente de execução. Vamos então inventar um método prototípico que poderemos utilizar com os nossos arrays neste contexto de execução. O nosso método utilizará o console.log para mostrar os elementos de um array quando o chamamos. A razão de criar este mecanismo de output é porque arrays, ao contrário de
strings, não faz diretamente um output dos seus elementos. Temos primeiro que os converter para strings. A razão porque chamo a este nosso método de prototípico é porque será criado não para um array específico, mas para ser utilizado em todos os arrays existentes nesta execução atual. 1- Para este exemplo utilizaremos o nosso array anterior. Se já não o tem no console copie o array mostrado abaixo:
var ijk = ["vermelho", "branco", "azul"]; Vamos criar um protótipo com uma função que chamará o length (comprimento) de um array a fim de iterar por cada elemento do array, e por cada iteração JavaScript fará o console.log de cada elemento:
Figure 18 ficheiro em raw. icontemp.com/p3/5.txt
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:
ijk.listArray(); // devolve vermelho brando azul. Você escreveu listArray desta forma listArray( )? métodos são funções e os parênteses são necessários com algumas excecões que veremos mais tarde. Só para provar que este método pertence ao objeto mãe Array e não a um array específico, vamos criar um novo array para ver se o método se aplica ao mesmo:
4- Declare um outro array:
var bcd = ["maçãs","bananas","uvas"]; 5- Chame o método listArray() a este novo array:
bcd.listArray(); // devolve maçãs bananas uvas. Resumindo, sabemos que a propriedade prototype é uma lista onde podemos adicionar propriedades e métodos para serem utilizados por replicas do objeto a quem o prototype pertence. Sabemos agora também como se adicionam tais métodos à propriedade prototype. Já o tínhamos visto aplicado a Strings e agora vimos como se aplicam métodos ao protótipo de Arrays. Não é necessário memorizar nada disto, aprendemos em camadas. Um elefante come-se dando uma dentada de cada vez, certo? Agora vamos aprender alguns métodos preexistentes para manipular arrays.
a) Métodos de Array: concat, join, reverse, sort, pop, push, shift, unshift, slice, splice Alguns destes métodos de array já foram discutidos ao falarmos do objeto String e serão familiares ao os revisitarmos outra vez. Repetição é a mãe da aprendizagem. Mas ao repetir tem sempre que haver alguma novidade que expanda o conceito que temos sobre o tema em foco.
2.2.1 concat() O método concat() que significa concatenar (adicionar), adiciona os elementos de um array ao final dos elementos de um outro array criando assim um terceiro array. O terceiro array não tem que ser inventado se o nosso intuito é apenas o de fazer um output da combinação de dois arrays. Vamos ver como isto funciona. Começamos com os nossos dois arrays anteriores. Se já não os tem no console, siga os primeiros dois passos do seguinte exercício: 1- Declare um array:
var ijk = ["vermelho", "branco", "azul"]; 2- Declare um segundo array:
var bcd = ["maçãs","bananas","uvas"]; 3- Agora vamos declarar um terceiro array e atribuir-lhe a soma dos dois arrays anteriores:
var abc = ijk.concat(bcd); 4- Chame o array abc:
abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas"] 5- Se chamar os dois primeiros arrays verificará que não mudaram em nada. Continuam a ter os mesmos elementos originais. É também importante saber que os elementos concatenados foram copiados ou duplicados, isto é, o terceiro array não é uma referência dos dois primeiros. Todos estes arrays são independentes uns dos outros. Se por acaso a nossa intenção fosse a de juntar um array a outro, teríamos codificado do seguinte modo sem criar um terceiro array: (não faça isto agora pois iremos utilizar estes arrays no próximo exercício.) ijk.concat(bcd); ijk; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas"] E deste modo, como não necessitaríamos mais do segundo array, bcd, poderíamos declara-lo como nulo para limpar a memória do browser: bcd = null; Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.2 join( ) Join significa juntar. Aqui juntar não significa concatenar como no método anterior. Join resulta numa junção de todos os elementos de um array, convertidos para um só string. O método de Array join( ) é o oposto do método de String split( ). Se ainda tem no console o array abc da nossa última experiência, salte este primeiro passo da nossa próxima experiência: 1- Declare um array (veja ficheiro em raw. ) icontemp.com/p3/222j.txt : var abc =["vermelho","branco","azul","maçãs","bananas","uvas"];
2- Declare um segundo array abc2, atribuindo-lhe o resultado de join() com o array abc:
var abc2 = abc.join(); 3- Chame abc2:
abc2; // devolve "vermelho,branco,azul,maçãs,bananas,uvas" Repare como todos os elementos foram separados por uma vírgula. Esta é a opção inicial mas pode mudar tal opção especificando o símbolo que quer utilizar para fazer a separação, incluindo espaços em branco entre aspas. Veja os seguintes exemplos:
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 :
2.2.3 reverse( ) O método reverse( ) de um Array inverte a ordem dos elementos contidos no mesmo. Reverse é permanente, isto é, para voltar ao original terá que aplicar o reverse outra vez. 1- Reverse abc:
abc.reverse( ); // devolve ["uvas", "bananas", "maçãs", "azul", "branco", "vermelho"] (baseado no nosso array anterior). Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.4 sort( ) O método sort() significa ordenar. No seu padrão original, sort() ordena alfabeticamente. Esta ordem alfabética não funciona muito bem com números porque por exemplo, o número 20 virá primeiro que o número 3 pois ordena pelo primeiro algarismo da esquerda. A razão disto é que sort() converte a data para type string e não reconhece os números pelo seu valor numérico. Para ordenar numericamente necessitamos de ser mais específicos. Vamos ver como isso funciona. 1- Primeiro teste. (Traga o array do tópico anterior):
abc.sort( ); // devolve ["azul", "bananas", "branco", "maçãs", "uvas", "vermelho"]. 2- Até aqui tudo bem. Mas o que acontecerá a um array numérico:
var numA = [5,8,1,3,20]; numA.sort();// devolve [1, 20, 3, 5, 8] Não faz sentido, não é? Por causa deste problema de ordenação numérica, JavaScript permite introduzir um parâmetro que ajuda na seleção numérica dentro dos parênteses do sort(). Existem várias maneiras de escrever este parâmetro e irei demonstrar a que acho mais simples. O que se introduz no sort( ) é um exemplo de uma função tipo callback. Não se preocupe muito com o termo neste momento mas chama-se callback porque JavaScript esta chamando uma função previamente introduzida ( o que iremos fazer em seguida). (Ver a função na imagem mais abaixo). O método sort( ) processa a tal função callback com o seguinte critério (continue lendo que fará mais sentido no final): Se o resultado da subtração entre dois elementos que estão sendo avaliados pela função for negativo, o primeiro dos dois elementos a serem avaliados é fornecido ao sorte() porque é menor do que o outro. Se o resultado da subtração for zero, é porque ambos os elementos são iguais e cabe então ao sort() decidir qual deles deve ir primeiro. Se o resultado da subtração for positivo, o segundo elemento é entregue ao sort() e o primeiro passa a ser então subtraído com o próximo elemento a comparar. 3- Copie esta função para o seu editor:
Figure 19 ficheiro em raw . icontemp.com/p3/19.txt
Ao passar a função compareMe (nome indiferente) como parâmetro do método sort(), estamos instruindo JavaScript para filtrar cada elemento do array, pela função introduzida como argumento no sort(). JavaScript filtrará cada dois elementos do array, decidindo depois qual segue para o sort e qual ficará para trás a fim de ser comparado com o próximo elemento, isto é, no nosso exemplo, quando num1 é menor que num2, o resultado é negativo. o que faz com que num1 vá primeiro. Quando a subtração dá zero então os números são idênticos e nesse caso não há troca de números. Quando num1 é maior que num2, o resultado é positivo. Neste caso JavaScript mostra num2 primeiro e compara num1 com o próximo número do array. Este critério está estabelecido numa tábua da biblioteca interna de JavaScript. A função nada tem a ver com a decisão. A função apenas auxilia JavaScript na sua decisão de execução. O mecanismo de sort() faz o resto. 4- Agora podemos ordenar os números corretamente:
numA.sort(compareMe); // devolve [1, 3, 5, 8, 20]. 5- E se quiséssemos comparar em ordem contrária?
numA.sort(compareMe).reverse(); // devolve [20, 8, 5, 3, 1]. Como sort( ) permite uma função de callback no seu parâmetro, podemos também tirar proveito para ordenar arrays alfabeticamente, mas baseado no tamanho das palavras, o seu length. Como os nomes de parâmetros não têm importância eu irei chama-los de a e b no exemplo que se segue. A figura a baixo mostra um script que poderá utilizar de futuro ou até experimentar agora. Veja o (ficheiro em raw). Note como o script utiliza muito eficientemente uma função anônima como callback. Repare também que sendo algumas das palavras de comprimento idêntico, JavaScript ordena-as alfabeticamente:
Figure 20 ficheiro em raw. icontemp.com/p3/20.txt Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.5 pop( ) e push( ) O método de Array pop( ) retira o último elemento do array para fora, diminuindo desta forma o número de elementos no array em menos 1. Está última ação de diminuir o número do length em menos um, pode causar um erro quando o número de elementos chega a zero, se estiver utilizando um loop para sacar os elementos. Preste atenção a essa inconveniência. Pop pode ser traduzido como um estouro, como faz o milho de pipoca quando salta. Os elementos saltam para fora do array um a um. 1- Crie o array de frutos outra vez. (ou copie do ficheiro em raw ) icontemp.com/p3/222j.txt : var abc = ["vermelho","branco","azul","maçãs","bananas","uvas"];
2- Chame o array só para ter a certeza:
abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas"]. 2- Vamos remover o último elemento com pop():
abc.pop(); // devolve "uvas", isto é, retira ‘uvas’. 3- Chame o array outro vez:
abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas"]. O elemento “uvas” foi apagado. Vamos traze-lo para trás com push( ): O método push() faz o oposto de pop(), push() coloca um novo elemento, avançando a última posição do array. 4- Reinsira o elemento ‘uvas’ de volta:
abc.push("uvas"); // devolve o length de 6 que representa o número de elementos no array contando com o novo ‘uvas’. 5- Chame o array abc outra vez:
abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas"]. Como se vê o último elemento ‘uvas’ foi recolocado no sítio. De certo modo o array está atuando como um stack, restringindo a inserção e exclusão de elementos no estilo último-dentro – primeiro-fora. Muitas vezes é útil poder inserir e remover items sem ter que referenciar um local específico onde os colocar, ou de onde os remover. 6- Podemos inserir com push( ) elementos múltiplos (manualmente ou em loop): abc.push("pêssegos","cerejas", "peras"); 7- Agora abc tem 9 elementos:
abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas", "pêssegos", "cerejas", "peras"]. 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.
abc.pop( ); // devolve “cerejas”. abc.pop( ); // devolve “pêssegos”. Agora devemos já ter o nosso array original de volta: abc; // devolve ["vermelho", "branco", "azul", "maçãs", "bananas", "uvas"] Nota: Poderíamos ter utilizado splice() como por exemplo abc.splice(-3,3); (splice será discutido um pouco mais à frente: 2.2.8 splice ). Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.6 shift() e unshift() Os métodos shift( ) e unshift( ) são semelhantes a pop( ) e push( ), mas atuam no outro extremo do array, na parte esquerda ou de baixo (conforme queria imaginar o mecanismo), ou seja, aumentam ou diminuem elementos na posição zero. Eu imagino um brinquedo de Pez em que quando quero uma bala da parte de cima carrego no mesmo e salta (pop) a bala para fora. Ou se desejo uma bala da parte de baixo, pego-a com os dedos e puxo-a para fora (shift) ou então insiro uma bala no fundo fazendo o unshift, ou insiro uma bala no topo fazendo o push. Utilizando o nosso array original anterior, ficheiro raw icontemp.com/p3/222j.txt : var abc = ["vermelho","branco","azul","maçãs","bananas","uvas"]; 1- Vamos remover o primeiro elemento com shift:
abc.shift(); // devolve “vermelho” que é retirado. 2- Chame o array abc outra vez:
abc; // devolve ["branco", "azul", "maçãs", "bananas", "uvas"]. 3- Agora vamos colocar o primeiro elemento para trás. Aqui temos que especificar o nome do elemento:
abc.unshift("vermelho"); // devolve 6. Seis é o número de elementos depois da inserção. 4- Chame o array uma vez mais:
abc; // devolve ["vermelho","branco","azul","maçãs","bananas","uvas"]. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.7 slice( ) Tal e qual se faz com objetos de type String 2.1.9 slice, o método slice( ) extrai uma cópia específica dos elementos de um array, e devolve um novo array com os elementos extraídos do primeiro array. O array original não é afetado pelo extrato a não ser que este extrato seja aplicado ao próprio array em si (isto é, do original para o original). Este método aceita dois parâmetros. O primeiro parâmetro indica o primeiro item a ser executado (a contagem começa em zero). O segundo parâmetro indica o primeiro item a não ser executado. Vejamos o nosso array outra vez: (ficheiro em raw) icontemp.com/p3/222j.txt. 1- O nosso array abc é neste momento var abc = ["vermelho","branco","azul","maçãs","bananas","uvas"];
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 abc2 = abc.slice(0,4); // começando no zero (vermelho), 1(branco), 2(azul), 3(maçãs), 4(bananas, excluído). 3- Chame o array abc2:
abc2; // devolve ["vermelho", "branco", "azul", "maçãs"]. O método slice( ) é mais complexo do que acabamos de ver. Por exemplo, utilizando números negativos podemos ser mais específicos e copiar elementos a partir do fim do array (o length do array) em direção ao princípio. Vamos ver alguns exemplos para ilustrar como funciona: 4- O nosso array abc é ["vermelho","branco","azul","maçãs","bananas","uvas"]. 5- Declare outro array abc3, e atribuindo-lhe um slice de abc com o parâmetro de -2:
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.
abc3; // devolve-nos ["bananas", "uvas"]. 6- Declare outro array abc4, e atribuindo-lhe um slice de abc com 2 parâmetros negativos, (-4,2): var abc4 = abc.slice(-4,-2); Este exemplo pode ser à primeira vista um pouco confuso . O -4 indica o primeiro elemento a contar do fim que irá ser copiado. Neste caso o quarto elemento a partir do fim é ‘azul’. O -2 indica o primeiro elemento a contar do fim que irá ser excluído da cópia. Neste caso, tudo o que vier a partir de ‘bananas’ e até ao final, não será copiado.
7- Chame abc4:
abc4; // devolve ["azul", "maçãs"]. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.2.8 splice( ) O método de Array splice( ) é bem interessante. Você ainda se lembra de push e unshift? ambos adicionam elementos respectivamente no final e no princípio de um array. O método splice() adiciona (ou retira) elementos por entre os meios. Vamos ver como isso funciona. Dependendo quantos argumentos introduzimos como parâmetros dentro dos parênteses de splice(), poderemos apagar, adicionar ou trocar elementos. ·
Utilizando apenas dois parâmetros (modo remover):
Quando utilizamos apenas dois argumentos como parâmetros estamos dando as posições de índice de onde a ação irá começar (inclusivamente), e quantos elementos serão apagados. Como fará mais sentido abaixo, quando temos só dois parâmetros, splice() actua em modo de remover ou apagar, isto é, não há elementos a adicionar. Nota: Não confundir os parâmetros de splice em arrays com os parâmetros de slice em strings, os parâmetros têm sentidos diferente. Por exemplo, splice(0,3) significa que elementos 0, 1 e 2 serão apagados. O número 3 representa a quantidade de elementos a apagar. Esta versão pode ser utilizada em vez de pop e shift. ·
Utilizando mais que dois parâmetros (modo adicionar ou trocar):
Quando inserimos mais que dois argumentos como parâmetros em splice(), JavaScript assume que iremos adicionar (ou trocar) elementos no array e o critério funciona da seguinte forma: a) O segundo parâmetro indica quantos elementos irão ser apagados ou trocados. Podemos também colocar o segundo parâmetro como zero, o que significa que a operação será apenas de inserção sem apagar ou trocar elementos. b) O terceiro e outros parâmetros seguintes, indicarão os novos elementos a serem adicionados ou trocados. Por exemplo, splice(2,0,"amarelo") significa para inserir "amarelo" na posição 2 (como terceiro elemento) aumentando o array length por mais um. splice(2,1, "amarelo") significa para trocar o elemento que se encontra na posição 2 (terceiro elemento) por "amarelo". O length do array mantém-se na mesma porque o parametro 1 diz que o amarelo tem que substituir o elemento atualmente existente. A posição 2 refere-se ao terceiro elemento a contar de zero. O terceio elemento é apagado e o amarelo toma o seu lugar. Esta versão de slice() pode ser utilizada em vez de push e unshift. A imagem a baixo ilustra ambos os conceitos.
Figure 21
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:
x; // devolve ["azul", "vermelho", "verde", "violeta", "castanho"]. Começamos na posição zero e adicionamos todos os itens listados, removendo zero itens. O que aconteceria se agora repetíssemos o exemplo 4, mas com o segundo parâmetro a 3 em vez de zero? splice() apagaria os três itens, e depois colocaria os três itens listados que são exatamente os mesmos. Por outras palavras, nada aconteceria. Como vemos, o segundo dígito é que faz toda a diferença entre os vários modos de splice(), exceto no próximo caso onde o primeiro parâmetro muda o modo drasticamente. ·
splice() com números negativos:
Lembra-se da nota ao fundo to tópico pop( )?
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 :
2.3 Novos Métodos de Iteração com Arrays em JavaScript 2.3.1 forEach() Não confundir o novo método forEach() com aquele tipo de loop for each… in que já foi descontinuado em JavaScript moderno. forEach que se traduz em paraCadaUm: faça isto, é uma função que aceita uma outra função como input e utiliza os parâmetros deste input para selecionar os valores do array especificado. Este método forEach( ) foi introduzido com a versão ECMAScript 5 que adicionou nove métodos ao arsenal de funcionalidade para manipulação de arrays. Estes métodos fazem operações que previamente teríamos que criar scripts mais complexos como por exemplo loops iterativas. Os nove métodos são: indexOf, lastIndexOf, every, some, forEach, map, filter, reduce, reduceRight. forEach( ) faz uma ação especificada pelo programador para cada elemento do array. Essa ação específica depende das instruções que se seguem no script depois da declaração do macro forEach(). forEach( ) aceita uma função como argumento (uma daquelas a que chamamos callback). Os parâmetros da função inserida farão comunicação (interface) com cada elemento válido do array onde serão aplicados de uma maneira iterativa, baseada no script que se segue, tal e qual como faríamos com um for loop. Com um elemento válido quero dizer um endereço de elemento onde existe data. Todos os índices do array que não tenham um valor ou que sejam undefined (se houver algum), serão ignorados. 1- Vamos experimentar com este método em incrementos. Comecemos por introduzir no console um novo array (Estou usando Opera ou Chrome):
var g = ["vermelho", "azul", "branco"]; 2- Chame forEach( ) aplicado ao array g usando uma função anônima com apenas um parâmetro, "i" (ver a figura seguinte) (o i é um nome fictício):
Figure 22 ficheiro em raw. icontemp.com/p3/22.txt
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 :
2.3.2 map( ) O método map( ) itera por um array visitando cada elemento, tal e qual o método forEach( ) mas com a exceção de que devolve um valor global (return de função), enquanto que forEach faz apenas uma leitura e não devolve um total no fim. Como map() devolve um valor real, este pode ser utilizado para criar um array baseado no original que está iterando. Outra diferença é que forEach() chama a função para cada item onde itera, enquanto que map() chama a função uma só vez em todo o processo. O significado disto é que, enquanto forEach() se esquece do elemento anterior quando passa para o próximo, map() vai acumulando a data para a devolver no final como um novo array. Vamos ver um exemplo: 1- Declare um novo array (ver o ficheiro raw por debaixo da imagem): var primeiroArray = ["amazonas", "nilo", "kwanza"]; 2- Declare um segundo array chamdo segundoArray e atribua-lhe o resultado de map() que pega no conteúdo do primeiro array e aplica-o ao novo array. Só para ter a certeza de que são realmente independentes e não uma referência para a mesma data, modifique o segundoArray para que os elementos sejam em letra maiúscula (lembra-se como se faz?). Depois chame o segundoArray para ver o resultado:
Figure 24 ficheiro em raw. icontemp.com/p3/24.txt
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 :
2.3.3 every( ) O método every( ) que se traduz em “todos ou tudo”, é outro método de iteração. Chama-se “every” porque tudo o que for verificado deve ser true para todos os elementos do array ou o resultado devolvido (return) será false. Tudo ou nada. Este método só faz leitura e relata a característica do array que está sendo verificado. 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:
var abc = [1,2,3,4,5]; 2- Chame o array abc com o método every( ), utilizando uma função callback que verifica se os números são todos pares. Se todos os itens não forem números pares, every ( ) resultará em “false”:
Figure 25 ficheiro em raw. icontemp.com/p3/25.txt
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 :
2.3.4 some( ) No que diz respeito a tudo ou nada, o método some( ) é o oposto de every( ). Some traduz-se em “alguns” ou “uns tantos”. Este método devolverá true se algum dos elementos qualificar para o critério estabelecido no corpo do método. Tal como every( ), não modifica em nada o script, serve apenas de investigador e faz a reportagem da classificação. Fica a nosso critério manipular tal data em questão. Aqui vai um exemplo baseado na mesma data do últimos exercício: 1- Declare um array abc com números consecutivos:
var abc = [1,2,3,4,5]; 2- Chame o array abc com o método some( ), utilizando uma função callback que verifica se alguns dos números são pares. Se houverem números pares de volta a classificação de “true”:
Figure 26 ficheiro em raw. icontemp.com/p3/26.txt Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.3.5 filter( ) O método de array filter( ) funciona do mesmo modo que some( ) mas em vez de reportar true ou false, ele devolve um array com os elementos que qualifica como true ou seja, verdadeiros. De certo modo faz lembrar map() devido à criação de um novo array. Vamos utilizar o mesmo array abc como exemplo. 1- Declare o array abc se já não o tem no console:
var abc = [1,2,3,4,5]; O que queremos fazer aqui como exemplo, é extrair uma cópia de todos os elementos numéricos que sejam par e colocá-los em outro array. 2- Então declare um novo array abc2 e use filter( ) para investigar todos os elementos do array abc, copiando para abc2 todos aqueles que qualificarem como par:
Figure 27 ficheiro em raw. icontemp.com/p3/27.txt
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 :
2.4 Novos Métodos de Redução e Localização para Arrays em JavaScript 2.4.1 O que é Redução Em ciência de computação, redução é um algoritmo que transforma um problema em outro problema mais simples, isto é, a conversão de uma expressão ou equação para a sua forma mais simplificada. ECMASCript 5 introduz dois métodos de redução: reduce( ) e reduceRight( ). A diferença entre um e outro é a direção ou o sentido em que eles operam. O primeiro começa no princípio do array, o segundo começa do fim para o princípio. Ambos os métodos de redução aceitam uma função anônima como valor de base opcional:
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:
var meuArray = [2,4,6,8]; 2- Declare um variável de nome aleatório reduzido e atribua-lhe o resultado da aplicação de reduce() ao meuArray, onde cada dois valores serão multiplicados um pelo outro.
Figure 30 ficheiro em raw. icontemp.com/p3/30.txt
3- Chame reduzido; // devolve 384. Na primeira iteração o valorPrevio é 2 (primeiro elemento) e o valorCorrente é 4 (segundo elemento): 2 x 4 = 8, depois 8 x 6 = 48, depois 48 x 8 = 384. Por outro lado, se tivéssemos incluído o valorInicial, que iria aqui: }, valorInicial); o valorPrevio teria começado no valor dado em valorInicial, e o valorCorrente teria começado no primeiro elemento do meuArray em vez de no segundo elemento. O resultado total teria sido diferente porque o valorInicial teria influenciado a operação. Veja o próximo exemplo. 4- Por exemplo, declaremos um outro variável reduzido2 para guardar o resultado de um método reduce() aplicado ao meuArray. Desta vez iremos incluir um valorInicial de 10:
Figure 31 ficheiro em raw. icontemp.com/p3/31.txt 5-
Chame reduzido2; // devolve 3840. Uma diferença enorme do script anterior.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.4.3 reduceRight( ) O método de array reduceRight( ) funciona da mesma maneira que o método anterior, reduce( ). A única diferença é que reduceRight() começa a processar a partir do último elemento do array, enquanto que reduce() começa a processar a partir do primeiro elemento do array. Um funciona da direita para a esquerda, o outro da esquerda para a direita. Tudo o resto que foi descrito para reduce( ) no tópico anterior aplica-se também a reduceRight( ). Se aplicarmos os scripts anteriores (multiplicação) a reduceRight( ), o resultado será idêntico, 384 e 3840. Se quiser experimentar, troque “reduce” por “reduceRight” na linha 1 dos exemplos anteriores (ver figuras a cima). Para o propósito de ilustrar onde diferem estes métodos, vamos criar um novo script. Desta vez subtraímos os valores em vez de os multiplicar: 1- Use o meuArray anterior. Se está a recomeçar o seu console de JavaScript, declare o array indicado a baixo:
var meuArray = [2,4,6,8]; 2- Depois copie ou codifique o seguinte método:
Figure 32 ficheiro em raw. icontemp.com/p3/32.txt
3- Chame subReduz; // devolve -16. 4- Agora modifique o método para reduceRight:
Figure 33 ficheiro em raw. icontemp.com/p3/33.txt
5- Chame uma vez mais subReduz; // devolve -4. No exemplo acima, subReduz devolveu -4 quando utilizando o método reduceRight, e -16 quando usando o método reduce. Como se pode ver, o resultado de subtrair todos os elementos é consideravelmente diferente entre um e outro método. Quando desejar aplicar um destes métodos em um projeto seu, estas diferenças serão importantes na sua decisão de qual método utilizar.
Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
2.4.4 Métodos de Localização Para além dos métodos de iteração e redução, JavaScript tem dois outros métodos que podem ser utilizados para localizar elementos em um array: indexOf( ) e lastIndexOf( ). IndexOf começa da esquerda para a direita, e lastIndexOf começa da direita para a esquerda. Ambos funcionam do mesmo modo e ambos devolvem a mesma posição de referência numérica, mas a posição escolhida por cada um destes métodos pode variar dependendo das circunstâncias, isto é, se houver duplicação de elementos o lastIndexOf devolverá o último elemento duplicado, enquanto que o indexOf devolverá o primeiro elemento (por primeiro e último refiro-me à direção de esquerda para direita ou seja, de princípio para o fim).
2.4.5 indexOf( ) versus lastIndexOf( ) Se tudo o que queremos é o índice de um elemento único, indexOf e lastIndexOf dão o mesmo resultado. No entanto, em arrays muito compridos um poderá ser mais eficiente do que o outro, dependendo onde se encontra o elemento que procuramos: se na primeira metade ou se na segunda. No caso que haja mais do que um elemento idêntico, escolher um método em vez do outro devolverá resultados diferentes. 1- Vamos declarar um novo array no console. (ficheiro em raw) icontemp.com/p3/245.txt var x1 = ["amendoim", "amendoas", "nozes", "castanhas"]; 2- Chame o index de “nozes” usando indexOf( ) :
x1.indexOf("nozes"); // devolve 2 porque conta 0,1,2. 3- Chame o index de “nozes” outra vez, mas utilizando lastIndexOf( ):
x1.lastIndexOf("nozes"); // devolve 2. Embora faça a pesquisa a partir da direita, o resultado numérico é o mesmo que o exemplo anterior porque se baseia na posição oficial de esquerda para direita. Por outras palavras, faz uma decrementação em ordem inversa como por exemplo 3,2,1,0. 4- Vamos tentar com “amendoim” em ambos os casos:
x1.indexOf("amendoim"); // devolve posição 0. x1.lastIndexOf("amendoim"); // devolve posição 0. Do ponto de vista dos quatro testes que fizemos a cima, não existe diferença no resultado de um ou outro método. No entanto, se existirem dois elementos com valores idênticos, o resultado será diferente. Considere o próximo exercício: 5- Adicione um segundo elemento “nozes” no fim do array utilizando o método push( ):
x1.push("nozes"); // devolve o índice de 5 elementos. Se chamar o array x1 será: ["amendoim", "amendoas", "nozes", "castanhas", "nozes"]. 6- Agora chame o index de “nozes” com indexOf( ) :
x1.indexOf("nozes"); // devolve posição 2. 7- Chame o index de “nozes” usando lastIndexOf( ):
x1.lastIndexOf("nozes"); // devolve posição 4. Encontra o último elemento “nozes” primeiro e é este que devolve.
Nota: Se o elemento que procuramos não for encontrado, estes métodos devolvem um -1. Isto é importante porque pode vir a ser muito útil como veremos em outras ocasiões. Como são os elementos comparados? Os métodos de índex comparam os elementos introduzidos na pesquisa com os elementos no array, usando um sistema que se chama igualdade estrita, que é o mesmo método utilizado pelo operador === (o operador triplo de igualdade). Isto é, se os elementos diferirem em sintaxe, ou em type, não serão reconhecidos como idênticos e o algoritmo devolverá um -1 que significa “não encontrado”.
Segundo parâmetro (opcional): Onde iniciar pesquisa. Existe um segundo parâmetro que aceita um argumento opcional nestes dois métodos indexOf( ) e lastIndexOf( ). Este parâmetro é conhecido como fromIndex ou seja aPartirDeIndex. Este parâmetro especifica a localização onde iniciar a pesquisa (inclusivamente). Isto significa que em logos arrays, é possível saltar uma certa quantidade de elementos para pesquisar mais além, poupando algum tempo de processamento. fromIndex começa a contar em zero, o que significa que o primeiro elemento conta como 0 e depois 1,2,3, etc. Este é o exemplo que temos neste momento no console: ["amendoim", "amendoas", "nozes", "castanhas", "nozes"];
8- Vamos experimentar com o parâmetro fromIndex. Use um argumento de 2:
x1.indexOf("nozes",2); // devolve posição 2 (encontrou o primeiro elemento “nozes” que por coincidência estava na posição onde a busca se iniciou. Se estivesse um pouco mais a trás só teríamos encontrado o último ‘nozes’ como iremos ver em seguida.
x1.indexOf("nozes",3); // devolve posição 4 (nunca chegou a ver o primeiro ‘nozes’ porque a pesquisa começou depois, na posição 3 (castanhas), e então encontrou o segundo elemento contendo o mesmo nome. Agora vamos ver como atua o método lastIndexOf em circunstâncias idênticas:
x1.lastIndexOf("nozes",2); // devolve 2 (Não se apercebeu do elemento nozes mais chegado ao fim porque só começou a pesquisa na segunda posição do array e em sentido decremental (2,1,0). Lembre-se que este método busca da direita para a esquerda. 9- Um outro exemplo:
x1.lastIndexOf("nozes",4); // devolve 4. Como 4 coincide com o final do array, este método encontrou o elemento ‘nozes’ mais chegado ao fim que coincidentemente era o último elemento (ou o primeiro a ser investigado da direita para a esquerda).
Critério de pesquisa - sumário. O sumário incluído em baixo contem algumas especificações que ainda não tínhamos coberto. Este sumário serve de referência para uso futuro. Não tente memorizar estas regras para não ficar confundido. Quando necessitar de tomar uma decisão volte a esta página para escolher o método mais apropriado. Se for algo que fará constantemente então com tempo ganhará experiencia sem ter que voltar a consultar esta página. O mais importante é compreender as diferenças entre uns métodos e outros assim como saber que eles existem. ·
Critério de pesquisa para o método indexOf( ) 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 posição zero para iniciar a sua pesquisa. Se por outro lado o fromIndex for maior ou igual ao comprimento do array, a pesquisa não será implementada porque já ultrapassou o array e o resultado de -1 será devolvido pelo método. b) Um fromIndex positivo indica onde iniciar a pesquisa (inclusivamente) e a busca do indexOf() é feita da esquerda para a direita. Exemplo: um fromIndex de 1 começa a busca no segundo elemento (inclusivamente) porque este índice é base zero, o que faz com que o primeiro elemento seja na posição zero. c) Por outro lado, se o fromIndex for um número negativo, será utilizado para fazer o offset do primeiro elemento a ser pesquisado. Este offset baseia-se no comprimento do array + o número negativo isto é, num array de 5 elementos, a busca começa na posição 4 e para a frente, o que inclui apenas 4 e 5 no nosso exemplo. (offset significa "fora do lugar", deslocado). Exemplo: 1- O nosso array x1 é ["amendoim", "amendoas", "nozes", "castanhas", "nozes"] 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’: x1.lastIndexOf("nozes",-1); // devolve posição 4. Do mesmo modo, o array com 5 elementos e um fromIndex de -2 começará na posição no.3, ou seja o quarto elemento (inclusivo). 3- No exemplo abaixo baseado neste array ["amendoim", "amendoas", "nozes", "castanhas", "nozes"], a busca começará em ‘castanhas’, devolvendo o ‘nozes’ da posição no. 2 (coincidência de números). x1.lastIndexOf("nozes",-2); //devolve posição no.2. Começou a pesquisa depois de ter passado o primeiro ‘nozes’ a contar do fim e da direita para a esquerda na posição de ‘castanhas’, e devolveu ‘nozes’ da posição no.2 (terceiro elemento a partir do principio). c- Num método lastIndexOf( ), um fromIndex positivo determina a posição onde se iniciar a pesquisa (inclusivamente) que depois move da direita para a esquerda. Exemplo: um fromIndex de 0 só pesquisará o primeiro elemento da esquerda porque é posição no. zero e a pesquisa move-se da direita para a esquerda. No nosso exemplo seria amendoim. Um fromIndex de 1 pesquisará o segundo e o primeiro elemento, por essa ordem. Etc. Embaixo pode encontrar a referencia oficial da ECMASCRIPT 5 sobre este método :
Parabéns por ter investigado todos este métodos. Este conhecimento ser-lhe-á muito útil. Não se esqueça de repetir a leitura de vez enquanto e também utilizar esta informação como referencia. Se houver alguma modificação eu chamarei atenção nos ficheiros em raw. Em seguida exploraremos um pouco mais sobre funções. Vamos então?
2.5 Funções de JavaScript e Argumentos como Objetos Num outro capítulo mencionamos que em JavaScript todas as funções são atualmente objetos. Vamos explorar esse conceito um pouco mais além.
2.5.1 O objeto arguments Em ECMAScript 5 os argumentos deixaram de ser propriedades da função para se tornarem num objeto chamado arguments. Este objeto foi estabelecido à semelhança de arrays, embora o número de propriedades herdadas do Array tenha sido limitado a apenas um: length (comprimento). Length é uma propriedade importante porque facilita a iteração de cada argumento através de loops (ciclos). Um objeto arguments atua como um variável local da função. Este objeto arguments só funciona mesmo com funções, não tem vida própria fora deste ambiente. Ele é posto em ação pela função em si ou alguma subfunção existente se houver um binding (colagem) apropriado para isso (veremos o tópico de binding mais à frente). Nós não podemos criar um objeto arguments diretamente porque este só passa a existir na altura de execução da função. Este objeto arguments cria um quase array com capacidade de 255 elementos. Se se lembra da nossa introdução a funções, podemos introduzir argumentos através dos parâmetros codificados dentro dos parênteses da função, que atuam como interface entre o mundo exterior e o interior da função. No entanto, nós não temos que realmente declarar estes parâmetros (como se faz entre parênteses) para poder passar informação (argumentos) à função. Tudo depende do que for codificado dentro da função em si, isto é, depende do que o script pede para inserir quando chamamos a função. Considere o seguinte exemplo: 1- Declare a função x sem nenhum parâmetro mas que devolva o objeto arguments:
Figure 34 ficheiro em raw. icontemp.com/p3/34.txt
2- Chame a função x incluindo os seguintes valores como argumentos:
x("zebra","girafa"); // devolve-nos todos os argumentos que introduzimos: ["zebra", "girafa"] 3- Chame a função com outros valores diferentes para solidificar o conceito:
x(1,2,3,4,5,6,7,8,9); // devolve [1, 2, 3, 4, 5, 6, 7, 8, 9] Embora não tenhamos programado nenhum parâmetro formal para fazer o interface entre o mundo externo e a função, esta função aceita toda a nossa data, e cria um objeto chamado arguments
com toda esta data. Isto significa que os parênteses da função são uma porta aberta a data in mesmo que não exista uma ordem de entrada estipulada. Embora o objeto arguments não seja bem um array completo, depois de ele ter sido criado através dos valores que introduzimos ao chamar a função, poderemos copiar a data contida no arguments para um novo array, utilizando por exemplo o método slice( ). Uma vez que tenhamos um verdadeiro array com a data do objeto arguments, será então possível utilizar ferramentas de Array para processar estes elementos de acordo com as nossas necessidades. (Se desejar, reveja este método no capítulo 2.2.7 slice( ) ). 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: