JavaScript is Sexy – dica rápida

Uma dica bem rápida. Hoje (não sei exatamente como), fui parar nesse site JavaScript is Sexy. E achei o site excelente, com vários “roadmaps” para aprender assuntos diferentes mas relacionados ao JavaScript.

Por exemplo, tem um bom roadmap para aprender Node.js com confiança ou então, um outro para aprender JavaScript avançado.

Bom, fica ai a dica rápida.

Loops for in no JavaScript

O JavaScript tem o suporte nativo a loops do tipo for in e, ele funciona de uma forma muito semelhante ao foreach do C# (por exemplo), mas temos que tomar alguns cuidados, por que como tudo no JavaScript, sempre temos uma pegadinha.

Primeiro um exemplo. Se eu quiser iterar por todas as propriedades é métodos de um objeto o código é exatamente o que imaginamos.

var propriedade;
var pessoa = {
    nome: 'Thiago',
    sobrenome: 'Temple',
    dizOla: function() {
        alert('Ola');
    }
};
for(propriedade in pessoa){
    document.writeln(propriedade);
}
//exibe: nome sobrenome dizOla

Esse código produz um retorno com: nome sobrenome dizOla

Se eu quiser exibir somente os atributos e não os métodos, eu preciso adicionar um filtro no loop, dessa forma:

for(propriedade in pessoa){
    if (typeof pessoa[propriedade] !== 'function') {
        document.writeln(propriedade);
    };
}
//exibe: nome sobrenome

Até aqui bem simples.

O primeiro ponto a considerar é que nunca podemos garantir a ordem do retorno de um loop for in. Geralmente isso não importaria muito, como no exemplo anterior, mas no caso de um array, que é também um objeto, isso pode ser importante. Nesse momento é melhor usar mesmo um loop for do que um loop for in.

A segundo ponto a considerar é que o JavaScript tem herança através da propriedade prototype, e que todo objeto herda de Object. Isso é importante porque o JavaScript é uma linguagem dinâmica e, portanto, se alterarmos Object isso vai refletir nos nossos objetos, não importa o momento que alteramos Object. Por exemplo:

var propriedade;
var pessoa = {
    nome: 'Thiago',
    sobrenome: 'Temple',
    dizOla: function() {
        alert('Ola');
    }
};

Object.prototype.naoRelacionado = 'eu nao devia estar aqui';

for(propriedade in pessoa){
    document.writeln(propriedade);
}

//exibe: nome sobrenome dizOla naoRelacionado

No código acima eu defini um objeto chamado pessoa e depois disso eu adicionei um atributo chamado naoRelacionado à Object. Mesmo assim esse atributo foi exibido dentro loop for in. Isso pode vir a ser um problema quando alteramos o prototype de Object em outra parte do código que não esta relacionado ao código que estamos fazendo.

No caso do loop for in, podemos resolver isso com o método hasOwnProperty.

for(propriedade in pessoa){
    if(pessoa.hasOwnProperty(propriedade)) {
        document.writeln(propriedade);
    }
}

//exibe: nome sobrenome dizOla

O método hasOwnProperty verifica se a propriedade foi definida no objeto em questão.

É isso ai.

Construtores de Arrays do JavaScript

Bom, o ano começou, lá se foi o carnaval e agora sim podemos dizer que o ano começou. Pelo menos aqui no blog. Alegre

Bora falar um pouco mais de JavaScript e o que existe de estranho com os construtores de Arrays do JavaScript.

Construtores de Arrays do JavaScript

No JavaScript podemos construir um Array da seguinte forma:

var a = new Array("texto");
console.log(a.length); // 1
console.log(a[0]); // texto

Quando passamos uma string ou outro tipo de objeto para o construtor de uma Array, o JavaScript cria uma array com um item e atribui o valor passado para esse item.

Agora vamos dar uma olhada em outro exemplo:

var a = new Array(3);
console.log(a.length) // 3
console.log(a[0]); // undefined

Quando passamos um número inteiro para o construtor ele cria um array com o número de elementos informados e, todos os elementos criados não tem um valor atribuído a eles, por isso o undefined.

