SlideShare uma empresa Scribd logo
1 de 39
Baixar para ler offline
Tutorial JavaScript Orientado à
Objeto e jQuery
Bem-vindo ao tutorial de JavaScript orientado à objeto, politicamente incorreto.
Índice
o Índice
o A pressa passa e a merda fica Introdução Histórica (e lição de vida)
o JavaScript un zero un ¡Hola Mundo!
o Como declarar uma variável Sério mesmo, como declarar uma variável!
 Number:
 String
 Array
 RegExp
 Function
 Object
 Object antigo
 Escopo das variáveis
 Misturando tudo com jQuery
o DOM - El Poderoso Jefón
 Solução 1 - Movendo a tag script
 Solução 2 - Postergando a execução na tag
 Se familiarizando com a Cosa Nostra
o Google - O Grande Irmão
o This doesn't makes sense - OOP
 Deixando as coisas mais previsíveis
 Closures e campos private
 Revisitando nosso abacaxi
o Desacoplando JavaScript das linguagens server-side
o Eventos
o Herança Maldita
o Ado, a ado, cada um no seu quadrado - Namespaces
 Conversão de tipos e falsy values
o Nota final
o Apêndice
A pressa passa e a merda
ficaIntrodução Histórica (e lição de vida)
JavaScript foi criado e programado em 2 semanas pela netscape para validar formulários
dentro dos navegadores, sem a necessidade de recarregar a página. Aparentemente na
época das conexões discadas medidas em BAUDS isso era muito importante (e com o
3G da tim tb ¬¬). Porém, este período curto para desenvolvimento da linguagem gerou
um produto com diversas questões controversas. Seu nome foi puramente uma jogada
de marketing, tentando se aproveitar da popularidade do Java, que na época era muito
usado para fazer applets. JavaScript teve várias versões após isso, sendo que a versão
atual, padronizada como ES5, é bastante diferente da inicial.
Ela é a única linguagem de programação capaz de interagir com todos os navegadores
de internet. Também pode ser utilizada para programação server side, através
do Node.js, mas não vou abordar isso pois não tenho conhecimento suficiente.
JavaScript já foi chamada de linguagem mais mal entendida do mundo. Grandes
empresas como a Microsoft e o Google passaram bastante tempo desenvolvendo
ferramentas para que não seja necessário escrever JavaScript. E o motivo de tudo isso...
é que JavaScript é como espanhol.
Hay um hombre tarado con el...
Parece com português, mas significa uma coisa completamente diferente.
Há um homem careca com o casaco na mão correndo atrás do ônibus.
PS: Essas palavras se chamam falsos cognatos.
A sintaxe do JavaScript parece com C e derivados, mas significa coisas diferentes. Por
exemplo, this faz coisas bem diferentes dos seus equivalentes em C#. É um falso
cognato
Vou enfatizar justamente as diferenças ao longo desse tutorial. Mas antes cabe
rapidamente rever um outro fato histórico, importante no desenvolvimento dessa
linguagem.
A Netscape inicialmente não tinha intenção que a linguagem fosse padronizada ou
aberta. Ela queria dominar o mercado, forçando as pessoas a fazerem páginas que só
funcionavam no netscape, e abriam zoadas no seu concorrente, o internet explorer. Esse
episódio é conhecido como Browser Wars. O resultado disso foi uma grande divisão na
linguagem, gerando incompatibilidades que temos que lidar até a versão 8 do Internet
Explorer. Este foi o principal motivo do sucesso de bibliotecas com jQuery, que
escondem estas incompatibilidades através de sua API.
Como os smartphones já nasceram com navegadores posteriores ao IE8, sites e
aplicativos para estes dispositivos não precisam se preocupar com isso.
JavaScript un zero un¡Hola
Mundo!
Sei que vc é um programador fodástico e que não precisa que ninguém te ensine nada de
programação. Então não vou mostrar nada além do estritamente necessário para explicar
as diferenças de JavaScript em comparação com C e derivados. Ok?
A linguagem em si é bem pequena, afinal, não dá pra pirar muito em 2 semanas. Suas
incompatibilidades no IE são poucas. Mas, existe uma API exposta pelos navegadores,
comumente utilizada em conjunto com JavaScript, chamada Document Object Model
(DOM), que é bem maior e mais incompatível. Ele serve para que o JavaScript acesse
os elementos de um arquivo html, xml ou svg. Mas vamos ver uma coisa de cada vez.
JavaScript pode existir dentro de um arquivo .js, .htm ou .svg
Arquivos .js contém apenas programação, ao passo que arquivos .htm e .svg misturam
linguagens de marcação com JavaScript. Podemos usar o bloco de notas para criar e
editar um arquivo .js.
O firefox tem uma ferramentinha bem útil pra testarmos pequenos blocos de JavaScript,
sem precisar usar um arquivo .js externo. Ela se chama scratchpad.
Para o próximo exemplo, vamos usar um arquivo .htm bem simples para fazer nossos
testes. Copie o trecho abaixo no bloco de notas de sua preferência, e salve como um
arquivo .htm
index.htm - olá mundo
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Html mínimo aceitável</title>
<meta charset="utf-8" />
<script src="tutorial.js"></script>
</head>
<body>
</body>
</html>
PS: Html também tem versões, que fazem com que a página pareça e se comporte de
maneira diferente de versão para versão. A primeira linha do arquivo:
<!DOCTYPE html>
indica que estamos usando a versão 5 do html.
Depois cole o texto abaixo num arquivo novo, e salve com a extensão .js. Salve na
mesma pasta que o arquivo .htm Note que a página deste exemplo usa utf-8. Se vira!
tutorial.js - olá mundo
"use strict";
console.log('Utilizadores de alert terão seus dedos cortados fora.');
Abra o arquivo .htm no seu navegador. Se vc fez tudo certo, observe atentamente que
não vai acontecer absolutamente porra nenhuma. Porém, se for IE 10-, vai gerar um
erro! Começamos bem hein! Disse que não ia mostrar nada além do estritamente
necessário para explicar as diferenças de JavaScript. E o propósito desse exemplo é
explicar algumas coisas sobre o ambiente de execução da linguagem e das ferramentas
de desenvolvimento dos navegadores.
Pra ver o resultado de nossa programação precisamos abrir as ferramentas de
desenvolvimento, que todos os navegadores possuem (o Firefox possui duas, uma nativa
e uma em forma de extensão). Para abrir faça:
 Chrome, Firefox (nativo), Opera e Safari: Ctrl + Shift + i
 Firefox (extensão firebug) e IE: F12