Um terceiro exemplo:

var a = new Array(3.2); // RangeError: invalid array length

Quando passamos um número decimal para o construtor, ele obviamente não cria um array decimal, mas também não cria um array com um só elemento e com o valor passado, o JavaScript retorna um erro para esse comando.

Uma prática um pouco melhor

A melhor forma de criar arrays no JavaScript é usando o construtor implícito dele, por exemplo:

var a = ["texto"]; // Cria um elemento e atribui o valor a ele

var b = [1, 2, 3]; // Cria um array com 3 elementos e com os valores 1, 2 e 3

Um problema simples de resolver esse, não é?

Tratamento de exceções em JavaScript

Sim, existe tratamento de exceções em JavaScript. Não é uma funcionalidade muito conhecida e, por isso talvez, não muito utilizada. Mas existem os blocos try/catch dentro do JavaScript e o comando throw. E funciona de uma forma bem similiar ao C#.

var comErro = function() {
    throw {
        name: 'CustomError',
        message: 'Um erro muito grave para ser tratado'
    };
};

try {
    comErro();
}
catch(e) {
    console.log(e.name + ' - ' + e.message); //CustomError - Um erro muito grave para ser tratado
}

Apesar do JavaScript não ser tipado, existe uma convenção que diz que todo objeto de erro deve conter ao menos duas propriedades: name e message. Acho que os nomes das propriedades são auto-explicatórios.

Obviamente, nada nos impede de adicionar outras propriedades com mais detalhes sobre o erro.

Tipos de erro do JavaScript

Por padrão o JavaScript tem internamente seis tipos diferentes de erro que ele pode gerar.

EvalError Usado quando a função eval() é usada de uma maneira incorreta
RangeError Usado quando uma variável numérica excede uma faixa de valores permitida
ReferenceError Usado quando é feita uma referência invalida à um objeto
SyntaxError Usado quando existe um erro de sintaxe
TypeError Usado quando o tipo da variável não é o mesmo que o esperado
URIError Usado quando as funções encodeURI() e decodeURI() são usadas de uma maneira incorreta.

Isso serve apenas como referência, como mostrei no exemplo anterior, podemos criar os nossos próprios tipos de erro sem problemas.

Construtores de objetos de erro

Por fim, o JavaScript também tem alguns construtores para os objetos que podemos usar para retornar um erro, por exemplo, eu poderia reescrever a exemplo acima da seguinte forma:

var comErro = function() {
    var error = new Error('Um erro muito grave para ser tratado');
    error.name = 'CustomError';
    throw error;
};

try {
    comErro();
}
catch(e) {
    console.log(e.name + ' - ' + e.message); //CustomError - Um erro muito grave para ser tratado
}

Esse código gera exatamente o mesmo resultado que antes, o único detalhe que quero ressaltar é que se eu não tivesse feito:

error.name = 'CustomError';

o nome do erro seria Error.

Como o objeto Error, temos também um construtor para cada tipo de erro do JavaScript: EvalError, TypeError, ReferenceError, etc.

Funções de callback no JavaScript

O que são funções de callback?

Uma função no JavaScript é um objeto, isso quer dizer que a gente pode passar uma função como parâmetro para outra função. Quando passamos uma função como parâmetro para outra, a função passada como parâmetro é uma função de callback.

Por exemplo:

var pessoa = function(nome, dizerOla) {
    dizerOla(nome);
};
var olaNoConsole = function(nome) {
    console.log(nome); // 
}
pessoa('Thiago', olaNoConsole);

No código acima, eu defini uma função chamada pessoa e outra olaNoConsole. A função pessoa recebe uma string com um nome e uma função de callback que ela vai usar para fazer uma saudação. Repare que, quando passo a função olaNoConsole como parâmetro, eu não utilizo parêntesis. Os parêntesis executam a função, então nesse momento que eu não quero executar a função, eu não os utilizo.
A função de callback pode ser também uma função anônima como no exemplo abaixo:

var pessoa = function(nome, dizerOla) {
    dizerOla(nome);
};
pessoa('Thiago', function(nome) {
    alert('Ola ' + nome);
});

A mesma função pessoa agora recebendo uma função anônima que faz um alert como saudação.

Por quê?

Por que fazer uso de funções de callback? Acho que o exemplo acima deixa claro que a principal vantagem é o reuso sem causar acoplamento. No caso a função pessoa nunca sabe como é feita a saudação, tudo o que ela sabe é que tem que chamar a função de callback passando um parâmetro. Foi possível trocar a saudação de console para alert sem alterar a função pessoa.

Um exemplo um pouco mais útil é quando fazemos loops de elementos, por exemplo, vamos supor que eu queira consultar todos os meus campos texto na página, fazer alguma coisa com cada um deles e no fim retornar a lista de campos.

var listaCampos = function() {
    var campos = document.getElementsByTagName("input");
    var totalItens = campos.length, i;
    for (i = 0; i < totalItens; i++) { 
        // algo bem complexo com cada campo
    }
    return campos;
};

Depois disso eu decido que eu quero que cada campo tenha uma borda vermelha, porque eu quero destacar esses campos. Eu poderia pegar o retorno dessa função, fazer um for em cada item de novo e adicionar a borda.

var campos = listaCampos();
var totalItens = campos.length, i;
for (i = 0; i < totalItens; i++) { 
    campos[i].setAttribute("style", "border: 1px solid red");
}

O problema com essa solução é que eu fiz dois loops, um quando peguei os campos e um quando quis deixá-los com a borda vermelha. É possível ser mais simples (e mais rápido) do que isso, eu poderia alterar dentro do for original da função listaCampos e adicionar a linha para deixar os campos com a borda vermelha.

var listaCampos = function() {
    var campos = document.getElementsByTagName("input");
    var totalItens = campos.length, i;
    for (i = 0; i < totalItens; i++) { 
        // algo bem complexo com cada campo
        campos[i].setAttribute("style", "border: 1px solid red");
    }
    return campos;
};

O problema dessa segunda opção é que se eu quiser usar essa função listaCampos em outra parte no meu código, eu sempre vou ter os campos com a borda vermelha. E nem sempre eu quero vermelho, pode ser que em outro ponto eu queria azul! Aí entra o callback.

var campoVermelho = function(campo) {
    campo.setAttribute("style", "border: 1px solid red");
};

var listaCampos = function(callback) {
    var campos = document.getElementsByTagName("input");
    var totalItens = campos.length, i;

    if (typeof callback !== 'function') {
        callback = false;
    }

    for (i = 0; i < totalItens; i++) { 
        // algo bem complexo com cada campo
        if(callback) {
            callback(campos[i]);
        }
    }
    return campos;
};

listaCampos(campoVermelho);

No código acima, a primeira coisa que eu fiz foi criar a função de callback que espera um campo como parâmetro e faz com que a borda desse campo fique vermelha. Depois, na função listaCampos eu alterei a função para que ela receba um parâmetro, que vai ser uma função de callback.

Em seguida eu testo para ver se uma função foi realmente passada (já que no JavaScript os parâmetros não são obrigatórios). Dentro do loop, se a função de callback foi passada como parâmetro, eu a executo.

Nesse caso agora se eu quisesse que os campos tivessem a borda azul era só criar uma outra função que fizesse isso e passar como parâmetro para a função listaCampos. A função listaCampos continua sendo genérica e pode ser utilizada por outras. Melhor que isso, é uma função que faz uma coisa só e não está com a funcionalidade acoplada a ela.

Funções de callback e escopo

Dito isso, agora vem a parte um pouco mais complicada que é a parte do escopo com as funções de callback. Imagine que ao invés de passar uma função, eu tenho um objeto e eu passo um método desse objeto como callback para a função listaCampos.

var listaCampos = function(callback) {
    var campos = documente.getElementsByTagName("input");
    var totalItens = campos.length, i;

    if (typeof callback !== 'function') {
        callback = false;
    }

    for (i = 0; i < totalItens; i++) {
        // algo bem complexo com cada campo
        if(callback) {
            callback(campos[i]);
        }
    }

    return campos;
};