Procure por uma aba chamada console. É para o console que as mensagens de debug
vão. Recarregue a página e você deverá ver nossa mensagem instrutiva, inclusive no IE.
As ferramentas de desenvolvimento também possibilitam inserir breakpoints no meio
do código e avançar linha por linha, como outras IDEs.
Vamos agora explicar nosso exemplo inicial linha por linha.
"use strict";
A versão ES5 do JavaScript possui um modo, chamado modo estrito. Ao incluir "use
strict"; como a primeira linha do arquivo, você está habilitando este modo. Ele
garante compatibilidade futura com novas versões da linguagem, além de reduzir as
chances de introduzir erros na programação. Este modo pode ser habilitado para o
arquivo inteiro, ou apenas para determinadas funções. Veremos esta segunda forma
mais adiante.
console.log('Utilizadores de alert terão seus dedos cortados fora.');
console é um objeto, mas não pertence à linguagem. Ele faz parte da API do navegador,
o DOM. Na prática isso significa que nem sempre ele está disponível para nós
utilizarmos. Cada navegador expõe métodos diferentes deste objeto, e os interpreta de
maneiras diversas. O método log existe em todos eles, e possui inúmeras vantagens
sobre métodos anteriores de debug.
Note que não existe método main. Uma página pode conter diversos blocos separados de
JavaScript, portanto não existe o conceito do ponto único de entrada do seu código.
Assim que o navegador identifica um trecho de código JavaScript, ele o interpreta e
executa linha por linha. Se o mesmo arquivo for inserido duas vezes, ele será executado
duas vezes. Não existem include guards em JavaScript, como em C ou
PHP.(#ifndef #define #endif, include_once...).
Em .net, existe algo similar em funcionalidade aos include guards. O
método RegisterStartupScript, recebe uma id ao registrar um script. Se for registrado
um novo script com a mesma id, o script anterior é descartado.
Como declarar uma
variávelSério mesmo, como declarar
uma variável!
Declaramos uma variável com a palavra-chave var, seguida do nome da variável;
Opcionalmente atribuímos um valor para ela. Se não atribuirmos, ela terá o valor
padrão undefined.
código js - declaração de variáveis
"use strict";
var naoInicializada; // possui valor undefined
var dica = 'JavaScript não é "fortemente tipado"';
Não declaramos o tipo da variável, pois JavaScript não proíbe que o tipo dela mude
(não é fortemente tipada). O tipo é inferido através do valor atualmente atribuído à
variável. Note porém que se mantivermos o mesmo tipo da variável ao longo da
execução de nosso código, o compilador consegue aplicar otimizações de
performance. Se necessário, é possível verificar o tipo da variável usando o
operador typeof
A lista completa de tipos de variáveis pode ser vista no MDN, que é o portal de
desenvolvimento da Mozilla. Vamos ver apenas os tipos que diferem de alguma
maneira de seus primos em outras linguagens.
Number:
Sempre é um double.
código js - exemplos de números válidos
"use strict";
var i = 1; // é um double!
var area = 4.527; // também é um double
var negativo = -2; // sem notação especial para números negativos
String
É um objeto imutável. Pode usar aspas simples ou duplas. Para quebrá-la em mais de
uma linha, escapamos a quebra de linha com .
código js - exemplos de strings válidas
"use strict";
var simples = 'A primeira faz "tchan"';
var dupla = "A segunda faz 'tchun'";
var iara = "mãe d'água";
var html = '<em class="especial">ênfase</em>';
var josePauloPaes = 'Meu amor é simples, Dora,
Como a água e o pão.
Como o céu refletido
Nas pupilas de um cão.';
Array
Mistura de Array e ArrayList<Object>, isto é, muda de tamanho conforme a
necessidade e pode armazenar qualquer tipo de objeto. Podemos inicializar com um
tamanho inicial por motivos de performance. Acessamos os elementos dela com o
operador [].
Assim como ganhamos performance ao manter sempre o mesmo tipo para uma
variável, ganhamos performance ao armazenar sempre o mesmo tipo de variáveis
numa Array.
código js - uso de arrays
"use strict";
var noticias = []; // array vazia
var noticias2 = new Array(10); // array vazia com espaço pré-alocado para
10 elementos.
console.log(noticias2[4]); // retorna undefined, pois não atribuímos um valor
para este índice
console.log(noticias2[20]); // retorna undefined, pois não atribuímos um
valor para este índice
noticias.push('notícia extremamente curta :P'); // insere um elemento ao
final da Array
noticias[5] = 'e outra reduzida também'; // insere um elemento no índice 5
noticias.push('ao vivo'); // insere um elemento no índice 6, que é o final da
Array
RegExp
Além do objeto RegExp, JavaScript possui expressões regulares em forma de literal.
código js - formatos de expressões regulares
"use strict;"
var regex = /a+/gi; // expressão na forma literal
var regex2 = new RegExp('a+', 'gi'); // expressão na forma de objeto
Function
Em JavaScript, métodos e funções são objetos! Isso significa que elas possuem
propriedades e métodos como o call por exemplo. O número de parâmetros não
precisa ser declarado. Esta funcionalidade é utilizada para simular sobrecarga de
função, juntamente com o objeto arguments que toda a função possui.
Note que embora a chamada de funções desta maneira seja permitida, isso não impede
que o compilador lance exceções ao tentar utilizar variáveis que não foram devidamente
passadas ao método.
código js - chamadas válidas de métodos
"use strict";
function comum() {
console.log(arguments); // objeto arguments iniciado automaticamente,
contendo os parâmetros utilizados na chamada
}
function normal(texto) {
console.log(arguments); // objeto arguments iniciado automaticamente,
contendo os parâmetros utilizados na chamada
}
//chamadas perfeitamente válidas
comum();
comum('lala');
normal('lolo');
//chamada válida, e o parâmetro texto terá o valor undefined
normal();
Um exemplo de sobrecarga de função.
código js - sobrecarga de função
"use strict";
function efetuarRequisicaoAjax(endereco) {
var url = endereco;
if (arguments[1]) {
if (typeof arguments[1] == 'number') { // segundo parâmetro
passado para a função
url += '?pagina=' + arguments[1];
} else if (typeof arguments[1] == 'string') {
url += '?token=' + arguments[1];
}
}
if (arguments[2]) {
url += '&pagina=' + arguments[2]; // terceiro parâmetro passado
para a função
}
console.log(url); // vamos ver um exemplo real de ajax lá no final ;)
}
efetuarRequisicaoAjax('webservicedagrecia.ashx');
efetuarRequisicaoAjax('webservicedagrecia.ashx', 2);
efetuarRequisicaoAjax('webserviceseguro.ashx', '987dcb987d6b96a9d5ab');
efetuarRequisicaoAjax('webserviceseguro.ashx', '987dcb987d6b96a9d5ab', 2);
Como mencionei lá no começo, funções podem ser selecionadas individualmente para
utilizar o modo estrito. Isso é útil nos casos em que precisamos dar manutenção em
códigos antigos, ou quando inserimos código dentro de tags <script>, ao invés de em
arquivos externos.
código js - funções estritas
function funcaoNormal() {
console.log(varNaoDeclarada); // mostra undefined em navegadores
antigos, nos novos lança exceção
}
function funcaoEstrita() {
"use strict";
console.log(varNaoDeclarada); // lança exceção
}
JavaScript suporta funções anônimas, ou expressões lambda. Expressões lambda são
utilizados frequentemente em JavaScript, por isso é importante compreender o que são.
Expressões lambda são declarações de funções diretamente no meio do código.
Basicamente em qualquer lugar onde podemos passar uma referência de uma função,
podemos criar uma expressão lambda. Seguem exemplos em outras linguagens de
expressões lambdas (espero que tenha acertado as sintaxes).
código C
// pegadinha do malandro! C não tem isso.
código Objective-C
// lambda em Objective-C (OS X v10.6+ e iOS 4+) - chamado de Block
[superior usando:^() {
// corpo da função
}];
código C#
// lambda em C# 4
superior(() => {
// corpo da função
});
código Java
// lambda em Java 8
superior(() -> {
// corpo da função
});
código C++
// lambda em C++11
superior([] () {
// corpo da função
});
código PHP
// lambda em PHP - idêntico a JavaScript
superior(function() {
// corpo da função
});
código js - funções de ordem superior
"use strict";
// função normal
function normal() {
//
}
// função de ordem superior
function superior(funcao) {
funcao(); // executa a função passada
}
// função normal sendo passado como parâmetro - note que usamos apenas o
nome, sem executar com ()
superior(normal);
// expressão lambda sendo passada como parâmetro
superior(function() {
//
});
PS: existem planos de implementar futuramente em JavaScript uma arrow sintax () =>
{} para expressões lambda, como outras linguagens já usam.
Além de expressões lambda, JavaScript suporta um modo de declaração
chamado expressão de função. Mais adiante veremos outro uso para este tipo de
declaração.
código js - expressões lambda
"use strict";
// declaração de função
function normal() {
//
}
// expressão de função - tratamos a função como um valor
var surpresa = function() {
//
}
// expressão de função
var outraSurpresa = normal;
normal();
surpresa();
outraSurpresa();
Object
Já estivemos utilizando o objeto console, que o navegador nos fornece. Ou seja,
estivemos escrevendo JavaScript orientado à objeto desde o começo! Mais um motivo
pra não usar alert.
código
console.log('console é um objeto. log é um método');
JavaScript é bastante diferente de outras linguagens quanto à criação de objetos novos.
Vamos nos ater ao básico por enquanto. Declaramos um objeto usando {}. Acessamos
as propriedades e métodos usando ponto, como fazemos com console. Por
conveniência, também existe a possibilidade de acessá-las usando a notação de Array.
Esta notação é útil quando queremos passar o nome do campo dinamicamente.
Da maneira abaixo, todas as propriedades e métodos são públicos.
código js - declaração de objetos com propriedades e métodos públicos
"use strict";
var objetoVazio = {};
var retangulo = {
// inicializa o objeto - é quase como um construtor, mas esta sintaxe
não necessita de construtores
init: function(largura, altura) {
this.largura = largura;
this.altura = altura;
}
// define os métodos do objeto
area: function() {
return this.largura * this.altura;
},
// define as propriedades e valores padrão
largura: 0,
altura: 0,
};
retangulo.init(10, 20);
console.log(retangulo.largura); // mostra 10
console.log(retangulo.area()); // mostra 200
console.log(retangulo['largura']); // mostra 10
A versão ES5 do JavaScript incrementa a sintaxe para criar objetos, permitindo o uso de
propriedades somente leitura, getters e setters.
código js - declaração de objetos ES5 com getters e setters
"use strict";
// somente para navegadores novos
// em realce os descritores de propriedade
var retangulo = Object.create(Object.prototype, {
// inicializa o objeto - é quase como um construtor, mas esta sintaxe
não necessita de construtores
init: {
value: function(largura, altura) {
this.largura = largura;
this.altura = altura;
},
writable: false
},
// define os métodos do objeto
area: {
value: function() {
return this.largura * this.altura;
},
writable: false
},
// define os getters e setters
largura: {
get: function() {
return this._largura;
},
set: function(largura) {
this._largura = largura;
}
},
altura: {
get: function() {
return this._altura;
},
set: function(altura) {
this._altura = altura;
}
},
// define as propriedades e valores padrão - mais adiante veremos como
deixá-las privadas
_largura: {
value: 0,
writable: true
},
_altura: {
value: 0,
writable: true
},
});
retangulo.init(10, 20);
console.log(retangulo.largura); // mostra 10
console.log(retangulo.area()); // mostra 200
console.log(retangulo['largura']); // mostra 10
Object antigo
Existe outra sintaxe para criar objetos, utilizando funções e a palavra-chave new. Foi
uma tentativa de não causar estranheza aos programadores vindos de outras
linguagens, simulando o comportamento que eles já estavam acostumados nas
linguagens baseadas em classes. Em contrapartida, isso escondia o verdadeiro
funcionamento dos objetos em JavaScript. Douglas Crockford, que é um guru e um dos
redatores da linguagem não recomenda a utilização desta sintaxe antiga.
Inclusive, funcionalidades novas na criação de objetos não estão sendo
implementadas com esta sintaxe. Vou mostrar apenas a título de curiosidade uma das
variantes da sintaxe antiga.
código js - declaração de objetos com sintaxe antiga, não use!
"use strict";
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
var objetoVazio = new Object();
// define o objeto base
function Retangulo(largura, altura) {
// define as propriedades e valores padrão
this.largura = largura;
this.altura = altura;
}
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// define os métodos do objeto
Retangulo.prototype.area = function() {
return this.largura * this.altura;
};
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// cria um objeto que herda de Retangulo
var novoRetangulo = new Retangulo(10, 20);
// acessa os métodos e propriedades normalmente
console.log(novoRetangulo.largura); // mostra 10
console.log(novoRetangulo.area()); // mostra 200
console.log(novoRetangulo['largura']); // mostra 10
Outra desvantagem deste método é ter que se lembrar de declarar os métodos no
protótipo do objeto, sendo que as propriedades são declaradas diretamente dentro do
construtor. A sintaxe mais nova é mais uniforme quanto à isso.
Escopo das variáveis
Em JavaScript, o único delimitador de escopo é a função. Variáveis fora de funções
estão no espaço global, e variáveis declaradas em blocos script ou arquivos
diferentes compartilham o mesmo espaço global. Isso foi feito assim porque o
programador original da linguagem não tinha tempo de fazer um linker.
código js - escopo de variáveis
"use strict";
var todosVe = 'o/';
var i = 0;
for (i = 0; i < 5; i++) {
//
}
for (var j = 0; j < 7; j++) {
//
}
console.log(i);//4
console.log(j);//6. Surpresa. j foi declarado no espaço global.
Este é um dos motivos que declaramos todas as varíaveis no topo do arquivo ou no
começo da função.
Se quisermos evitar poluir o espaço global, temos que usar uma função anônima
(lambda) auto executável. Projetos de aplicativos windows 8 escritos em JavaScript já
utilizam por padrão esse procedimento.
código js - função anônima auto executável definindo escopo
(function(){
var segredo = '******';
})();
console.log(segredo); // undefined em navegadores mais antigos ou lança
exceção se modo estrito ou em navegadores novos
Pra memorizar isso eu geralmente escrevo assim:
Declaro a função.
function(){...}
Transformo ela em expressão.
(function(){...});
Executo ela.
(function(){...})();
Note que é possível passar parâmetros para a função anônima auto executável se
necessário. É bem comum fazer isso em conjunto com jQuery. Note que esse padrão
também permite que nós habilitemos o modo estrito num determinado escopo.
código js - função anônima auto executável com parâmetros
jQuery.noConflict(); // indicamos que não vamos usar jQuery com $
(function($){
"use strict"; // habilita modo estrito só para este escopo
// podemos usar $ aqui dentro sem interferir com o espaço global que
continua usando jQuery
$('body').html('<p>padrão para plug-ins jQuery</p>');
})(jQuery);
Outro motivo para se declarar as variáveis no começo do escopo é uma característica
do compilador JavaScript, chamada variable hoisting.
O que isso significa é que, independente de onde você declarar sua variável, o
compilador vai mover a declaração de forma transparente para o topo do escopo e
deixá-la com valor undefined. A linha onde um valor é atribuído à variável não é
afetada. Isso pode causar problemas se o código depender de uma verificação da
existência da variável.
código js - variable hoisting
"use strict";
// o que vc escreveu
console.log(estranha); // mostra undefined e continua normalmente
var estranha = 11;
console.log(estranha); // mostra 11
Ou seja, o que o compilador vai fazer de modo transparente é:
código js - variable hoisting explicado
"use strict";
// o que o compilador vê
var estranha; // o compilador moveu a declaracão para o topo do arquivo
console.log(estranha);
estranha = 11; // e manteve a atribução do valor na mesma linha
console.log(estranha);
Misturando tudo com jQuery
O framework jQuery mistura vários dos conceitos apresentados aqui. Ainda não
apresentei todos os conceitos para entendermos todas as partes do jQuery, mas podemos
identificar os já vistos no código abaixo.
código js - uso simples de jQuery
"use strict";
jQuery(document).ready(function(evento) {
// escopo isolado, não polui o espaço global
});
O primeiro dos conceitos utilizados é que jQuery é uma função. Por isso podemos
chamá-la utilizando jQuery(). Esta função sempre retorna uma referência para ela
mesma, ou seja, sempre retornajQuery. Esta organização de código se chama interface
fluida. O método recebe como parâmetro um seletor CSS ou objeto do DOM, neste caso
o objeto document.
jQuery(document).ready(function(evento) {
// escopo isolado, não polui o espaço global
});
A seguir temos o método ready.
jQuery(document).ready(function(evento) {
// escopo isolado, não polui o espaço global
});
Mas se jQuery() sempre retorna outra função jQuery, como acessamos o método ready?
Lembre-se em JavaScript funções são objetos, e portanto podem ter propriedades e
outros métodos.
Por último, passamos ao método ready uma função anônima que contém o código que
queremos executar. Esta sintaxe reduz a quantidade de variáveis e métodos no espaço
global.
jQuery(document).ready(function(evento) {
// escopo isolado, não polui o espaço global
});
DOM - El Poderoso Jefón
Se você leu o capítulo anterior (e deveria), pode ter visto que o IE10- lançou um erro em
nosso exemplo simples, quando as ferramentas de desenvolvimento não estavam
abertas. Isso ocorre porque nestes navegadores, o objeto console, pertencente ao DOM,
não é acessível pelo JavaScript enquanto as ferramentas não estiverem abertas. Vou
mostrar mais adiante como corrigir esta funcionalidadedo IE10-.
Embora por caminhos tortos, isso nos ensina um conceito importante. O conceito de que
JavaScript e o DOM são mundos diferentes, e nem sempre JavaScript consegue acessar
tudo do DOM a toda a hora. Existem momentos específicos em que isso pode ser feito.
Vamos ver isso e outros conceitos com um segundo exemplo mais complexo.
seosoquenao.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Google está olhando</title>
<meta charset="utf-8" />
<script src="seosoquenao.js"></script>
</head>
<body>
<h1 id="tagTitulo">Acessibilidade e SEO</h1>
<p id="tagParagrafo">
Sim pequeno gafanhoto, html tem mais de <strong>100
tags</strong>
diferentes além de table e div.
</p>
</body>
</html>
seosoquenao.js
"use strict";
//pega elementos da página pela id e os torna manipuláveis dentro do
JavaScript
var titulo = document.getElementById('tagTitulo');
var paragrafo = document.getElementById('tagParagrafo');
titulo.innerHTML = 'Black hat SEO';
paragrafo.attributes.add('style', 'display: none');
Ao abrir este arquivo diretamente do seu computador, com o console aberto, talvez você
note mais erros. Ao abrir este arquivo através de um servidor, mesmo que local, com
certeza você notará mais erros.
Porém a programação acima está perfeitamente correta, e o arquivo .htm também. O
problema que ocorre neste caso não existe em linguagens C e similares, e se dá quando
combinamos os arquivos .htm e .js de uma determinada maneira que provoca uma
interação inadequada entre o JavaScript e o DOM (Notou a elegância da frase? Ela é
ótima para impressionar seu chefe quando vc tiver que explicar pq aquele site está
funcionando perfeitamente no seu pc mas quando vc publicou pipocaram erros em todo
o lugar).
Lembre-se que JavaScript não possui o conceito de único ponto de entrada
(método main). Quando o navegador encontra uma tag <script> na página, ele pára de
processar o restante do documento, baixa o arquivo js, roda seu conteúdo, e só depois
continua o processamento normalmente. Em nosso exemplo, como a tag <script> está
antes do conteúdo, o navegador carrega e roda a programação antes de tomar ciência do
resto do documento. Nas primeiras linhas de nosso código, estamos pedindo para o
navegador pegar determinados elementos na página, mas ele ainda não sabe que esses
elementos existem. Isso talvez não ocorra sempre ao abrir o arquivo local devido
diferenças que existem ao ler o arquivo diretamente do hd, porém sempre irá acontecer
através de um servidor http.
E porque não tivemos esse problema no primeiro exemplo? Porque lá não estávamos
acessando nenhum elemento da página. Nunca saímos do mundinho JavaScript para o
mundo DOM.
Podemos corrigir nosso problema de três maneiras pelo menos. A primeira é mover
nossa tag <script> para o final da página, após o conteúdo. A segunda é pedir para o
navegador só executar nossa programação após o carregamento da página, na própria
tag. A terceira e fazer a mesma coisa, mas dentro do código JavaScript. Essa vou deixar
pra mais adiante, pois precisaríamos saber o que são eventos. Vamos ver os 2 primeiros
casos e explicar o que acontece.
Solução 1 - Movendo a tag script
seosoquenaoS1.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Google está olhando</title>
<meta charset="utf-8" />
</head>
<body>
<h1 id="tagTitulo">Acessibilidade e SEO</h1>
<p id="tagParagrafo">
Sim pequeno gafanhoto, html tem mais de 100 tags
diferentes além de table e div.
</p>
<script src="seosoquenao.js"></script>
<!--
Segura a empolgação, a especificação não permite colocar mais nada
depois de </body> e </html>. Mas isso nunca é necessário.
-->
</body>
</html>
Essa solução talvez seja a mais simples. Quando o navegador rodar o JavaScript, já vai
ter lido todos os elementos da página, e pode pegar qualquer um deles sem problema. A
desvantagem deste método é que o navegador não poderá otimizar o download dos
arquivos que a página irá usar (baixando em paralelo por exemplo), pois ele só vai saber
que precisará deles no final do documento.
Solução 2 - Postergando a execução na tag
seosoquenaoS2.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Google está olhando</title>
<meta charset="utf-8" />
<script src="seosoquenao.js" defer></script>
</head>
<body>
<h1 id="tagTitulo">Acessibilidade e SEO</h1>
<p id="tagParagrafo">
Sim pequeno gafanhoto, html tem mais de 100 tags
diferentes além de table e div.
</p>
</body>
</html>
Essa solução é bastante elegante, porém não é suportada nos IEs mais antigos. Além
disso, ela só funciona para arquivos .js externos. Não é possível utilizá-la quando
colocamos a programação entre as tags <script> e </script>. Através do
atributo defer da tag, indicamos nosso desejo de executar a programação dentro dela só
após o carregamento da página.
Se familiarizando com a Cosa Nostra
Utilizamos o método document.getElementById para trazer o elemento da página para
dentro do nosso código. Esta solucão não é a mais antiga para fazermos tal coisa, porém
é a recomendada pela performance e compatibilidade com os navegadores.
Mas e se precisamos pegar elementos que não possuem id? A API disponibiliza outros
métodos que retornam valores diferentes dependendo da sua necessidade, sendo que o
último deles,document.querySelectorAll foi inspirado após o surgimento do jQuery.
cosanostra.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>1001 maneiras de falar com o DOM</title>
<meta charset="utf-8" />
</head>
<body>
<header>
<nav>
<ul>
<li><a href="pag1">link 1</a></li>
<li><a href="pag1">link 1</a></li>
<li><a href="pag1">link 1</a></li>
<li><a href="pag1">link 1</a></li>
</ul>
</nav>
</header>
...
<footer>
<div>Ir para o <a href="#">topo</a></div>
</footer>
<script src="cosanostra.js"></script>
</body>
</html>
cosanostra.js
"use strict";
var links = document.getElementsByTagName('a'); // retorna os links do menu
e o ir para o topo
var linksMenu = document.querySelectorAll('ul a'); // retorna os links do
menu apenas. ñ funciona em IE8-
var linksMenuJ = jQuery('ul a'); // retorna os links do menu apenas. versões
do jQuery 1.10.x funcionam em todos os navegadores
Notem que cada chamada de método para trazer um elemento do DOM para dentro do
JavaScript leva um certo tempo para realizar esta operação. Por motivos
de performance, devemos sempre que possível guardar a referência para estes
elementos e reutilizá-la, ao invés de chamar continuamente métodos que busquem
no DOM. Vejam por exemplo este trecho da própria documentação do jQuery UI
(http://jqueryui.com/datepicker/#date-formats):
código js - interação pobre com o DOM
"use strict";
$(function() {
$("#datepicker").datepicker();
$("#format").change(function() {
$("#datepicker").datepicker("option", "dateFormat",
$(this).val());
});
});
Note que o mesmo elemento foi buscado duas vezes denecessariamente. Se quisermos
deixar o código mais performático, podemos fazer a seguinte alteração:
código js - interação correta com o DOM
"use strict";
$(function() {
var meuDatePicker = $("#datepicker"); // salvamos a referência ao
elemento na variável
meuDatePicker.datepicker();
$("#format").change(function() {
meuDatePicker.datepicker("option", "dateFormat",
$(this).val());
});
});
Google - O Grande Irmão
Acabamos de aprender como alterar html através de JavaScript. Igualmente importante
é sabermos quando não devemos fazer isso. Via de regra, Google e outros bots não
indexam conteúdo criado ou alterado por javascript. Porém, pode penalizar você se
ele achar que você está tentando trapacear, mostrando um conteúdo muito diferente do
que foi indexado. Para aplicativos web isso talvez não seja um problema, mas para
páginas de internet com conteúdo com certeza é. Aqui vale o bom senso. Podemos fazer
um ou outro dropdown, mas se abusarmos deste recursos, escondermos coisas sem um
método de mostrá-las novamente, podemos ser punidos pelo Google com posições
mais baixas nos resultados, e até mesmo banimento temporário.
O fato de bots não rodarem javascript em sua maioria também pode ser usado em
nossa vantagem. É possível por exemplo proteger endereços de emails ou evitar envio
automático de formulários alterando certas propriedades por JavaScript.
semcaptcha.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Google está olhando</title>
<meta charset="utf-8" />
</head>
<body>
<form action="#">
<dl>
<dt><label for="nome">nome</label></dt>
<dd><input id="nome" type="text" /></dd>
</dl>
<p><input type="submit" /></p>
</form>
<script src="semcaptcha.js"></script>
</body>
</html>
semcaptcha.js
"use strict";
var formulario = document.getElementsByTagName('form')[0];
formulario.action = 'cadastrar.php'; // não vai ser lido pela maioria dos
bots
This doesn't makes sense
- OOP
Já vimos acima como declarar objetos simples, utilizando literais de objeto {}. Estes
objetos são dinâmicos, não possuindo uma noção de classe como em outras linguagens.
De fato não existem classes em JavaScript. Esta característica, associada ao fato de
que JavaScript não é fortemente tipado, permite que objetos tenham propriedades e
métodos inseridos ou removidos dinamicamente, em tempo de execução.
Mesmo no modo estrito, podemos adicionar e remover propriedades. Note que isso
não é muito bom para performance. Assim como acontece com os tipos de variáveis, se
evitarmos mudar o objeto após sua criação, o compilador não consegue efetuar
otimizações para tipos específicos.
Adicionalmente, JavaScript possui o operador delete para remover propriedades, que
é lento. Preferencialmente atribuímos o valor null para a propriedade que desejamos
remover. A versão ES5 adicionou um método chamado Object.seal, que previne
modificação dinâmica do objeto.
código js - adicão e remoção dinâmica de propriedades
"use strict";
var escravoDeJo = {
jogar: function() {
//
}
};
escravoDeJo.jogar = null; // tira a propriedade - use o valor null ao invés
de delete
escravoDeJo.som = 'zig zig za'; // bota a propriedade
console.log(escravoDeJo); // note a mudança das propriedades
Object.seal(escravoDeJo); // "corpo fechado" - previne mudanças no objeto
// lança exceção
escravoDeJo.fazer = function() {
//
}
Para acomodar toda esta flexibilidade, JavaScript possui mais uma diferença, desta vez
em relação à palavra-chave this. Se podemos inserir funções dinamicamente nos
objetos, o que acontece quando uma destas funções utiliza this?
código js - significado de this
"use strict";
var modeloCumprimentar = function() {
console.log('Olá, menu nome é ' + this.nome);
}
var diretorSuspense = {
nome: 'Alfred';
};
var diretorAlternativo = {
nome: 'Tim';
};
diretorSuspense.cumprimentar = modeloCumprimentar;
diretorAlternativo.cumprimentar = modeloCumprimentar;
diretorSuspense.cumprimentar(); // mostra Olá, menu nome é Alfred
diretorAlternativo.cumprimentar(); // mostra Olá, menu nome é Tim
Resumindo, this em JavaScript muda de significado de acordo com o contexto. Mais
precisamente, this aponta sempre para o objeto que está executando a função
atualmente. Isso traz mais problemas do que vantagens geralmente. Vejamos um outro
exemplo disso:
código js - mudanças indesejadas do contexto de execução, this
"use strict";
var frutas = [
{ nome: 'banana', peso: 100 },
{ nome: 'maçã', peso: 80 },
{ nome: 'abacaxi', peso: 3500 } // um abacaxi enorme!
];
var ordenador = {
ordenarPor: 'nome',
ordenar: function(arrayOriginal) {
arrayOriginal.sort(this.comparador); // sort muda o contexto de
execução
},
comparador: function(a, b) {
// this aponta para o quê aqui??
// note também o uso da notação [] para acessar campos
dinamicamente
if (typeof a[this.ordenarPor] === 'number'
&& typeof b[this.ordenarPor] === 'number'
) {
return a[this.ordenarPor] - b[this.ordenarPor];
}
if (a[this.ordenarPor] > b[this.ordenarPor]) {
return 1;
} else if (a[this.ordenarPor] < b[this.ordenarPor]) {
return -1;
} else if (a[this.ordenarPor] == b[this.ordenarPor]) {
return 0;
}
}
};
ordenador.ordenarPor = 'nome';
ordenador.ordenar(frutas);
console.log(frutas);
ordenador.ordenarPor = 'peso';
ordenador.ordenar(frutas);
console.log(frutas);
this parece apontar para o objeto ordenador, mas isso não é sempre verdade. Quando
passamos o método para arrayOriginal.sort, nosso comparador vai executar num
contexto diferente. Assim othis referenciado não é o que esperávamos. Tentamos usar
a propriedade ordenarPor que não está mais acessível e isso causa uma exceção.
Deixando as coisas mais previsíveis
Não estamos desamparados para resolver os problemas de mudança de contexto
(amém!). De fato, as funções em JavaScript possuem diversos métodos para controlar
essa mudança. Vamos ver a seguir dois deles.
Lembrando que em JavaScript funções são objetos, em navegadores recentes as funções
possuem um método chamado bind, que retorna uma cópia da função com o this fixo.
código js - controlando contexto com bind
"use strict";
arrayOriginal.sort(this.comparador.bind(this)); // ES3
Para navegadores mais antigos temos outras duas alternativas. A concentualmente mais
simples é utilizar jQuery.proxy.
código js - controlando contexto com jQuery.proxy
"use strict";
arrayOriginal.sort(jQuery.proxy(this.comparador, this);
Note que jQuery.proxy permite passar parâmetros para a função na seguinte forma:
código js - passando parâmetros com jQuery.proxy
"use strict";
jQuery.proxy(this.comparador, this, var1, var2, var3);
Internamente jQuery.proxy utiliza um outro método pertencente às funções, que
também altera o valor de this, chamado apply. Na verdade, não é difícil fazermos uma
versão simplificada de bind, oujQuery.proxy, evitando ser necessário uma biblioteca
inteira apenas para essa funcionalidade. A parte que fala sobre compatibilidade explica
isso com mais detalhes.
A terceira alternativa é a mais manual e complexa. Possui porém a vantagem de cobrir
todos os casos que o bind nativo cobre. Ela utiliza o conceito de closures. Closures são
uma parte mais delicada da linguagem, portanto vou apresentá-la antes e depois
voltamos à terceira solução.
Closures e campos private
Objetos em JavaScript se lembram do escopo em que foram criados, e podem acessá-lo
se precisarem. Se movermos esse objeto para fora de seu escopo original, ela ainda pode
ler variáveis e funções de lá.
código js - closures
"use strict";
function externa() {
var informacao = 'wikileaks';
return function interna() {
console.log('vazou ' + informacao);
}
}
var dedoDuro = externa();
dedoDuro(); // expões os segredos dentro do escopo!
JavaScript não possui uma maneira formal de criar permissões de acesso
com private, protected e public. Mas podemos usar closures para simular esta
funcionalidade. Primeiro criamos um contexto com nossa já conhecida função anônima
autoexecutável, e vazamos desse contexto a parte que queremos usar como interface de
nosso objeto. Este objeto será usado de maneira transparente, sem ninguém precisar
saber que sua funcionalidade está fragmentada internamente. O exemplo abaixo mostra
o essencial deste procedimento.
código js - encapsulamento
"use strict";
var objeto = (function() {
var publico = {
// acessar privado aqui dentro
};
var privado = {
// se necessário, pode acessar publico
};
return publico; // expõe apenas a interface pública
})();
Um exemplo mais completo.
código js - encapsulamento
"use strict";
var matador = (function() {
var publico = {
matarACobra: function() {
return privado.metodoInterno();
},
getPau: function() {
return privado.propriedadeInterna;
}
};
var privado = {
metodoInterno: function() {
return "método interno";
},
propriedadeInterna: 10
};
return publico;
})();
console.log(matador.matarACobra());
console.log(matador.getPau());
Revisitando nosso abacaxi
A terceira solução para nosso problema, é simplesmente não utilizar a palavra this.
Apenas a palavra this muda de significado, se conseguirmos referenciar nosso objeto
original por outro nome, podemos acessá-lo sempre que precisarmos sem ter medo de
uma crise de identidade. Para fazermos isso usamos uma closure. A primeira forma de
utilizá-la, é fazendo-a conter a função completa de comparação.
código js - resolvendo mudanças de contexto com closures
"use strict";
var frutas = [
{ nome: 'banana', peso: 100 },
{ nome: 'maçã', peso: 80 },
{ nome: 'abacaxi', peso: 3500 } // um abacaxi enorme!
];
var ordenador = {
ordenarPor: 'nome',
init: function() {
this.comparador = this.closureComparador(this);
},
ordenar: function(arrayOriginal) {
arrayOriginal.sort(this.comparador);
},
closureComparador: function (contexto) {
return function(a, b) {
if (typeof a[contexto.ordenarPor] === 'number'
&& typeof b[contexto.ordenarPor] === 'number'
) {
return a[contexto.ordenarPor] -
b[contexto.ordenarPor];
}
if (a[contexto.ordenarPor] > b[contexto.ordenarPor]) {
return 1;
} else if (a[contexto.ordenarPor] <
b[contexto.ordenarPor]) {
return -1;
} else if (a[contexto.ordenarPor] ==
b[contexto.ordenarPor]) {
return 0;
}
}
},
comparador: null
};
ordenador.init();
ordenador.ordenarPor = 'nome';
ordenador.ordenar(frutas);
console.log(frutas);
ordenador.ordenarPor = 'peso';
ordenador.ordenar(frutas);
console.log(frutas);
Note que precisamos ser bastante cuidadosos para sempre escrever contexto ao invés
de this. Existe uma segunda forma dessa solução, que utiliza a closure para
simplesmente passar o contexto correto ao método executado.
código js - resolvendo mudanças de contexto com closures - solução alternativa
"use strict";
var frutas = [
{ nome: 'banana', peso: 100 },
{ nome: 'maçã', peso: 80 },
{ nome: 'abacaxi', peso: 3500 } // um abacaxi enorme!
];
var ordenador = {
ordenarPor: 'nome',
init: function() {
this.comparador = this.closureComparador(this);
},
ordenar: function(arrayOriginal) {
arrayOriginal.sort(this.comparador);
},
closureComparador: function (contexto) {
return function(a, b) {
contexto.comparar(a, b);
}
},
comparador: null,
comparar: function(a, b) {
if (typeof a[this.ordenarPor] === 'number'
&& typeof b[this.ordenarPor] === 'number'
) {
return a[this.ordenarPor] - b[this.ordenarPor];
}
if (a[this.ordenarPor] > b[this.ordenarPor]) {
return 1;
} else if (a[this.ordenarPor] < b[this.ordenarPor]) {
return -1;
} else if (a[this.ordenarPor] == b[this.ordenarPor]) {
return 0;
}
}
};
ordenador.init();
ordenador.ordenarPor = 'nome';
ordenador.ordenar(frutas);
console.log(frutas);
ordenador.ordenarPor = 'peso';
ordenador.ordenar(frutas);
console.log(frutas);
Desacoplando
JavaScriptdas linguagens server-side
Se estruturarmos nossa programação de modo que as bibliotecas com os objetos estejam
em arquivos externos, podemos apenas chamar os construtores de nossos objetos nas
páginas com programação server-side, passando os parâmetros com valores dinâmicos.
serverside.php
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Html mínimo aceitável</title>
<meta charset="utf-8" />
<script src="dropdown.js"></script>
<script>
(function() {
"use strict";
dropdown.init(<?php // valor calculado server side ?>);
})();
</script>
</head>
<body>
</body>
</html>
dropdown.js
"use strict";
var dropdown = {
init: function(idConteiner) {
this.idConteiner = idConteiner;
this.conteiner = document.getElementById(this.idConteiner);
this.rotulo = this.conteiner.querySelector('button');
this.lista = this.conteiner.querySelector('ul');
this.rotulo.addEventListener('click',
this.clickHandler.bind(this));
},
clickHandler: function(evento) {
this.visivel = !this.visivel;
this.atualizar();
},
atualizar: function() {
if (this.visible) {
this.lista.style.display = 'block';
return;
}
this.lista.style.display = 'none';
},
idConteiner: null,
conteiner: null
rotulo: null,
lista: null,
visivel: false
};
Ou usando closures.
dropdown.js
"use strict";
var dropdown = (function(){
var publico = {
init: function(idConteiner) {
privado.idConteiner = idConteiner;
privado.conteiner =
document.getElementById(privado.idConteiner);
privado.rotulo =
privado.conteiner.querySelector('button');
privado.lista = privado.conteiner.querySelector('ul');
privado.rotulo.addEventListener('click',
this.clickHandler.bind(this));
},
clickHandler: function(evento) {
privado.visivel = !privado.visivel;
privado.atualizar();
}
};
var privado = {
atualizar: function() {
if (this.visible) {
this.lista.style.display = 'block';
return;
}
this.lista.style.display = 'none';
},
idConteiner: null,
conteiner: null
rotulo: null,
lista: null,
visivel: false
};
return publico;
})();
Eventos
o que são eventos passar funções anônimas e objetos para eventos
Herança Maldita
Herança em JavaScript é baseada em protótipos. Um objeto herda diretamente de
outro objeto, utilizando o método Object.create. Já vimos isso rapidamente quando
definimos um objeto com getters e setters utilizando descritores de propriedades.
Vamos ver agora um exemplo com polimorfismo. Se precisarmos acessar o objeto base
de dentro do objeto filho, chamamos o métodoObject.getPrototypeOf, que retorna um
objeto acima da cadeia de herança.
Note que estamos tratando de objetos concretos, e não de definições de objetos como
são as classes em outras linguagens. Isso significa que, quando acessarmos o objeto
base, estaremos acessandonão só as definições de métodos mas também os valores das
propriedades dele. Devemos portanto tomar o cuidado de, ao chamar estes métodos
base, indicar que queremos executá-los no contexto do objeto filho.
Outro cuidado que devemos tomar, é o de atribuir valores aos objetos somente num
método separado, que o inicializa. Se atribuirmos valores diretamente na definição do
objeto, e estes valores forem tipos de referência como arrays, eles serão compartilhados
pelos subobjetos ao invés de cada um ter sua própria cópia.
código js - herança com polimorfismo
"use strict";
var objetoPai = {
init: function() {
this.propriedade = []; // inicializar valores das propriedades
só aqui! Muito importante!
},
metodo: function() {
console.log(this.propriedade);
},
propriedade: null
};
// herança
var objetoFilho = Object.create(objetoPai);
// sobrescrita do método
objetoFilho.metodo = function() {
Object.getPrototypeOf(this).metodo.call(this);
}
objetoPai.init(3);
objetoFilho.init(3);
objetoPai.propriedade.push('pai');
objetoFilho.propriedade.push('filho');
objetoPai.metodo();
objetoFilho.metodo();
Se não precisarmos de compatibilidade com navegadores antigos, podemos usar
descritores de propriedade, que nos dão mais controle sobre a definição do objeto.
código js - herança com polimorfismo ES5
"use strict";
var objetoPai = Object.create(Object.prototype, {
init: {
value: function() {
this.propriedade = []; // inicializar valores das
propriedades só aqui! Muito importante!
},
writable: false
},
metodo: {
value: function() {
console.log(this.propriedade);
},
writable: true
},
propriedade: {
value: null,
writable: true
}
});
// herança
var objetoFilho = Object.create(objetoPai, {
// sobrescrita do método
metodo: {
value: function() {
Object.getPrototypeOf(this).metodo.call(this);
},
writable: false
}
});
objetoPai.init(3);
objetoFilho.init(3);
objetoPai.propriedade.push('pai');
objetoFilho.propriedade.push('filho');
objetoPai.metodo();
objetoFilho.metodo();
Mostro também um exemplo de herança com a sintaxe antiga. Note que a sintaxe é mais
confusa e propensa a erros, pois possui etapas manuais que devem ser lembradas toda
a vez. Além disso, existem as questões comentadas anteriormente sobre misturar
definições de coisas no construtor e no protótipo, e dar a ilusão que JavaScript possui
classes, o que não é verdade.
código js - herança com polimorfismo com sintaxe antiga, não use!
"use strict";
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
function ObjetoPai(valor) {
// definir e inicializar valores aqui
this.propriedade = [];
this.propriedade.push(valor);
}
ObjetoPai.prototype.metodo = function() {
console.log(this.propriedade);
};
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// herança
function ObjetoFilho() {
ObjetoPai.apply(this, Array.prototype.slice.call(arguments)); //
"super()"
}
ObjetoFilho.prototype = new ObjetoPai(); // especificar manualmente qual o
protótipo para herdar
ObjetoFilho.prototype.constructor = ObjetoFilho; // sobrescrever manualmente
o construtor
// sobrescrita do método
ObjetoFilho.prototype.metodo = function() {
ObjetoPai.prototype.metodo.call(this); // nome do objeto base é
hardcoded e não muda automaticamente de acordo com a herança
};
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
var objetoPai = new ObjetoPai('pai');
var objetoFilho = new ObjetoFilho('filho');
objetoPai.metodo();
objetoFilho.metodo();
Vamos ver um exemplo mais completo.
código js - polimorfismo, exemplo completo
"use strict";
// define o objeto base
var ampulheta = {
init: function(tempo) {
// define os valores padrão das propriedades
this.tempoTotal = tempo;
this.tempoAtual = this.tempoTotal;
},
contar: function() {
if (this.tempoAtual > 0) {
this.tempoAtual--;
}
},
girar: function() {
this.tempoAtual = this.tempoTotal - this.tempoAtual;
},
tempoTotal: null,
tempoAtual: null
};
// herança
var ampulhetaAutomatica = Object.create(ampulheta); // "ampulhetaAutomatica
extends ampulheta"
// define os métodos do objeto derivado
ampulhetaAutomatica.contar = function() {
if (this.tempoAtual <= 0) {
this.girar();
} else {
// chamar o método da maneira abaixo o executaria no contexto do
objeto pai, ampulheta
// Object.getPrototypeOf(this).contar();
// por isso usando o método call da função para mudar o contexto
novamente para ampulhetaAutomática
Object.getPrototypeOf(this).contar.call(this);
}
}
ampulheta.init(3);
ampulhetaAutomatica.init(3);
ampulheta.contar(); // 2
ampulheta.contar(); // 1
ampulheta.contar(); // 0
ampulheta.girar(); // 3
ampulhetaAutomatica.contar(); // 2
ampulhetaAutomatica.contar(); // 1
ampulhetaAutomatica.contar(); // 0
ampulhetaAutomatica.contar(); // 3
Mostrar properties
código js - polimorfismo, exemplo completo com sintaxe antiga, não use!
"use strict";
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// define o objeto base
function Ampulheta(tempo) {
// define as propriedades e valores padrão
this.tempoTotal = tempo;
this.tempoAtual = this.tempoTotal;
}
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// define os métodos do objeto base
Ampulheta.prototype.contar = function() {
if (this.tempoAtual > 0) {
this.tempoAtual--;
}
};
Ampulheta.prototype.girar = function() {
this.tempoAtual = this.tempoTotal - this.tempoAtual;
};
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// define o objeto derivado
function AmpulhetaAutomatica() {
Ampulheta.apply(this, Array.prototype.slice.call(arguments)); //
"super()"
}
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// herança
AmpulhetaAutomatica.prototype = new Ampulheta(); // "AmpulhetaAutomatica
extends Ampulheta"
AmpulhetaAutomatica.prototype.constructor = AmpulhetaAutomatica; //
"conserta" o construtor
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// define os métodos do objeto derivado
AmpulhetaAutomatica.prototype.contar = function() {
if (this.tempoAtual <= 0) {
this.girar();
} else {
// chamar o método da maneira abaixo o executaria no contexto do
objeto pai, ampulheta
// Ampulheta.prototype.contar();
// por isso usando o método call da função para mudar o contexto
novamente para ampulhetaAutomatica
Ampulheta.prototype.contar.call(this);
}
};
// NÃO COPIE!!! SINTAXE ANTIGA!!!!
// simulação de sintaxe de outras linguagens
var contador = new Ampulheta(3);
var contadorAutomatico = new AmpulhetaAutomatica(3);
contador.contar(); // 2
contador.contar(); // 1
contador.contar(); // 0
contador.girar(); // 3
contadorAutomatico.contar(); // 2
contadorAutomatico.contar(); // 1
contadorAutomatico.contar(); // 0
contadorAutomatico.contar(); // 3
Ado, a ado, cada um no
seu quadrado -
Namespaces
Como JavaScript escrito sem cuidado pode poluir rapidamente o espaço global, existe
uma maneira de evitar a sobrescrita de variáveis.
var console = console || {}; // cria um objeto somente se console não
existir
Guarde esse padrão, porque vamos utilizar esta técnica mais para frente para outros
meios também.
Vimos anteriormente que utilizando funções anônimas autoexecutáveis isolamos um
escopo. Porém, não conseguimos acessá-lo de fora. Para tanto, precisamos simular um
namespace. Aproveitamos que JavaScript permite adicionar dinamicamente
propriedades em objetos para fazer isso.
código js - simulação de namespaces
var namespace = namespace || {};
namespace.subnamespace = namespace.subnamespace || {};
namespace.subnamespace.meuObjeto = {
metodo: function() {
//
}
};
(function() {
// "import"
var meuObjeto = namespace.subnamespace.meuObjeto;
meuObjeto.metodo();
})();
O primeiro nivel do namescape ocupa o espaço global, que como já vimos, é
compartilhado por todos os arquivos e blocos de script da página. Portanto, podemos
acrescentar mais coisas posteriormente ao mesmo namespace em um arquivo diferente.
código js - simulação de namespaces, outro arquivo
var namespace = namespace || {};
namespace.subnamespace = namespace.subnamespace || {};
namespace.subnamespace.meuOutroObjeto = {
metodo: function() {
//
}
};
(function() {
// "import"
var meuObjeto = namespace.subnamespace.meuObjeto;
var meuOutroObjeto = namespace.subnamespace.meuOutroObjeto;
meuObjeto.metodo();
meuOutroObjeto.metodo();
})();
Conversão de tipos e falsy values
JavaScript converte tipos automaticamente em true e false quando necessário. Isso
nos proporciona uma sintaxe reduzida para verificar estes valores e a existência de
variáveis.
Os valores que são convertidos para false são chamados de falsy. são
eles "", 0, null, undefined e NaN. Todos os demais são convertidos para true.
var objeto = {};
if (objeto) {
//
}
// isso equivale a
if (objeto == true) {
//
}
// e ainda a
if (objeto !== false && objeto !== "" && objeto !== 0 && objeto !== null
&& objeto !== undefined && objeto !== NaN) {
//
}
Note que existem dois comparadores de igualdade em JavaScript. == e ===, assim como
seus opostos != e !==. A primeira forma converte automaticamente os tipos, enquanto
que a outra não.
Nota final
Podemos verificar nosso código na ferramenta online JSLint, que nos dá divas para
melhorá-lo.
Apêndice
SOLUÇÃO 3 - Postergar execução na programação
seosoquenaoS3.htm
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="pt-BR"
xml:lang="pt-br"
>
<head>
<title>Google está olhando</title>
<meta charset="utf-8" />
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="seosoquenaoS3.js"></script>
</head>
<body>
<h1 id="tagTitulo">Acessibilidade e SEO</h1>
<p id="tagParagrafo">
Sim pequeno gafanhoto, html tem mais de 100 tags
diferentes além de table e div.
</p>
</body>
</html>
seosoquenaoS3b.js
"use strict";
function init() {
//pega elementos da página pela id e os torna manipuláveis
dentro do JavaScript
var titulo = document.getElementById('tagTitulo');
var paragrafo = document.getElementById('tagParagrafo');
titulo.innerHTML = 'Black hat SEO';
paragrafo.attributes.add('style', 'display: none');
}
jQuery(document).ready(function(evento) {
init();
});
Como citei lá no comecinho, jQuery ganhou fama justamente por esconder as várias
incompatibilidades entre os navegadores através de sua API. Este exemplo faz a mesma
coisa que o exemplo anterior, porém depende de uma biblioteca externa. Se isso não for
um problema pra vc, vai em frente. A sintaxe do jQuery é um pouco diferente do
JavaScript convencional, e será abordada mais pra frente.
seosoquenaoS3.js
"use strict";
function init() {
//pega elementos da página pela id e os torna manipuláveis
dentro do JavaScript
var titulo = document.getElementById('tagTitulo');
var paragrafo = document.getElementById('tagParagrafo');
titulo.innerHTML = 'Black hat SEO';
paragrafo.attributes.add('style', 'display: none');
}
//verifica se o JavaScript rodou após o evento de carregamento completado da
página
if (document.readyState === "interactive"
|| document.readyState === "complete"
|| document.readyState === "loaded"
) {
init();
//se não, se registra para executar no evento
} else {
//registro do evento para navegadores legais
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', init);
//registro de evento para IE8-
} else if (window.attachEvent) {
window.attachEvent('onload', init);
}
}
Esta solução é a mais compatível. Porém, precisamos adicionar uma verificação meio
grande pra tudo funcionar de acordo com o que esperamos. Note que, dependendo do
momento em que o JavaScript passa a existir na página, o evento indicando que o
processamento do html terminou pode já ter sido disparado. Existe também a questão de
compatibilidade com IEs antigos. Claro que podemos refatorar esse código e facilitar o
uso deste evento. Mas aqui no ICI fazemos outra coisa. Usamos jQuery.

Mais conteúdo relacionado

Mais procurados

classes_objetos_ e_cia_em_java
classes_objetos_ e_cia_em_javaclasses_objetos_ e_cia_em_java
classes_objetos_ e_cia_em_java
Bel Arts
 
Crackeando aplicativos no android _by c0_m3nd4d0r
Crackeando aplicativos no android  _by c0_m3nd4d0rCrackeando aplicativos no android  _by c0_m3nd4d0r
Crackeando aplicativos no android _by c0_m3nd4d0r
nunes666
 

Mais procurados (20)

Ruby
RubyRuby
Ruby
 
Trabalho sobre a linguagem Python
Trabalho sobre a linguagem PythonTrabalho sobre a linguagem Python
Trabalho sobre a linguagem Python
 
classes_objetos_ e_cia_em_java
classes_objetos_ e_cia_em_javaclasses_objetos_ e_cia_em_java
classes_objetos_ e_cia_em_java
 
Listas (parte 2 de 3)
Listas (parte 2 de 3)Listas (parte 2 de 3)
Listas (parte 2 de 3)
 
Programação Orientada a Objetos parte 3
Programação Orientada a Objetos parte 3Programação Orientada a Objetos parte 3
Programação Orientada a Objetos parte 3
 
Aula04-JavaScript
Aula04-JavaScriptAula04-JavaScript
Aula04-JavaScript
 
Xdebug seus problemas acabaram - tdc floripa 2017
Xdebug   seus problemas acabaram - tdc floripa 2017Xdebug   seus problemas acabaram - tdc floripa 2017
Xdebug seus problemas acabaram - tdc floripa 2017
 
JavaScript - #Aula03 parte 03 - Bichinho Virtual - Respondendo Perguntas
JavaScript - #Aula03 parte 03 - Bichinho Virtual - Respondendo PerguntasJavaScript - #Aula03 parte 03 - Bichinho Virtual - Respondendo Perguntas
JavaScript - #Aula03 parte 03 - Bichinho Virtual - Respondendo Perguntas
 
Logica programar
Logica programarLogica programar
Logica programar
 
Programação Orientada a Objetos parte 2
Programação Orientada a Objetos parte 2Programação Orientada a Objetos parte 2
Programação Orientada a Objetos parte 2
 
Crackeando aplicativos no android _by c0_m3nd4d0r
Crackeando aplicativos no android  _by c0_m3nd4d0rCrackeando aplicativos no android  _by c0_m3nd4d0r
Crackeando aplicativos no android _by c0_m3nd4d0r
 
Apostila linguagem pascal
Apostila linguagem pascalApostila linguagem pascal
Apostila linguagem pascal
 
Programação Orientada a Objetos parte 1
Programação Orientada a Objetos parte 1Programação Orientada a Objetos parte 1
Programação Orientada a Objetos parte 1
 
Facebook's Hack programming language / Linguagem de programação Hack do Facebook
Facebook's Hack programming language / Linguagem de programação Hack do FacebookFacebook's Hack programming language / Linguagem de programação Hack do Facebook
Facebook's Hack programming language / Linguagem de programação Hack do Facebook
 
Aprendendo C# do zero
Aprendendo C# do zeroAprendendo C# do zero
Aprendendo C# do zero
 
Aula10 -PHP
Aula10 -PHPAula10 -PHP
Aula10 -PHP
 
Java e orientação a objetos
Java e orientação a objetosJava e orientação a objetos
Java e orientação a objetos
 
Aprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalhoAprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalho
 
Palestra: Introdução à TypeScript & Features Ts 2.x
Palestra: Introdução à TypeScript & Features Ts 2.x Palestra: Introdução à TypeScript & Features Ts 2.x
Palestra: Introdução à TypeScript & Features Ts 2.x
 
Fascículo1java
Fascículo1javaFascículo1java
Fascículo1java
 

Semelhante a Tutorial java script orientado à objeto e jquery

Linguagem de java
Linguagem de javaLinguagem de java
Linguagem de java
Genique
 
Java Fundamentos
Java FundamentosJava Fundamentos
Java Fundamentos
Wilson Lima
 
Aprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalhoAprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalho
FelipeDi
 
J query apostila - noções básicas
J query   apostila - noções básicasJ query   apostila - noções básicas
J query apostila - noções básicas
Luciano Marwell
 

Semelhante a Tutorial java script orientado à objeto e jquery (20)

Aula1- Java PRof.ª Cristiane Fidelix
Aula1- Java PRof.ª Cristiane FidelixAula1- Java PRof.ª Cristiane Fidelix
Aula1- Java PRof.ª Cristiane Fidelix
 
LIVRO PROPRIETÁRIO - PROGRAMAÇÃO I
LIVRO PROPRIETÁRIO - PROGRAMAÇÃO ILIVRO PROPRIETÁRIO - PROGRAMAÇÃO I
LIVRO PROPRIETÁRIO - PROGRAMAÇÃO I
 
Linguagem de java
Linguagem de javaLinguagem de java
Linguagem de java
 
Comandos gerais do PHP
Comandos gerais do PHPComandos gerais do PHP
Comandos gerais do PHP
 
Java programação orientada a objetos
Java   programação orientada a objetosJava   programação orientada a objetos
Java programação orientada a objetos
 
Javascript - Aplicações Interativas para a Web
Javascript - Aplicações Interativas para a WebJavascript - Aplicações Interativas para a Web
Javascript - Aplicações Interativas para a Web
 
Hangout Tempo Real Eventos - Nodejs - Os Primeiros Passos
Hangout  Tempo Real Eventos - Nodejs - Os Primeiros PassosHangout  Tempo Real Eventos - Nodejs - Os Primeiros Passos
Hangout Tempo Real Eventos - Nodejs - Os Primeiros Passos
 
Javascript levado a serio
Javascript levado a serioJavascript levado a serio
Javascript levado a serio
 
Isc aula 7
Isc   aula 7Isc   aula 7
Isc aula 7
 
Java Fundamentos
Java FundamentosJava Fundamentos
Java Fundamentos
 
Vivendo de hacking
Vivendo de hackingVivendo de hacking
Vivendo de hacking
 
Aprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalhoAprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalho
 
Aula de C para Linux
Aula de C para LinuxAula de C para Linux
Aula de C para Linux
 
J query apostila - noções básicas
J query   apostila - noções básicasJ query   apostila - noções básicas
J query apostila - noções básicas
 
JQuery - introdução ao
JQuery - introdução ao JQuery - introdução ao
JQuery - introdução ao
 
J query basico
J query basicoJ query basico
J query basico
 
Tutorial JSP parte 1
Tutorial JSP parte 1Tutorial JSP parte 1
Tutorial JSP parte 1
 
Aprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalhoAprenda a programar-luciano_ramalho
Aprenda a programar-luciano_ramalho
 
Ruby
RubyRuby
Ruby
 
Guia php
Guia phpGuia php
Guia php
 

Último

Último (8)

Boas práticas de programação com Object Calisthenics
Boas práticas de programação com Object CalisthenicsBoas práticas de programação com Object Calisthenics
Boas práticas de programação com Object Calisthenics
 
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docxATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
ATIVIDADE 1 - GCOM - GESTÃO DA INFORMAÇÃO - 54_2024.docx
 
ATIVIDADE 1 - ESTRUTURA DE DADOS II - 52_2024.docx
ATIVIDADE 1 - ESTRUTURA DE DADOS II - 52_2024.docxATIVIDADE 1 - ESTRUTURA DE DADOS II - 52_2024.docx
ATIVIDADE 1 - ESTRUTURA DE DADOS II - 52_2024.docx
 
Padrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemploPadrões de Projeto: Proxy e Command com exemplo
Padrões de Projeto: Proxy e Command com exemplo
 
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docxATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
ATIVIDADE 1 - LOGÍSTICA EMPRESARIAL - 52_2024.docx
 
Luís Kitota AWS Discovery Day Ka Solution.pdf
Luís Kitota AWS Discovery Day Ka Solution.pdfLuís Kitota AWS Discovery Day Ka Solution.pdf
Luís Kitota AWS Discovery Day Ka Solution.pdf
 
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docxATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
ATIVIDADE 1 - CUSTOS DE PRODUÇÃO - 52_2024.docx
 
Programação Orientada a Objetos - 4 Pilares.pdf
Programação Orientada a Objetos - 4 Pilares.pdfProgramação Orientada a Objetos - 4 Pilares.pdf
Programação Orientada a Objetos - 4 Pilares.pdf
 

Tutorial java script orientado à objeto e jquery

  • 1. Tutorial JavaScript Orientado à Objeto e jQuery Bem-vindo ao tutorial de JavaScript orientado à objeto, politicamente incorreto. Índice o Índice o A pressa passa e a merda fica Introdução Histórica (e lição de vida) o JavaScript un zero un ¡Hola Mundo! o Como declarar uma variável Sério mesmo, como declarar uma variável!  Number:  String  Array  RegExp  Function  Object  Object antigo  Escopo das variáveis  Misturando tudo com jQuery o DOM - El Poderoso Jefón  Solução 1 - Movendo a tag script  Solução 2 - Postergando a execução na tag  Se familiarizando com a Cosa Nostra o Google - O Grande Irmão o This doesn't makes sense - OOP  Deixando as coisas mais previsíveis  Closures e campos private  Revisitando nosso abacaxi o Desacoplando JavaScript das linguagens server-side o Eventos o Herança Maldita o Ado, a ado, cada um no seu quadrado - Namespaces  Conversão de tipos e falsy values o Nota final o Apêndice
  • 2. A pressa passa e a merda ficaIntrodução Histórica (e lição de vida) JavaScript foi criado e programado em 2 semanas pela netscape para validar formulários dentro dos navegadores, sem a necessidade de recarregar a página. Aparentemente na época das conexões discadas medidas em BAUDS isso era muito importante (e com o 3G da tim tb ¬¬). Porém, este período curto para desenvolvimento da linguagem gerou um produto com diversas questões controversas. Seu nome foi puramente uma jogada de marketing, tentando se aproveitar da popularidade do Java, que na época era muito usado para fazer applets. JavaScript teve várias versões após isso, sendo que a versão atual, padronizada como ES5, é bastante diferente da inicial. Ela é a única linguagem de programação capaz de interagir com todos os navegadores de internet. Também pode ser utilizada para programação server side, através do Node.js, mas não vou abordar isso pois não tenho conhecimento suficiente. JavaScript já foi chamada de linguagem mais mal entendida do mundo. Grandes empresas como a Microsoft e o Google passaram bastante tempo desenvolvendo ferramentas para que não seja necessário escrever JavaScript. E o motivo de tudo isso... é que JavaScript é como espanhol. Hay um hombre tarado con el... Parece com português, mas significa uma coisa completamente diferente. Há um homem careca com o casaco na mão correndo atrás do ônibus. PS: Essas palavras se chamam falsos cognatos. A sintaxe do JavaScript parece com C e derivados, mas significa coisas diferentes. Por exemplo, this faz coisas bem diferentes dos seus equivalentes em C#. É um falso cognato Vou enfatizar justamente as diferenças ao longo desse tutorial. Mas antes cabe rapidamente rever um outro fato histórico, importante no desenvolvimento dessa linguagem. A Netscape inicialmente não tinha intenção que a linguagem fosse padronizada ou aberta. Ela queria dominar o mercado, forçando as pessoas a fazerem páginas que só funcionavam no netscape, e abriam zoadas no seu concorrente, o internet explorer. Esse episódio é conhecido como Browser Wars. O resultado disso foi uma grande divisão na linguagem, gerando incompatibilidades que temos que lidar até a versão 8 do Internet Explorer. Este foi o principal motivo do sucesso de bibliotecas com jQuery, que escondem estas incompatibilidades através de sua API. Como os smartphones já nasceram com navegadores posteriores ao IE8, sites e aplicativos para estes dispositivos não precisam se preocupar com isso.
  • 3. JavaScript un zero un¡Hola Mundo! Sei que vc é um programador fodástico e que não precisa que ninguém te ensine nada de programação. Então não vou mostrar nada além do estritamente necessário para explicar as diferenças de JavaScript em comparação com C e derivados. Ok? A linguagem em si é bem pequena, afinal, não dá pra pirar muito em 2 semanas. Suas incompatibilidades no IE são poucas. Mas, existe uma API exposta pelos navegadores, comumente utilizada em conjunto com JavaScript, chamada Document Object Model (DOM), que é bem maior e mais incompatível. Ele serve para que o JavaScript acesse os elementos de um arquivo html, xml ou svg. Mas vamos ver uma coisa de cada vez. JavaScript pode existir dentro de um arquivo .js, .htm ou .svg Arquivos .js contém apenas programação, ao passo que arquivos .htm e .svg misturam linguagens de marcação com JavaScript. Podemos usar o bloco de notas para criar e editar um arquivo .js. O firefox tem uma ferramentinha bem útil pra testarmos pequenos blocos de JavaScript, sem precisar usar um arquivo .js externo. Ela se chama scratchpad. Para o próximo exemplo, vamos usar um arquivo .htm bem simples para fazer nossos testes. Copie o trecho abaixo no bloco de notas de sua preferência, e salve como um arquivo .htm index.htm - olá mundo <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Html mínimo aceitável</title> <meta charset="utf-8" /> <script src="tutorial.js"></script> </head> <body> </body> </html> PS: Html também tem versões, que fazem com que a página pareça e se comporte de maneira diferente de versão para versão. A primeira linha do arquivo: <!DOCTYPE html> indica que estamos usando a versão 5 do html. Depois cole o texto abaixo num arquivo novo, e salve com a extensão .js. Salve na mesma pasta que o arquivo .htm Note que a página deste exemplo usa utf-8. Se vira!
  • 4. tutorial.js - olá mundo "use strict"; console.log('Utilizadores de alert terão seus dedos cortados fora.'); Abra o arquivo .htm no seu navegador. Se vc fez tudo certo, observe atentamente que não vai acontecer absolutamente porra nenhuma. Porém, se for IE 10-, vai gerar um erro! Começamos bem hein! Disse que não ia mostrar nada além do estritamente necessário para explicar as diferenças de JavaScript. E o propósito desse exemplo é explicar algumas coisas sobre o ambiente de execução da linguagem e das ferramentas de desenvolvimento dos navegadores. Pra ver o resultado de nossa programação precisamos abrir as ferramentas de desenvolvimento, que todos os navegadores possuem (o Firefox possui duas, uma nativa e uma em forma de extensão). Para abrir faça:  Chrome, Firefox (nativo), Opera e Safari: Ctrl + Shift + i  Firefox (extensão firebug) e IE: F12 Procure por uma aba chamada console. É para o console que as mensagens de debug vão. Recarregue a página e você deverá ver nossa mensagem instrutiva, inclusive no IE. As ferramentas de desenvolvimento também possibilitam inserir breakpoints no meio do código e avançar linha por linha, como outras IDEs. Vamos agora explicar nosso exemplo inicial linha por linha. "use strict"; A versão ES5 do JavaScript possui um modo, chamado modo estrito. Ao incluir "use strict"; como a primeira linha do arquivo, você está habilitando este modo. Ele garante compatibilidade futura com novas versões da linguagem, além de reduzir as chances de introduzir erros na programação. Este modo pode ser habilitado para o arquivo inteiro, ou apenas para determinadas funções. Veremos esta segunda forma mais adiante. console.log('Utilizadores de alert terão seus dedos cortados fora.'); console é um objeto, mas não pertence à linguagem. Ele faz parte da API do navegador, o DOM. Na prática isso significa que nem sempre ele está disponível para nós utilizarmos. Cada navegador expõe métodos diferentes deste objeto, e os interpreta de maneiras diversas. O método log existe em todos eles, e possui inúmeras vantagens sobre métodos anteriores de debug. Note que não existe método main. Uma página pode conter diversos blocos separados de JavaScript, portanto não existe o conceito do ponto único de entrada do seu código. Assim que o navegador identifica um trecho de código JavaScript, ele o interpreta e executa linha por linha. Se o mesmo arquivo for inserido duas vezes, ele será executado duas vezes. Não existem include guards em JavaScript, como em C ou PHP.(#ifndef #define #endif, include_once...). Em .net, existe algo similar em funcionalidade aos include guards. O método RegisterStartupScript, recebe uma id ao registrar um script. Se for registrado um novo script com a mesma id, o script anterior é descartado.
  • 5. Como declarar uma variávelSério mesmo, como declarar uma variável! Declaramos uma variável com a palavra-chave var, seguida do nome da variável; Opcionalmente atribuímos um valor para ela. Se não atribuirmos, ela terá o valor padrão undefined. código js - declaração de variáveis "use strict"; var naoInicializada; // possui valor undefined var dica = 'JavaScript não é "fortemente tipado"'; Não declaramos o tipo da variável, pois JavaScript não proíbe que o tipo dela mude (não é fortemente tipada). O tipo é inferido através do valor atualmente atribuído à variável. Note porém que se mantivermos o mesmo tipo da variável ao longo da execução de nosso código, o compilador consegue aplicar otimizações de performance. Se necessário, é possível verificar o tipo da variável usando o operador typeof A lista completa de tipos de variáveis pode ser vista no MDN, que é o portal de desenvolvimento da Mozilla. Vamos ver apenas os tipos que diferem de alguma maneira de seus primos em outras linguagens. Number: Sempre é um double. código js - exemplos de números válidos "use strict"; var i = 1; // é um double! var area = 4.527; // também é um double var negativo = -2; // sem notação especial para números negativos String É um objeto imutável. Pode usar aspas simples ou duplas. Para quebrá-la em mais de uma linha, escapamos a quebra de linha com . código js - exemplos de strings válidas "use strict"; var simples = 'A primeira faz "tchan"'; var dupla = "A segunda faz 'tchun'"; var iara = "mãe d'água"; var html = '<em class="especial">ênfase</em>'; var josePauloPaes = 'Meu amor é simples, Dora, Como a água e o pão.
  • 6. Como o céu refletido Nas pupilas de um cão.'; Array Mistura de Array e ArrayList<Object>, isto é, muda de tamanho conforme a necessidade e pode armazenar qualquer tipo de objeto. Podemos inicializar com um tamanho inicial por motivos de performance. Acessamos os elementos dela com o operador []. Assim como ganhamos performance ao manter sempre o mesmo tipo para uma variável, ganhamos performance ao armazenar sempre o mesmo tipo de variáveis numa Array. código js - uso de arrays "use strict"; var noticias = []; // array vazia var noticias2 = new Array(10); // array vazia com espaço pré-alocado para 10 elementos. console.log(noticias2[4]); // retorna undefined, pois não atribuímos um valor para este índice console.log(noticias2[20]); // retorna undefined, pois não atribuímos um valor para este índice noticias.push('notícia extremamente curta :P'); // insere um elemento ao final da Array noticias[5] = 'e outra reduzida também'; // insere um elemento no índice 5 noticias.push('ao vivo'); // insere um elemento no índice 6, que é o final da Array RegExp Além do objeto RegExp, JavaScript possui expressões regulares em forma de literal. código js - formatos de expressões regulares "use strict;" var regex = /a+/gi; // expressão na forma literal var regex2 = new RegExp('a+', 'gi'); // expressão na forma de objeto Function Em JavaScript, métodos e funções são objetos! Isso significa que elas possuem propriedades e métodos como o call por exemplo. O número de parâmetros não precisa ser declarado. Esta funcionalidade é utilizada para simular sobrecarga de função, juntamente com o objeto arguments que toda a função possui. Note que embora a chamada de funções desta maneira seja permitida, isso não impede que o compilador lance exceções ao tentar utilizar variáveis que não foram devidamente passadas ao método. código js - chamadas válidas de métodos "use strict"; function comum() { console.log(arguments); // objeto arguments iniciado automaticamente, contendo os parâmetros utilizados na chamada }
  • 7. function normal(texto) { console.log(arguments); // objeto arguments iniciado automaticamente, contendo os parâmetros utilizados na chamada } //chamadas perfeitamente válidas comum(); comum('lala'); normal('lolo'); //chamada válida, e o parâmetro texto terá o valor undefined normal(); Um exemplo de sobrecarga de função. código js - sobrecarga de função "use strict"; function efetuarRequisicaoAjax(endereco) { var url = endereco; if (arguments[1]) { if (typeof arguments[1] == 'number') { // segundo parâmetro passado para a função url += '?pagina=' + arguments[1]; } else if (typeof arguments[1] == 'string') { url += '?token=' + arguments[1]; } } if (arguments[2]) { url += '&pagina=' + arguments[2]; // terceiro parâmetro passado para a função } console.log(url); // vamos ver um exemplo real de ajax lá no final ;) } efetuarRequisicaoAjax('webservicedagrecia.ashx'); efetuarRequisicaoAjax('webservicedagrecia.ashx', 2); efetuarRequisicaoAjax('webserviceseguro.ashx', '987dcb987d6b96a9d5ab'); efetuarRequisicaoAjax('webserviceseguro.ashx', '987dcb987d6b96a9d5ab', 2); Como mencionei lá no começo, funções podem ser selecionadas individualmente para utilizar o modo estrito. Isso é útil nos casos em que precisamos dar manutenção em códigos antigos, ou quando inserimos código dentro de tags <script>, ao invés de em arquivos externos. código js - funções estritas function funcaoNormal() { console.log(varNaoDeclarada); // mostra undefined em navegadores antigos, nos novos lança exceção
  • 8. } function funcaoEstrita() { "use strict"; console.log(varNaoDeclarada); // lança exceção } JavaScript suporta funções anônimas, ou expressões lambda. Expressões lambda são utilizados frequentemente em JavaScript, por isso é importante compreender o que são. Expressões lambda são declarações de funções diretamente no meio do código. Basicamente em qualquer lugar onde podemos passar uma referência de uma função, podemos criar uma expressão lambda. Seguem exemplos em outras linguagens de expressões lambdas (espero que tenha acertado as sintaxes). código C // pegadinha do malandro! C não tem isso. código Objective-C // lambda em Objective-C (OS X v10.6+ e iOS 4+) - chamado de Block [superior usando:^() { // corpo da função }]; código C# // lambda em C# 4 superior(() => { // corpo da função }); código Java // lambda em Java 8 superior(() -> { // corpo da função }); código C++ // lambda em C++11
  • 9. superior([] () { // corpo da função }); código PHP // lambda em PHP - idêntico a JavaScript superior(function() { // corpo da função }); código js - funções de ordem superior "use strict"; // função normal function normal() { // } // função de ordem superior function superior(funcao) { funcao(); // executa a função passada } // função normal sendo passado como parâmetro - note que usamos apenas o nome, sem executar com () superior(normal); // expressão lambda sendo passada como parâmetro superior(function() { // }); PS: existem planos de implementar futuramente em JavaScript uma arrow sintax () => {} para expressões lambda, como outras linguagens já usam. Além de expressões lambda, JavaScript suporta um modo de declaração chamado expressão de função. Mais adiante veremos outro uso para este tipo de declaração. código js - expressões lambda "use strict"; // declaração de função function normal() { // } // expressão de função - tratamos a função como um valor var surpresa = function() { // }
  • 10. // expressão de função var outraSurpresa = normal; normal(); surpresa(); outraSurpresa(); Object Já estivemos utilizando o objeto console, que o navegador nos fornece. Ou seja, estivemos escrevendo JavaScript orientado à objeto desde o começo! Mais um motivo pra não usar alert. código console.log('console é um objeto. log é um método'); JavaScript é bastante diferente de outras linguagens quanto à criação de objetos novos. Vamos nos ater ao básico por enquanto. Declaramos um objeto usando {}. Acessamos as propriedades e métodos usando ponto, como fazemos com console. Por conveniência, também existe a possibilidade de acessá-las usando a notação de Array. Esta notação é útil quando queremos passar o nome do campo dinamicamente. Da maneira abaixo, todas as propriedades e métodos são públicos. código js - declaração de objetos com propriedades e métodos públicos "use strict"; var objetoVazio = {}; var retangulo = { // inicializa o objeto - é quase como um construtor, mas esta sintaxe não necessita de construtores init: function(largura, altura) { this.largura = largura; this.altura = altura; } // define os métodos do objeto area: function() { return this.largura * this.altura; }, // define as propriedades e valores padrão largura: 0, altura: 0, }; retangulo.init(10, 20); console.log(retangulo.largura); // mostra 10 console.log(retangulo.area()); // mostra 200 console.log(retangulo['largura']); // mostra 10 A versão ES5 do JavaScript incrementa a sintaxe para criar objetos, permitindo o uso de propriedades somente leitura, getters e setters. código js - declaração de objetos ES5 com getters e setters
  • 11. "use strict"; // somente para navegadores novos // em realce os descritores de propriedade var retangulo = Object.create(Object.prototype, { // inicializa o objeto - é quase como um construtor, mas esta sintaxe não necessita de construtores init: { value: function(largura, altura) { this.largura = largura; this.altura = altura; }, writable: false }, // define os métodos do objeto area: { value: function() { return this.largura * this.altura; }, writable: false }, // define os getters e setters largura: { get: function() { return this._largura; }, set: function(largura) { this._largura = largura; } }, altura: { get: function() { return this._altura; }, set: function(altura) { this._altura = altura; } }, // define as propriedades e valores padrão - mais adiante veremos como deixá-las privadas _largura: { value: 0, writable: true }, _altura: { value: 0, writable: true
  • 12. }, }); retangulo.init(10, 20); console.log(retangulo.largura); // mostra 10 console.log(retangulo.area()); // mostra 200 console.log(retangulo['largura']); // mostra 10 Object antigo Existe outra sintaxe para criar objetos, utilizando funções e a palavra-chave new. Foi uma tentativa de não causar estranheza aos programadores vindos de outras linguagens, simulando o comportamento que eles já estavam acostumados nas linguagens baseadas em classes. Em contrapartida, isso escondia o verdadeiro funcionamento dos objetos em JavaScript. Douglas Crockford, que é um guru e um dos redatores da linguagem não recomenda a utilização desta sintaxe antiga. Inclusive, funcionalidades novas na criação de objetos não estão sendo implementadas com esta sintaxe. Vou mostrar apenas a título de curiosidade uma das variantes da sintaxe antiga. código js - declaração de objetos com sintaxe antiga, não use! "use strict"; // NÃO COPIE!!! SINTAXE ANTIGA!!!! var objetoVazio = new Object(); // define o objeto base function Retangulo(largura, altura) { // define as propriedades e valores padrão this.largura = largura; this.altura = altura; } // NÃO COPIE!!! SINTAXE ANTIGA!!!! // define os métodos do objeto Retangulo.prototype.area = function() { return this.largura * this.altura; }; // NÃO COPIE!!! SINTAXE ANTIGA!!!! // cria um objeto que herda de Retangulo var novoRetangulo = new Retangulo(10, 20); // acessa os métodos e propriedades normalmente console.log(novoRetangulo.largura); // mostra 10 console.log(novoRetangulo.area()); // mostra 200 console.log(novoRetangulo['largura']); // mostra 10 Outra desvantagem deste método é ter que se lembrar de declarar os métodos no protótipo do objeto, sendo que as propriedades são declaradas diretamente dentro do construtor. A sintaxe mais nova é mais uniforme quanto à isso. Escopo das variáveis
  • 13. Em JavaScript, o único delimitador de escopo é a função. Variáveis fora de funções estão no espaço global, e variáveis declaradas em blocos script ou arquivos diferentes compartilham o mesmo espaço global. Isso foi feito assim porque o programador original da linguagem não tinha tempo de fazer um linker. código js - escopo de variáveis "use strict"; var todosVe = 'o/'; var i = 0; for (i = 0; i < 5; i++) { // } for (var j = 0; j < 7; j++) { // } console.log(i);//4 console.log(j);//6. Surpresa. j foi declarado no espaço global. Este é um dos motivos que declaramos todas as varíaveis no topo do arquivo ou no começo da função. Se quisermos evitar poluir o espaço global, temos que usar uma função anônima (lambda) auto executável. Projetos de aplicativos windows 8 escritos em JavaScript já utilizam por padrão esse procedimento. código js - função anônima auto executável definindo escopo (function(){ var segredo = '******'; })(); console.log(segredo); // undefined em navegadores mais antigos ou lança exceção se modo estrito ou em navegadores novos Pra memorizar isso eu geralmente escrevo assim: Declaro a função. function(){...} Transformo ela em expressão. (function(){...}); Executo ela. (function(){...})(); Note que é possível passar parâmetros para a função anônima auto executável se necessário. É bem comum fazer isso em conjunto com jQuery. Note que esse padrão também permite que nós habilitemos o modo estrito num determinado escopo. código js - função anônima auto executável com parâmetros jQuery.noConflict(); // indicamos que não vamos usar jQuery com $
  • 14. (function($){ "use strict"; // habilita modo estrito só para este escopo // podemos usar $ aqui dentro sem interferir com o espaço global que continua usando jQuery $('body').html('<p>padrão para plug-ins jQuery</p>'); })(jQuery); Outro motivo para se declarar as variáveis no começo do escopo é uma característica do compilador JavaScript, chamada variable hoisting. O que isso significa é que, independente de onde você declarar sua variável, o compilador vai mover a declaração de forma transparente para o topo do escopo e deixá-la com valor undefined. A linha onde um valor é atribuído à variável não é afetada. Isso pode causar problemas se o código depender de uma verificação da existência da variável. código js - variable hoisting "use strict"; // o que vc escreveu console.log(estranha); // mostra undefined e continua normalmente var estranha = 11; console.log(estranha); // mostra 11 Ou seja, o que o compilador vai fazer de modo transparente é: código js - variable hoisting explicado "use strict"; // o que o compilador vê var estranha; // o compilador moveu a declaracão para o topo do arquivo console.log(estranha); estranha = 11; // e manteve a atribução do valor na mesma linha console.log(estranha); Misturando tudo com jQuery O framework jQuery mistura vários dos conceitos apresentados aqui. Ainda não apresentei todos os conceitos para entendermos todas as partes do jQuery, mas podemos identificar os já vistos no código abaixo. código js - uso simples de jQuery "use strict"; jQuery(document).ready(function(evento) { // escopo isolado, não polui o espaço global }); O primeiro dos conceitos utilizados é que jQuery é uma função. Por isso podemos chamá-la utilizando jQuery(). Esta função sempre retorna uma referência para ela mesma, ou seja, sempre retornajQuery. Esta organização de código se chama interface fluida. O método recebe como parâmetro um seletor CSS ou objeto do DOM, neste caso o objeto document. jQuery(document).ready(function(evento) {
  • 15. // escopo isolado, não polui o espaço global }); A seguir temos o método ready. jQuery(document).ready(function(evento) { // escopo isolado, não polui o espaço global }); Mas se jQuery() sempre retorna outra função jQuery, como acessamos o método ready? Lembre-se em JavaScript funções são objetos, e portanto podem ter propriedades e outros métodos. Por último, passamos ao método ready uma função anônima que contém o código que queremos executar. Esta sintaxe reduz a quantidade de variáveis e métodos no espaço global. jQuery(document).ready(function(evento) { // escopo isolado, não polui o espaço global }); DOM - El Poderoso Jefón Se você leu o capítulo anterior (e deveria), pode ter visto que o IE10- lançou um erro em nosso exemplo simples, quando as ferramentas de desenvolvimento não estavam abertas. Isso ocorre porque nestes navegadores, o objeto console, pertencente ao DOM, não é acessível pelo JavaScript enquanto as ferramentas não estiverem abertas. Vou mostrar mais adiante como corrigir esta funcionalidadedo IE10-. Embora por caminhos tortos, isso nos ensina um conceito importante. O conceito de que JavaScript e o DOM são mundos diferentes, e nem sempre JavaScript consegue acessar tudo do DOM a toda a hora. Existem momentos específicos em que isso pode ser feito. Vamos ver isso e outros conceitos com um segundo exemplo mais complexo. seosoquenao.htm <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Google está olhando</title> <meta charset="utf-8" /> <script src="seosoquenao.js"></script> </head> <body> <h1 id="tagTitulo">Acessibilidade e SEO</h1> <p id="tagParagrafo">
  • 16. Sim pequeno gafanhoto, html tem mais de <strong>100 tags</strong> diferentes além de table e div. </p> </body> </html> seosoquenao.js "use strict"; //pega elementos da página pela id e os torna manipuláveis dentro do JavaScript var titulo = document.getElementById('tagTitulo'); var paragrafo = document.getElementById('tagParagrafo'); titulo.innerHTML = 'Black hat SEO'; paragrafo.attributes.add('style', 'display: none'); Ao abrir este arquivo diretamente do seu computador, com o console aberto, talvez você note mais erros. Ao abrir este arquivo através de um servidor, mesmo que local, com certeza você notará mais erros. Porém a programação acima está perfeitamente correta, e o arquivo .htm também. O problema que ocorre neste caso não existe em linguagens C e similares, e se dá quando combinamos os arquivos .htm e .js de uma determinada maneira que provoca uma interação inadequada entre o JavaScript e o DOM (Notou a elegância da frase? Ela é ótima para impressionar seu chefe quando vc tiver que explicar pq aquele site está funcionando perfeitamente no seu pc mas quando vc publicou pipocaram erros em todo o lugar). Lembre-se que JavaScript não possui o conceito de único ponto de entrada (método main). Quando o navegador encontra uma tag <script> na página, ele pára de processar o restante do documento, baixa o arquivo js, roda seu conteúdo, e só depois continua o processamento normalmente. Em nosso exemplo, como a tag <script> está antes do conteúdo, o navegador carrega e roda a programação antes de tomar ciência do resto do documento. Nas primeiras linhas de nosso código, estamos pedindo para o navegador pegar determinados elementos na página, mas ele ainda não sabe que esses elementos existem. Isso talvez não ocorra sempre ao abrir o arquivo local devido diferenças que existem ao ler o arquivo diretamente do hd, porém sempre irá acontecer através de um servidor http. E porque não tivemos esse problema no primeiro exemplo? Porque lá não estávamos acessando nenhum elemento da página. Nunca saímos do mundinho JavaScript para o mundo DOM. Podemos corrigir nosso problema de três maneiras pelo menos. A primeira é mover nossa tag <script> para o final da página, após o conteúdo. A segunda é pedir para o navegador só executar nossa programação após o carregamento da página, na própria tag. A terceira e fazer a mesma coisa, mas dentro do código JavaScript. Essa vou deixar pra mais adiante, pois precisaríamos saber o que são eventos. Vamos ver os 2 primeiros casos e explicar o que acontece. Solução 1 - Movendo a tag script seosoquenaoS1.htm
  • 17. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Google está olhando</title> <meta charset="utf-8" /> </head> <body> <h1 id="tagTitulo">Acessibilidade e SEO</h1> <p id="tagParagrafo"> Sim pequeno gafanhoto, html tem mais de 100 tags diferentes além de table e div. </p> <script src="seosoquenao.js"></script> <!-- Segura a empolgação, a especificação não permite colocar mais nada depois de </body> e </html>. Mas isso nunca é necessário. --> </body> </html> Essa solução talvez seja a mais simples. Quando o navegador rodar o JavaScript, já vai ter lido todos os elementos da página, e pode pegar qualquer um deles sem problema. A desvantagem deste método é que o navegador não poderá otimizar o download dos arquivos que a página irá usar (baixando em paralelo por exemplo), pois ele só vai saber que precisará deles no final do documento. Solução 2 - Postergando a execução na tag seosoquenaoS2.htm <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Google está olhando</title> <meta charset="utf-8" /> <script src="seosoquenao.js" defer></script> </head> <body> <h1 id="tagTitulo">Acessibilidade e SEO</h1> <p id="tagParagrafo"> Sim pequeno gafanhoto, html tem mais de 100 tags diferentes além de table e div. </p> </body> </html> Essa solução é bastante elegante, porém não é suportada nos IEs mais antigos. Além disso, ela só funciona para arquivos .js externos. Não é possível utilizá-la quando colocamos a programação entre as tags <script> e </script>. Através do atributo defer da tag, indicamos nosso desejo de executar a programação dentro dela só após o carregamento da página.
  • 18. Se familiarizando com a Cosa Nostra Utilizamos o método document.getElementById para trazer o elemento da página para dentro do nosso código. Esta solucão não é a mais antiga para fazermos tal coisa, porém é a recomendada pela performance e compatibilidade com os navegadores. Mas e se precisamos pegar elementos que não possuem id? A API disponibiliza outros métodos que retornam valores diferentes dependendo da sua necessidade, sendo que o último deles,document.querySelectorAll foi inspirado após o surgimento do jQuery. cosanostra.htm <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>1001 maneiras de falar com o DOM</title> <meta charset="utf-8" /> </head> <body> <header> <nav> <ul> <li><a href="pag1">link 1</a></li> <li><a href="pag1">link 1</a></li> <li><a href="pag1">link 1</a></li> <li><a href="pag1">link 1</a></li> </ul> </nav> </header> ... <footer> <div>Ir para o <a href="#">topo</a></div> </footer> <script src="cosanostra.js"></script> </body> </html> cosanostra.js "use strict"; var links = document.getElementsByTagName('a'); // retorna os links do menu e o ir para o topo var linksMenu = document.querySelectorAll('ul a'); // retorna os links do menu apenas. ñ funciona em IE8- var linksMenuJ = jQuery('ul a'); // retorna os links do menu apenas. versões do jQuery 1.10.x funcionam em todos os navegadores Notem que cada chamada de método para trazer um elemento do DOM para dentro do JavaScript leva um certo tempo para realizar esta operação. Por motivos de performance, devemos sempre que possível guardar a referência para estes elementos e reutilizá-la, ao invés de chamar continuamente métodos que busquem no DOM. Vejam por exemplo este trecho da própria documentação do jQuery UI (http://jqueryui.com/datepicker/#date-formats): código js - interação pobre com o DOM
  • 19. "use strict"; $(function() { $("#datepicker").datepicker(); $("#format").change(function() { $("#datepicker").datepicker("option", "dateFormat", $(this).val()); }); }); Note que o mesmo elemento foi buscado duas vezes denecessariamente. Se quisermos deixar o código mais performático, podemos fazer a seguinte alteração: código js - interação correta com o DOM "use strict"; $(function() { var meuDatePicker = $("#datepicker"); // salvamos a referência ao elemento na variável meuDatePicker.datepicker(); $("#format").change(function() { meuDatePicker.datepicker("option", "dateFormat", $(this).val()); }); }); Google - O Grande Irmão Acabamos de aprender como alterar html através de JavaScript. Igualmente importante é sabermos quando não devemos fazer isso. Via de regra, Google e outros bots não indexam conteúdo criado ou alterado por javascript. Porém, pode penalizar você se ele achar que você está tentando trapacear, mostrando um conteúdo muito diferente do que foi indexado. Para aplicativos web isso talvez não seja um problema, mas para páginas de internet com conteúdo com certeza é. Aqui vale o bom senso. Podemos fazer um ou outro dropdown, mas se abusarmos deste recursos, escondermos coisas sem um método de mostrá-las novamente, podemos ser punidos pelo Google com posições mais baixas nos resultados, e até mesmo banimento temporário. O fato de bots não rodarem javascript em sua maioria também pode ser usado em nossa vantagem. É possível por exemplo proteger endereços de emails ou evitar envio automático de formulários alterando certas propriedades por JavaScript. semcaptcha.htm <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br"
  • 20. > <head> <title>Google está olhando</title> <meta charset="utf-8" /> </head> <body> <form action="#"> <dl> <dt><label for="nome">nome</label></dt> <dd><input id="nome" type="text" /></dd> </dl> <p><input type="submit" /></p> </form> <script src="semcaptcha.js"></script> </body> </html> semcaptcha.js "use strict"; var formulario = document.getElementsByTagName('form')[0]; formulario.action = 'cadastrar.php'; // não vai ser lido pela maioria dos bots This doesn't makes sense - OOP Já vimos acima como declarar objetos simples, utilizando literais de objeto {}. Estes objetos são dinâmicos, não possuindo uma noção de classe como em outras linguagens. De fato não existem classes em JavaScript. Esta característica, associada ao fato de que JavaScript não é fortemente tipado, permite que objetos tenham propriedades e métodos inseridos ou removidos dinamicamente, em tempo de execução. Mesmo no modo estrito, podemos adicionar e remover propriedades. Note que isso não é muito bom para performance. Assim como acontece com os tipos de variáveis, se evitarmos mudar o objeto após sua criação, o compilador não consegue efetuar otimizações para tipos específicos. Adicionalmente, JavaScript possui o operador delete para remover propriedades, que é lento. Preferencialmente atribuímos o valor null para a propriedade que desejamos remover. A versão ES5 adicionou um método chamado Object.seal, que previne modificação dinâmica do objeto. código js - adicão e remoção dinâmica de propriedades "use strict"; var escravoDeJo = {
  • 21. jogar: function() { // } }; escravoDeJo.jogar = null; // tira a propriedade - use o valor null ao invés de delete escravoDeJo.som = 'zig zig za'; // bota a propriedade console.log(escravoDeJo); // note a mudança das propriedades Object.seal(escravoDeJo); // "corpo fechado" - previne mudanças no objeto // lança exceção escravoDeJo.fazer = function() { // } Para acomodar toda esta flexibilidade, JavaScript possui mais uma diferença, desta vez em relação à palavra-chave this. Se podemos inserir funções dinamicamente nos objetos, o que acontece quando uma destas funções utiliza this? código js - significado de this "use strict"; var modeloCumprimentar = function() { console.log('Olá, menu nome é ' + this.nome); } var diretorSuspense = { nome: 'Alfred'; }; var diretorAlternativo = { nome: 'Tim'; }; diretorSuspense.cumprimentar = modeloCumprimentar; diretorAlternativo.cumprimentar = modeloCumprimentar; diretorSuspense.cumprimentar(); // mostra Olá, menu nome é Alfred diretorAlternativo.cumprimentar(); // mostra Olá, menu nome é Tim Resumindo, this em JavaScript muda de significado de acordo com o contexto. Mais precisamente, this aponta sempre para o objeto que está executando a função atualmente. Isso traz mais problemas do que vantagens geralmente. Vejamos um outro exemplo disso: código js - mudanças indesejadas do contexto de execução, this "use strict"; var frutas = [ { nome: 'banana', peso: 100 }, { nome: 'maçã', peso: 80 }, { nome: 'abacaxi', peso: 3500 } // um abacaxi enorme! ];
  • 22. var ordenador = { ordenarPor: 'nome', ordenar: function(arrayOriginal) { arrayOriginal.sort(this.comparador); // sort muda o contexto de execução }, comparador: function(a, b) { // this aponta para o quê aqui?? // note também o uso da notação [] para acessar campos dinamicamente if (typeof a[this.ordenarPor] === 'number' && typeof b[this.ordenarPor] === 'number' ) { return a[this.ordenarPor] - b[this.ordenarPor]; } if (a[this.ordenarPor] > b[this.ordenarPor]) { return 1; } else if (a[this.ordenarPor] < b[this.ordenarPor]) { return -1; } else if (a[this.ordenarPor] == b[this.ordenarPor]) { return 0; } } }; ordenador.ordenarPor = 'nome'; ordenador.ordenar(frutas); console.log(frutas); ordenador.ordenarPor = 'peso'; ordenador.ordenar(frutas); console.log(frutas); this parece apontar para o objeto ordenador, mas isso não é sempre verdade. Quando passamos o método para arrayOriginal.sort, nosso comparador vai executar num contexto diferente. Assim othis referenciado não é o que esperávamos. Tentamos usar a propriedade ordenarPor que não está mais acessível e isso causa uma exceção. Deixando as coisas mais previsíveis Não estamos desamparados para resolver os problemas de mudança de contexto (amém!). De fato, as funções em JavaScript possuem diversos métodos para controlar essa mudança. Vamos ver a seguir dois deles. Lembrando que em JavaScript funções são objetos, em navegadores recentes as funções possuem um método chamado bind, que retorna uma cópia da função com o this fixo. código js - controlando contexto com bind
  • 23. "use strict"; arrayOriginal.sort(this.comparador.bind(this)); // ES3 Para navegadores mais antigos temos outras duas alternativas. A concentualmente mais simples é utilizar jQuery.proxy. código js - controlando contexto com jQuery.proxy "use strict"; arrayOriginal.sort(jQuery.proxy(this.comparador, this); Note que jQuery.proxy permite passar parâmetros para a função na seguinte forma: código js - passando parâmetros com jQuery.proxy "use strict"; jQuery.proxy(this.comparador, this, var1, var2, var3); Internamente jQuery.proxy utiliza um outro método pertencente às funções, que também altera o valor de this, chamado apply. Na verdade, não é difícil fazermos uma versão simplificada de bind, oujQuery.proxy, evitando ser necessário uma biblioteca inteira apenas para essa funcionalidade. A parte que fala sobre compatibilidade explica isso com mais detalhes. A terceira alternativa é a mais manual e complexa. Possui porém a vantagem de cobrir todos os casos que o bind nativo cobre. Ela utiliza o conceito de closures. Closures são uma parte mais delicada da linguagem, portanto vou apresentá-la antes e depois voltamos à terceira solução. Closures e campos private Objetos em JavaScript se lembram do escopo em que foram criados, e podem acessá-lo se precisarem. Se movermos esse objeto para fora de seu escopo original, ela ainda pode ler variáveis e funções de lá. código js - closures "use strict"; function externa() { var informacao = 'wikileaks'; return function interna() { console.log('vazou ' + informacao); } } var dedoDuro = externa(); dedoDuro(); // expões os segredos dentro do escopo! JavaScript não possui uma maneira formal de criar permissões de acesso com private, protected e public. Mas podemos usar closures para simular esta funcionalidade. Primeiro criamos um contexto com nossa já conhecida função anônima autoexecutável, e vazamos desse contexto a parte que queremos usar como interface de nosso objeto. Este objeto será usado de maneira transparente, sem ninguém precisar
  • 24. saber que sua funcionalidade está fragmentada internamente. O exemplo abaixo mostra o essencial deste procedimento. código js - encapsulamento "use strict"; var objeto = (function() { var publico = { // acessar privado aqui dentro }; var privado = { // se necessário, pode acessar publico }; return publico; // expõe apenas a interface pública })(); Um exemplo mais completo. código js - encapsulamento "use strict"; var matador = (function() { var publico = { matarACobra: function() { return privado.metodoInterno(); }, getPau: function() { return privado.propriedadeInterna; } }; var privado = { metodoInterno: function() { return "método interno"; }, propriedadeInterna: 10 }; return publico; })(); console.log(matador.matarACobra()); console.log(matador.getPau());
  • 25. Revisitando nosso abacaxi A terceira solução para nosso problema, é simplesmente não utilizar a palavra this. Apenas a palavra this muda de significado, se conseguirmos referenciar nosso objeto original por outro nome, podemos acessá-lo sempre que precisarmos sem ter medo de uma crise de identidade. Para fazermos isso usamos uma closure. A primeira forma de utilizá-la, é fazendo-a conter a função completa de comparação. código js - resolvendo mudanças de contexto com closures "use strict"; var frutas = [ { nome: 'banana', peso: 100 }, { nome: 'maçã', peso: 80 }, { nome: 'abacaxi', peso: 3500 } // um abacaxi enorme! ]; var ordenador = { ordenarPor: 'nome', init: function() { this.comparador = this.closureComparador(this); }, ordenar: function(arrayOriginal) { arrayOriginal.sort(this.comparador); }, closureComparador: function (contexto) { return function(a, b) { if (typeof a[contexto.ordenarPor] === 'number' && typeof b[contexto.ordenarPor] === 'number' ) { return a[contexto.ordenarPor] - b[contexto.ordenarPor]; } if (a[contexto.ordenarPor] > b[contexto.ordenarPor]) { return 1; } else if (a[contexto.ordenarPor] < b[contexto.ordenarPor]) { return -1; } else if (a[contexto.ordenarPor] == b[contexto.ordenarPor]) { return 0; } } },
  • 26. comparador: null }; ordenador.init(); ordenador.ordenarPor = 'nome'; ordenador.ordenar(frutas); console.log(frutas); ordenador.ordenarPor = 'peso'; ordenador.ordenar(frutas); console.log(frutas); Note que precisamos ser bastante cuidadosos para sempre escrever contexto ao invés de this. Existe uma segunda forma dessa solução, que utiliza a closure para simplesmente passar o contexto correto ao método executado. código js - resolvendo mudanças de contexto com closures - solução alternativa "use strict"; var frutas = [ { nome: 'banana', peso: 100 }, { nome: 'maçã', peso: 80 }, { nome: 'abacaxi', peso: 3500 } // um abacaxi enorme! ]; var ordenador = { ordenarPor: 'nome', init: function() { this.comparador = this.closureComparador(this); }, ordenar: function(arrayOriginal) { arrayOriginal.sort(this.comparador); }, closureComparador: function (contexto) { return function(a, b) { contexto.comparar(a, b); } }, comparador: null, comparar: function(a, b) { if (typeof a[this.ordenarPor] === 'number' && typeof b[this.ordenarPor] === 'number' ) { return a[this.ordenarPor] - b[this.ordenarPor]; }
  • 27. if (a[this.ordenarPor] > b[this.ordenarPor]) { return 1; } else if (a[this.ordenarPor] < b[this.ordenarPor]) { return -1; } else if (a[this.ordenarPor] == b[this.ordenarPor]) { return 0; } } }; ordenador.init(); ordenador.ordenarPor = 'nome'; ordenador.ordenar(frutas); console.log(frutas); ordenador.ordenarPor = 'peso'; ordenador.ordenar(frutas); console.log(frutas); Desacoplando JavaScriptdas linguagens server-side Se estruturarmos nossa programação de modo que as bibliotecas com os objetos estejam em arquivos externos, podemos apenas chamar os construtores de nossos objetos nas páginas com programação server-side, passando os parâmetros com valores dinâmicos. serverside.php <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Html mínimo aceitável</title> <meta charset="utf-8" /> <script src="dropdown.js"></script> <script> (function() { "use strict"; dropdown.init(<?php // valor calculado server side ?>); })(); </script> </head> <body> </body> </html> dropdown.js "use strict";
  • 28. var dropdown = { init: function(idConteiner) { this.idConteiner = idConteiner; this.conteiner = document.getElementById(this.idConteiner); this.rotulo = this.conteiner.querySelector('button'); this.lista = this.conteiner.querySelector('ul'); this.rotulo.addEventListener('click', this.clickHandler.bind(this)); }, clickHandler: function(evento) { this.visivel = !this.visivel; this.atualizar(); }, atualizar: function() { if (this.visible) { this.lista.style.display = 'block'; return; } this.lista.style.display = 'none'; }, idConteiner: null, conteiner: null rotulo: null, lista: null, visivel: false }; Ou usando closures. dropdown.js "use strict"; var dropdown = (function(){ var publico = { init: function(idConteiner) { privado.idConteiner = idConteiner; privado.conteiner = document.getElementById(privado.idConteiner); privado.rotulo = privado.conteiner.querySelector('button'); privado.lista = privado.conteiner.querySelector('ul');
  • 29. privado.rotulo.addEventListener('click', this.clickHandler.bind(this)); }, clickHandler: function(evento) { privado.visivel = !privado.visivel; privado.atualizar(); } }; var privado = { atualizar: function() { if (this.visible) { this.lista.style.display = 'block'; return; } this.lista.style.display = 'none'; }, idConteiner: null, conteiner: null rotulo: null, lista: null, visivel: false }; return publico; })(); Eventos o que são eventos passar funções anônimas e objetos para eventos
  • 30. Herança Maldita Herança em JavaScript é baseada em protótipos. Um objeto herda diretamente de outro objeto, utilizando o método Object.create. Já vimos isso rapidamente quando definimos um objeto com getters e setters utilizando descritores de propriedades. Vamos ver agora um exemplo com polimorfismo. Se precisarmos acessar o objeto base de dentro do objeto filho, chamamos o métodoObject.getPrototypeOf, que retorna um objeto acima da cadeia de herança. Note que estamos tratando de objetos concretos, e não de definições de objetos como são as classes em outras linguagens. Isso significa que, quando acessarmos o objeto base, estaremos acessandonão só as definições de métodos mas também os valores das propriedades dele. Devemos portanto tomar o cuidado de, ao chamar estes métodos base, indicar que queremos executá-los no contexto do objeto filho. Outro cuidado que devemos tomar, é o de atribuir valores aos objetos somente num método separado, que o inicializa. Se atribuirmos valores diretamente na definição do objeto, e estes valores forem tipos de referência como arrays, eles serão compartilhados pelos subobjetos ao invés de cada um ter sua própria cópia. código js - herança com polimorfismo "use strict"; var objetoPai = { init: function() { this.propriedade = []; // inicializar valores das propriedades só aqui! Muito importante! }, metodo: function() { console.log(this.propriedade); }, propriedade: null }; // herança var objetoFilho = Object.create(objetoPai); // sobrescrita do método objetoFilho.metodo = function() { Object.getPrototypeOf(this).metodo.call(this); } objetoPai.init(3); objetoFilho.init(3); objetoPai.propriedade.push('pai'); objetoFilho.propriedade.push('filho');
  • 31. objetoPai.metodo(); objetoFilho.metodo(); Se não precisarmos de compatibilidade com navegadores antigos, podemos usar descritores de propriedade, que nos dão mais controle sobre a definição do objeto. código js - herança com polimorfismo ES5 "use strict"; var objetoPai = Object.create(Object.prototype, { init: { value: function() { this.propriedade = []; // inicializar valores das propriedades só aqui! Muito importante! }, writable: false }, metodo: { value: function() { console.log(this.propriedade); }, writable: true }, propriedade: { value: null, writable: true } }); // herança var objetoFilho = Object.create(objetoPai, { // sobrescrita do método metodo: { value: function() { Object.getPrototypeOf(this).metodo.call(this); }, writable: false } }); objetoPai.init(3); objetoFilho.init(3); objetoPai.propriedade.push('pai'); objetoFilho.propriedade.push('filho'); objetoPai.metodo(); objetoFilho.metodo();
  • 32. Mostro também um exemplo de herança com a sintaxe antiga. Note que a sintaxe é mais confusa e propensa a erros, pois possui etapas manuais que devem ser lembradas toda a vez. Além disso, existem as questões comentadas anteriormente sobre misturar definições de coisas no construtor e no protótipo, e dar a ilusão que JavaScript possui classes, o que não é verdade. código js - herança com polimorfismo com sintaxe antiga, não use! "use strict"; // NÃO COPIE!!! SINTAXE ANTIGA!!!! function ObjetoPai(valor) { // definir e inicializar valores aqui this.propriedade = []; this.propriedade.push(valor); } ObjetoPai.prototype.metodo = function() { console.log(this.propriedade); }; // NÃO COPIE!!! SINTAXE ANTIGA!!!! // herança function ObjetoFilho() { ObjetoPai.apply(this, Array.prototype.slice.call(arguments)); // "super()" } ObjetoFilho.prototype = new ObjetoPai(); // especificar manualmente qual o protótipo para herdar ObjetoFilho.prototype.constructor = ObjetoFilho; // sobrescrever manualmente o construtor // sobrescrita do método ObjetoFilho.prototype.metodo = function() { ObjetoPai.prototype.metodo.call(this); // nome do objeto base é hardcoded e não muda automaticamente de acordo com a herança }; // NÃO COPIE!!! SINTAXE ANTIGA!!!! var objetoPai = new ObjetoPai('pai'); var objetoFilho = new ObjetoFilho('filho'); objetoPai.metodo(); objetoFilho.metodo(); Vamos ver um exemplo mais completo. código js - polimorfismo, exemplo completo "use strict"; // define o objeto base var ampulheta = {
  • 33. init: function(tempo) { // define os valores padrão das propriedades this.tempoTotal = tempo; this.tempoAtual = this.tempoTotal; }, contar: function() { if (this.tempoAtual > 0) { this.tempoAtual--; } }, girar: function() { this.tempoAtual = this.tempoTotal - this.tempoAtual; }, tempoTotal: null, tempoAtual: null }; // herança var ampulhetaAutomatica = Object.create(ampulheta); // "ampulhetaAutomatica extends ampulheta" // define os métodos do objeto derivado ampulhetaAutomatica.contar = function() { if (this.tempoAtual <= 0) { this.girar(); } else { // chamar o método da maneira abaixo o executaria no contexto do objeto pai, ampulheta // Object.getPrototypeOf(this).contar(); // por isso usando o método call da função para mudar o contexto novamente para ampulhetaAutomática Object.getPrototypeOf(this).contar.call(this); } } ampulheta.init(3); ampulhetaAutomatica.init(3); ampulheta.contar(); // 2 ampulheta.contar(); // 1 ampulheta.contar(); // 0 ampulheta.girar(); // 3 ampulhetaAutomatica.contar(); // 2 ampulhetaAutomatica.contar(); // 1
  • 34. ampulhetaAutomatica.contar(); // 0 ampulhetaAutomatica.contar(); // 3 Mostrar properties código js - polimorfismo, exemplo completo com sintaxe antiga, não use! "use strict"; // NÃO COPIE!!! SINTAXE ANTIGA!!!! // define o objeto base function Ampulheta(tempo) { // define as propriedades e valores padrão this.tempoTotal = tempo; this.tempoAtual = this.tempoTotal; } // NÃO COPIE!!! SINTAXE ANTIGA!!!! // define os métodos do objeto base Ampulheta.prototype.contar = function() { if (this.tempoAtual > 0) { this.tempoAtual--; } }; Ampulheta.prototype.girar = function() { this.tempoAtual = this.tempoTotal - this.tempoAtual; }; // NÃO COPIE!!! SINTAXE ANTIGA!!!! // define o objeto derivado function AmpulhetaAutomatica() { Ampulheta.apply(this, Array.prototype.slice.call(arguments)); // "super()" } // NÃO COPIE!!! SINTAXE ANTIGA!!!! // herança AmpulhetaAutomatica.prototype = new Ampulheta(); // "AmpulhetaAutomatica extends Ampulheta" AmpulhetaAutomatica.prototype.constructor = AmpulhetaAutomatica; // "conserta" o construtor // NÃO COPIE!!! SINTAXE ANTIGA!!!! // define os métodos do objeto derivado AmpulhetaAutomatica.prototype.contar = function() { if (this.tempoAtual <= 0) { this.girar(); } else {
  • 35. // chamar o método da maneira abaixo o executaria no contexto do objeto pai, ampulheta // Ampulheta.prototype.contar(); // por isso usando o método call da função para mudar o contexto novamente para ampulhetaAutomatica Ampulheta.prototype.contar.call(this); } }; // NÃO COPIE!!! SINTAXE ANTIGA!!!! // simulação de sintaxe de outras linguagens var contador = new Ampulheta(3); var contadorAutomatico = new AmpulhetaAutomatica(3); contador.contar(); // 2 contador.contar(); // 1 contador.contar(); // 0 contador.girar(); // 3 contadorAutomatico.contar(); // 2 contadorAutomatico.contar(); // 1 contadorAutomatico.contar(); // 0 contadorAutomatico.contar(); // 3 Ado, a ado, cada um no seu quadrado - Namespaces Como JavaScript escrito sem cuidado pode poluir rapidamente o espaço global, existe uma maneira de evitar a sobrescrita de variáveis. var console = console || {}; // cria um objeto somente se console não existir Guarde esse padrão, porque vamos utilizar esta técnica mais para frente para outros meios também. Vimos anteriormente que utilizando funções anônimas autoexecutáveis isolamos um escopo. Porém, não conseguimos acessá-lo de fora. Para tanto, precisamos simular um namespace. Aproveitamos que JavaScript permite adicionar dinamicamente propriedades em objetos para fazer isso.
  • 36. código js - simulação de namespaces var namespace = namespace || {}; namespace.subnamespace = namespace.subnamespace || {}; namespace.subnamespace.meuObjeto = { metodo: function() { // } }; (function() { // "import" var meuObjeto = namespace.subnamespace.meuObjeto; meuObjeto.metodo(); })(); O primeiro nivel do namescape ocupa o espaço global, que como já vimos, é compartilhado por todos os arquivos e blocos de script da página. Portanto, podemos acrescentar mais coisas posteriormente ao mesmo namespace em um arquivo diferente. código js - simulação de namespaces, outro arquivo var namespace = namespace || {}; namespace.subnamespace = namespace.subnamespace || {}; namespace.subnamespace.meuOutroObjeto = { metodo: function() { // } }; (function() { // "import" var meuObjeto = namespace.subnamespace.meuObjeto; var meuOutroObjeto = namespace.subnamespace.meuOutroObjeto; meuObjeto.metodo(); meuOutroObjeto.metodo(); })(); Conversão de tipos e falsy values JavaScript converte tipos automaticamente em true e false quando necessário. Isso nos proporciona uma sintaxe reduzida para verificar estes valores e a existência de variáveis. Os valores que são convertidos para false são chamados de falsy. são eles "", 0, null, undefined e NaN. Todos os demais são convertidos para true. var objeto = {}; if (objeto) { // } // isso equivale a
  • 37. if (objeto == true) { // } // e ainda a if (objeto !== false && objeto !== "" && objeto !== 0 && objeto !== null && objeto !== undefined && objeto !== NaN) { // } Note que existem dois comparadores de igualdade em JavaScript. == e ===, assim como seus opostos != e !==. A primeira forma converte automaticamente os tipos, enquanto que a outra não. Nota final Podemos verificar nosso código na ferramenta online JSLint, que nos dá divas para melhorá-lo. Apêndice SOLUÇÃO 3 - Postergar execução na programação seosoquenaoS3.htm <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" lang="pt-BR" xml:lang="pt-br" > <head> <title>Google está olhando</title> <meta charset="utf-8" /> <script src="//code.jquery.com/jquery-1.10.2.min.js"></script> <script src="seosoquenaoS3.js"></script> </head> <body> <h1 id="tagTitulo">Acessibilidade e SEO</h1> <p id="tagParagrafo">
  • 38. Sim pequeno gafanhoto, html tem mais de 100 tags diferentes além de table e div. </p> </body> </html> seosoquenaoS3b.js "use strict"; function init() { //pega elementos da página pela id e os torna manipuláveis dentro do JavaScript var titulo = document.getElementById('tagTitulo'); var paragrafo = document.getElementById('tagParagrafo'); titulo.innerHTML = 'Black hat SEO'; paragrafo.attributes.add('style', 'display: none'); } jQuery(document).ready(function(evento) { init(); }); Como citei lá no comecinho, jQuery ganhou fama justamente por esconder as várias incompatibilidades entre os navegadores através de sua API. Este exemplo faz a mesma coisa que o exemplo anterior, porém depende de uma biblioteca externa. Se isso não for um problema pra vc, vai em frente. A sintaxe do jQuery é um pouco diferente do JavaScript convencional, e será abordada mais pra frente. seosoquenaoS3.js "use strict"; function init() { //pega elementos da página pela id e os torna manipuláveis dentro do JavaScript var titulo = document.getElementById('tagTitulo'); var paragrafo = document.getElementById('tagParagrafo'); titulo.innerHTML = 'Black hat SEO'; paragrafo.attributes.add('style', 'display: none'); } //verifica se o JavaScript rodou após o evento de carregamento completado da página if (document.readyState === "interactive" || document.readyState === "complete" || document.readyState === "loaded" ) { init(); //se não, se registra para executar no evento
  • 39. } else { //registro do evento para navegadores legais if (document.addEventListener) { document.addEventListener('DOMContentLoaded', init); //registro de evento para IE8- } else if (window.attachEvent) { window.attachEvent('onload', init); } } Esta solução é a mais compatível. Porém, precisamos adicionar uma verificação meio grande pra tudo funcionar de acordo com o que esperamos. Note que, dependendo do momento em que o JavaScript passa a existir na página, o evento indicando que o processamento do html terminou pode já ter sido disparado. Existe também a questão de compatibilidade com IEs antigos. Claro que podemos refatorar esse código e facilitar o uso deste evento. Mas aqui no ICI fazemos outra coisa. Usamos jQuery.