var customCampo = {
    'cor': 'red',
    'defineCor': function(campo){
        campo.setAttribute('style', 'border: 1px solid ' + this.cor);
    }
};

listaCampos(customCampo.defineCor);

No exemplo acima, o objeto customCampo tem um método defineCor que eu passo como callback para a função listaCampos. O problema é que o método defineCor usa this para fazer acesso ao valor da propriedade cor. Mas, a função listaCampos faz parte do objeto global e portanto nesse momento this vai fazer acesso ao objeto global também.

A solução para esse caso é sempre que passar uma função de callback, passar também o objeto que será o contexto da função de callback.

listaCampos(customCampo.defineCor, customCampo);

E aí eu preciso modificar também a função listaCampos

var listaCampos = function(callback, ctx) {
    var campos = documente.getElementsByTagName("input");
    var totalItens = campos.length, i;

    if (typeof callback !== 'function') {
        callback = false;
    }

    for (i = 0; i < totalItens; i++) {
        // algo bem complexo com cada campo
        if(typeof callback === 'function') {
            callback.call(ctx, campos[i]);
        }
    }

    return campos;
};

Isso resolve o problema, mas quando chamamos a função listaCampos temos que informar duas vezes o objeto customCampo:

listaCampos(customCampo.defineCor, customCampo);

Existe uma forma um pouco mais elegante para isso, a gente pode chamar a função listaCampos dessa forma:

listaCampos('defineCor', customCampo);

E aí alteramos a função listaCampos para:

var listaCampos = function(callback, ctx) {
    var campos = documente.getElementsByTagName("input");
    var totalItens = campos.length, i;

    if (typeof callback === 'string') {
        callback = ctx[callback];
    }

    if (typeof callback !== 'function') {
        callback = false;
    }

    for (i = 0; i < totalItens; i++) {
        // algo bem complexo com cada campo

        if(typeof callback === 'function') {
            callback.call(ctx, campos[i]);
        }
    }

    return campos;
};

var customCampo = {
    'cor': 'red',
    'defineCor': function(campo){
        campo.setAttribute('style', 'border: 1px solid ' + this.cor);
    }
};

Um pouco mais elegante.

Por hoje é só, espero que tenha sido útil!

Seletores jQuery

Seletores jQuery

Seletores jQuery Para selecionar elementos do DOM o jQuery faz uso dos seletores do CSS e também possui alguns seletores próprios. Nesse video eu mostro alguns exemplos desses seletores, de forma individual e combinando tanto seletores CSS como seletores jQuery. …Continue lendo…

Escopo no JavaScript: esse this não é daqui

Existem duas questões importantes com relação ao escopo no JavaScript.  Declaração de variáveis e o valor de this.

A primeira parte é fácil, eu já falei um pouco disso quando falei de declaração de variáveis. Toda variável declarada com a palavra chave var tem o escopo da função em que foi declarada. Portanto, na função abaixo:

var func1 = function() { 
    var i = 100; 
    for(var i = 0; i < 10; i++) { 
        // qualquer coisa aqui 
    } 
    console.log(i); // 10 
}

A variável i vai ser 10 no fim do método, porque dentro de um método, não importa onde a variável é declarada, ela sempre faz parte do escopo da função. E claro, não pode ser acessada fora da função.

Agora o this é um pouco mais complicado. O this sempre faz referência ao objeto que a função faz parte. Por exemplo:

var pessoa = { 
    'nome': 'Thiago', 
    'getNome': function(){ 
        console.log('Olá, ' + this.nome); 
    } 
}; 
pessoa.getNome(); // Olá, Thiago

No exemplo acima, eu declaro um objeto e atribuo à variável pessoa. E getNome é definido com uma função, portanto essa função é um método desse objeto. Dessa forma o contexto de this dentro desse método é o objeto em que ele foi definido e chamada de getNome funciona como o esperado.

Já no exemplo abaixo:

var pessoa = {  
    'nome': 'Thiago',  
    'getNome': function(){  
        var fx = function() { 
            console.log('Olá, ' + this.nome); 
        }; 
        fx(); 
    }  
};  
pessoa.getNome(); // Olá, undefined

Dentro do método, fx é apenas uma variável que recebe uma função, não é um método que faz parte de um objeto, por isso, this nesse caso faz referência ao escopo global, que no caso do browser, quer dizer o objeto window.

Trocando o escopo de uma função

A última coisa que eu quero dizer sobre o escopo e this é que no JavaScript nós podemos alterar a referência this. Veja o exemplo abaixo:

var dizOla = function() { 
    console.log('Ola, ' + this.nome); 
}; 

var pessoa = { 
    'nome': 'Thiago Temple' 
} 

dizOla.call(pessoa);

A função dizOla nao faz parte de nenhum objeto, portanto this dentro daquela função faz referencia ao objeto window. Mas, quando executo a função usando o método call, eu passo o objeto pessoa como parâmetro. Nesse momento estou dizendo para a função dizOla usar o objeto pessoa como contexto.

Alterar o escopo no JavaScript é uma característica muito importante e muito utilizada, porque é uma forma bem simples e eficaz de fazer reuso de funções.

É isso aí por hoje. Até a próxima!

Introdução ao jQuery | VideoCast

Introdução ao jQuery

Introdução ao jQuery O jQuery é hoje um dos frameworks feitos em JavaScript mais populares que existe, se não o mais popular. Simples e intuitivo, ele é de fácil compreensão e essa é também uma razão para a sua alta …Continue lendo…

JavaScript e o problema com o ponto e vírgula

Dessa vez eu quero falar sobre uma questão bem simples, o problema do ponto e vírgula e das chaves.

O JavaScript tenta nos ajudar cada vez que esquecemos ou simplesmente deixamos de colocar um ponto e vírgula no final de uma linha.

Assim, o código abaixo funciona perfeitamente:

var nome  = "Thiago"
nome = "Thiago Temple"
console.log(nome)

Dito isso, um outro ponto relevante para esse caso é que o JavaScript também suporta duas formas diferentes de usar as chaves {}. Os dois exemplos abaixo funcionam da mesma maneira.

if(true) {
}

if(true)
{
}

Agora, vamos dizer que eu tenha uma função que retorna um objeto, como as duas funções abaixo:

function getNome1() {
  return {
    nome: "Thiago"
  };
}
console.log(getNome1().nome); // Thiago

function getNome2() {
    return
    {
        nome: "Thiago"
    };
}
console.log(getNome2().nome); // syntax error

No exemplo acima a função getNome1 funciona normalmente, mas no segundo caso, a função getNome2 retorna um erro. Isso acontece porque o JavaScript entende que, depois da palavra chave return, está faltando um ponto e vírgula e automaticamente insere um. Obviamente, tudo que está depois desse ponto e vírgula passa a não ter nenhum sentido e vira um erro de sintaxe.

Solução

É bem simples nesse caso, use sempre a abertura de chave na mesma linha da instrução, como na função getNome1 e o problema estará resolvido.

function getNome1() {
  return {
    nome: "Thiago"
  };
}
console.log(getNome1().nome); // Thiago

E também sempre coloque o ponto e vírgula ao final das sentenças, nesse caso é melhor ser explícito do que confiar no JavaScript.

Elevação em JavaScript

Elevação em JavaScript é o ato de mover a declaração de variáveis para o topo da função. O que? Sim, é isso mesmo. Não importa onde essa variável foi declarada dentro da função, para o JavaScript é como se ela tivesse sido declarada na primeira linha.

Veja o exemplo abaixo:

var func1 = function () {
     console.log(nome); // undefined
     var nome = "Thiago";
     console.log(nome); // Thiago
}
func1();

A variável nome foi declarada na segunda linha do método e já teve um valor atribuído a ela. Mas no caso do exemplo, quando eu executo o console.log antes da declaração o resultado é undefined e não um erro. Já o console.log depois da declaração funciona conforme esperado.

Primeiro ponto importante, undefined não quer dizer que a variável não foi declarada, quer dizer que ela  foi declarada mas que nenhum valor foi atribuído a ela. O JavaScript, portanto, eleva a declaração da variável para o topo da função, mas não eleva a atribuição inicial. O código acima é equivalente a esse:

var func1 = function () {
    var nome;
    console.log(nome); // undefined
    nome = "Thiago";
    console.log(nome); // Thiago
}
func1();

Eu sei, o nome undefined é um pouco confuso, mas nesse caso, o equivalente de var nome; em C# seria string nome = null; Se eu fizesse isso em C#, eu teria acesso a variável nome, ela foi declarada, só que o valor dela é null.

A diferença fica clara com esse segundo exemplo:

var func2 = function () {
    console.log(nome); // undefined
    console.log(sobrenome); // ReferenceError: b is not defined
    var nome = "Thiago";
    console.log(nome); // Thiago
}
func2();

Nesse caso, a variável sobrenome nunca é declarada, por isso quando eu tenho acessá-la recebo um erro. Percebeu a diferença?

Cuidado com loops for

var func3 = function() {
    var x;
    x = 100;
    for(var x = 0; x < 10; x ++) {
    }
    console.log(x); // 10
}
func3();

No JavaScript, loops for não tem seu próprio escopo como em C#, o escopo é sempre o da função, por causa disso, não importa que no exemplo acima eu fiz uma declaração dentro do loop for, a variável é a mesma declarada no inicio da função, porque todas as declarações var são elevadas para a primeira linha da função.

Elevação com funções

Em JavaScript eu posso atribuir uma função a uma variável, com eu fiz nos 3 exemplos anteriores: func1, func2 e func3 são três variáveis com funções atribuídas a elas. Isso se chama function expression. E function expressions estão sujeitas as mesmas regras que qualquer outra variável, porque, no JavaScript, uma variável que contenha uma função atribuída a ela é igual a uma função que tenha uma string como valor.

var func4 = function() {
    console.log(nomeCompleto("Thiago", "Temple")); // TypeError: undefined is not a function
    var nomeCompleto = function(nome, sobrenome) {
        return nome + " " + sobrenome;
    }
}
func4();

No exemplo acima eu vou receber um erro TypeError, porque a função nomeCompleto foi declarada, e portanto ela é elevada para a primeira linha, mas como o valor dela foi atribuído depois, nomeCompleto é igual a undefined. Veja que é diferente desse caso:

var func4 = function() {
    console.log(nomeCompleto("Thiago", "Temple")); // ReferenceError: nomeCompleto is not defined
    var nomeCompleto = function(nome, sobrenome) {
        return nome + " " + sobrenome;
    }
}
func4();

Novamente, no caso acima a nomeCompleto nunca foi declarada por isso o erro é ReferenceError. O correto obviamente seria:

var func4 = function() {
    var nomeCompleto = function(nome, sobrenome) {
        return nome + " " + sobrenome;
    }
    console.log(nomeCompleto("Thiago", "Temple")); // "Thiago Temple"
}
func4();

Uma outra opção no JavaScript é declarar a função, ao invés de fazer um function expression.

var func5 = function() {
    console.log(nomeCompleto("Thiago", "Temple")); // "Thiago Temple"
    function nomeCompleto(nome, sobrenome) {
        return nome + " " + sobrenome;
    }
}
func5();

O exemplo acima mostra uma declaração de função, e nesse caso, mesmo que eu chame essa função antes de declará-la, funciona. Funciona porque toda declaração de função também é elevada para o todo do método. E como no caso da declaração de função não existe uma váriavel, toda a implementação é elevada.

Conclusão

Como todas as variáveis declaradas em método são sempre elevadas para o topo do método, não existe a necessidade de declarar variáveis em outros locais do método. Por isso, sempre declare todas as variáveis na primeira linha do método.
var nome, sobrenome, teste, i, etc.;

Dessa forma, sempre que for necessária uma nova variável, vai existir só uma linha com todas as variáveis no começo do método. E claro, vai ficar mais fácil de saber se a variável já foi declarada, ou se existe algum conflito.