Títulos descritivos para páginas Web, Success Criterion 2.4.2

W3C-Accessibility-Standards

Quando começamos a aprender HTML uma das primeiras coisas que nos é apresentada é a TAG <title>; o uso é obrigatório e tem como objetivo definir o título do documento HTML. Mas é só isso?

Motivos para definir um título

Como usuários, ficamos acostumados com detalhes simples que pesam na nossa experiência. Durante a navegação por abas, o navegador utiliza o título da página para nos orientar; é o título do documento que aparece abreviado na aba. Em outros casos, como o de um usuário que utilize o screen reader, o título da página é a primeira coisa a ser lida.

Critérios ao definir um título

Existem algumas orientações para nos ajudar a criar bons títulos; seguem:

  • Não repita o título entre as páginas do seu site;
  • O título deve identificar a finalidade da página;
  • O título deve ser curto;
  • O título deve fazer sentido quando lido fora do contexto.

Não é correto repetir um título pois, se o título deve identificar a finalidade de uma página, mudanças no conteúdo implicam em mudanças no título. Um título descritivo trabalha de modo que o usuário não precise ler ou interpretar o conteúdo da página. Um título curto é benéfico para pessoas que possuem deficiências cognitivas, limitações como memorização de short-term, além daquelas com dificuldades em leitura. Um título é lido fora de seu contexto, por exemplo, em um site map, na lista de resultados ou por screen readers; por isso é importante que ele faça sentido quando visto além da própria página.

Módulos + Closures

javascript

No post anterior abordamos um problema comum ao executar funções dentro de laços de repetição. Relacionamos o que aprendemos sobre Closures com a natureza do problema e entendemos a solução proposta. Agora, seguiremos com  a demonstração de Closures ao implementar o padrão Module.

Para aqueles que não estão familiarizados com o conceito de padrões, recomendo a leitura da definição na Wikipedia. Em JavaScript, o uso de padrões permite uma melhor codificação e estruturação do código da aplicação. Por fim, este se torna de fácil entendimento, limpo e testável.

Vamos abordar o exemplo dado no post sobre Closures e adicionar algumas funcionalidades ao nosso contador:


function DoCountClass() {

    var start= 0;

    var counter= start;

    function add() {
        counter += 1;
    }

    function sub() {
       counter -= 1;
    }

    function getCounter() {
       console.log(counter);
    }

    return {
        add: add,
        sub: sub,
        getCounter: getCounter
    };
}

Com duas novas funções, nosso contador ganhou a habilidade de subtrair uma unidade e, além disso,  retornar o valor atual do contador. Apesar do nosso código estar bem diferente do exemplo anterior, nossa Closure ainda está aí. Vamos começar a análise das diferenças pelo modo como nós utilizamos a função add nos dois exemplos. Na primeira abordagem tínhamos uma função cujo o escopo ficou acessível através da variável add; isso tudo quase ao mesmo tempo em as funções e as variáveis eram declaradas, depois era só chamar por add() :

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})(); // self-invoking function

add();
add();
add();

// the counter is now 3
Até aí tínhamos as variáveis protegidas, ou seja, counter não estava acessível por fora de add. Para viabilizar a consulta ao valor de counter seria necessário disponibilizar uma interface pública com tal capacidade. Por meio desta interface seria possível consultar o valor de counter além de somar uma unidade a seu valor. Neste ponto, a primeira versão do contador já não nos atende como também nos limita. Basta observar que a Closure se fecha sobre a execução da rotina de adição; precisamos que esta rotina de adição passe de agente principal do nosso código para um componente que trabalhe em conjunto com outras funções. A fim de atingir tal objetivo, preservando o o acesso restrito à variável counter e o bom funcionamento da nossa Closure, estruturamos as funcionalidades em um módulo:
function DoCountClass() {
//...
}

DoCountClass é uma função simples. Ela não esta contida em uma a notação como (function() { … })() e portanto precisa ser invocada para que a instância da mesma, seu escopo e Closures passem a existir. O segundo ponto importante é o retorno de DoCountClass. A instrução return retorna um objeto literal com referências para as funções internas de DoCountClass.


function DoCountClass() {

// .. declarando variaveis e criando as funções add, sub, getCounter

    return {
        add: add, // ... referência à função interna add
        sub: sub, // ... referência à função interna sub
        getCounter: getCounter // ... referência à função interna getCounter
    };

}

Por fim, o objeto que retornamos possui referências para as funções internas mas não para nossas variáveis internas. As variáveis continuam restritas no escopo de DoCountClass. Desse modo, nosso objeto retornado é uma interface de acesso às funcionalidades de DoCountClass. Nós encerramos o acesso às variáveis internas dentro de add() e sub() no momento em que DoCountClass é instanciado; disponibilizamos o acesso à estas funções por meio de uma interface de modo que, agora, acessamos seus recursos fora do seu escopo léxico. Nossas Closures estão à vista e em exercício.

* O texto acima foi uma síntese do conteúdo do livro You Don’t Know JavaScript.

* Recurso similar utilizado para responder: Increase the value of timer by 1 minute when user clicks on a button

* Código no JSFIDDLE : http://jsfiddle.net/df773p9m/10/

* Um pouco mais sobre padrões: Learning JavaScript Design Patterns

Close over (um problema com variáveis em laços)

javascript

No post anterior explicamos o que é Closure e como se comporta em relação ao escopo. O exemplo dado pelo W3C demonstrou que, com o uso de Closures, é possível manter o escopo de uma função acessível ao executá-la fora do seu escopo léxico.

Agora que possuímos o entendimento teórico, podemos iniciar a abordagem de exemplos práticos. Vamos começar avaliando  o código a seguir; você consegue prever o resultado?

for(var i=0; i< 5; i++) { 
    setTimeout(function timer() {   
        console.log(i); 
    }, i * 1000); 
}

Esperar que o código acima retorne 0,1,2,..,4 é um erro comum relacionado a má interpretação sobre o comportamento do compilador. Adiantando o resultado, o código acima retorna “5” cinco vezes. Alguns podem acabar pensando que isto é um defeito da engine já que, semanticamente, o código parece atender o que se propôs. Na realidade, o problema está no próprio código e não está relacionado em momento algum com a engine.

Analisando o código, fica evidente que cada chamada de função setTimeout acessa a mesma referência da variável i. Além deste ponto, setTimeout é executado após a execução do loop, ou seja, quando o valor de i é “5”.

Para resolver este problema precisamos que nossa chamada setTimeout seja executada de modo que enxergue o valor correto de i. Precisamos que cada callback de nosso timer tenha um escopo definido sobre cada execução do laço. Para isto, utilizaremos um recurso que nos permite executar uma função no momento em que a mesma é declarada. Isto é conhecido como Immediately-invoked function expression (IIFE). IIFE funciona criando um escopo ao declarar a função e logo em seguida executá-la. Vejamos o exemplo:

for(var i=0; i< 5; i++) {
    (function(j){
       setTimeout(function timer() {
            console.log(j);
       }, j*1000);
     })(i);
}

Com o uso da IIFE, conseguimos fechar o escopo de cada callback de timer sobre a execução do nosso laço. O valor de i fica guardado através da referência j, acessível neste escopo criado a cada execução. Desse modo, conseguimos referenciar corretamente os valores desejados para i.

O texto acima foi uma síntese do conteúdo do livro You Don’t Know JavaScript.

Closures

Entender o conceito de Closures é um passo importante para aqueles que estudam JavaScript. Depois de acompanhar e compreender o significado de escopo e como o escopo léxico funciona, o caminho naturalmente segue para as Closures. Caso você nunca tenha tido contato (ou pensa que não teve) com Closures, pode pensar que se trata de uma ferramenta com sintaxe diferenciada; ao progredir com o entendimento vamos desmistificar este conceito e mostrar que Closures podem estar tão arraigadas no JavaScript que acabam por passar despercebidas por aquele que ainda não as conhece.

Antes de apresentar a definição formal de Closures, vamos exercitar alguns problemas. Existem muitos exemplos na internet sobre Closures. Apesar de gostar muito do conteúdo da MDN, vou abordar o exemplo do W3C por ser mais simples e objetivo. Trata-se de problema comum: escrever uma função que soma uma unidade a um contador. A primeira proposta para resolver o problema inclui a declaração de uma variável em escopo global:

var counter = 0; 
function add() { 
    counter += 1; 
} 
add(); 
add(); 
add(); // a variável counter agora guarda 3

O código acima funciona e resolve o problema. O problema é que a variável counter está definida no escopo global, ou seja, pode ser modificada por outras vias além da função add. A fim de resolver o problema podemos pensar, imediatamente, em mover counter para dentro de add, restringindo seu acesso por meio das regras que aprendemos sobre o escopo léxico. Assim temos o seguinte código :

function add() {
    var counter = 0;
    counter += 1;
}

add();
add();
add();

// the counter should now be 3, but it does not work !

Apesar de resolvermos o problema com o escopo global, agora nosso código não funciona como precisamos. Sempre que chamamos por add, counter é definido e inicializado com zero. Isso acontece por que nosso escopo não é persistido ou mantido quando a função add encerra sua execução, ou seja, tudo que pertence a ele é perdido. Portanto, cada chamada é uma nova execução legítima, independente das execuções anteriores. Então, agora precisamos pensar em como criar esta dependência, ou seja, manter o escopo na memória e relacioná-lo com as chamadas seguintes.

Antes de prosseguir, vamos conhecer a definição formal de Closure :

Closure é um recurso capaz de persistir o escopo de uma função, provendo acesso ao seu escopo léxico mesmo quando esta mesma função é executada fora do seu escopo léxico.

Pode parecer um pouco confuso mas nosso exemplo está caminhando para a definição acima. O que precisamos é que o escopo de add não seja perdido, Para isto, podemos declarar uma função dentro de add. Assim reservamos as declarações e inicializações em add e a manipulação neste segundo escopo da hierarquia.

Desse modo, a variável counter seria declarada e inicializada somente uma vez. Vamos ver o exemplo :

function add() {
    var counter = 0;
    function plus() {counter += 1;}
    plus();
    return counter;
}

Funciona, mas só uma vez. Nós não temos acesso a função plus em add. Desse modo, ainda é necessário chamar pela função add se quisermos somar uma unidade em counter. Sendo assim, voltamos ao problema anterior, pois counter se perderá e será novamente declarado e inicializado.

Como acessar plus mesmo depois de add ter sido executado? Para responder esta pergunta vamos analisar a proposta abaixo:

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})(); // self-invoking function

add();
add();
add();

// the counter is now 3

Agora add é uma variável declarada com var e seu valor de inicialização é uma função anônima, a qual é imediatamente executada em virtude da notação (function() { …})(). Essa estratégia permite acessar a função plus de dentro de add; neste caso não temos add como identificador da função onde counter é declarado, mas sim como identificador do que é retornado pela função anônima. A instrução return devolve uma função que guarda consigo o escopo onde foi declarada. Essa função retornada é a nossa antiga plus, agora acessível via add() e fora do seu escopo léxico. Temos assim uma Closure.

O tutorial original é da W3C : JavaScript Closures

Existe um material mais completo na MDN : Closures

A declaração const

Vamos prolongar o assunto do post anterior e abordar a instrução const. Além do let, const é mais uma novidade que vem com ES6. Esta instrução é mais um recurso para abandonarmos os truques que simulam escopo em blocos. Assim como o let, a instrução const declara uma variável, restrita ao escopo o qual foi declarada. A diferença entre let e const é que a instrução  const define uma variável de valor fixo, ou seja, uma constante. Vejamos um exemplo :

if(true) {
   const b = 3;
   b = 4; // erro
}

console.log(b); // ReferenceError

Regras ao utilizar a instrução const :

  1. Ao declarar uma constante, você é obrigado a atribuir-lhe um valor;
  2. Você não pode declarar uma função ou uma variável que tenha a mesma denominação que sua constante em um mesmo escopo;
  3. Você não pode redeclarar uma constante;
  4. Você não pode reatribuir valores a uma constante.

Para mais informações sobre const visite CONST EM MDN.

Exemplos foram adaptados do livro You Don’t Know JavaScript.

A instrução LET

Até agora, vimos alguns conceitos de definição de escopo através de blocos. No post Blocos como escopo, vimos a importância de organizar o código com o propósito de facilitar a manutenção e sua legibilidade. Todos os recursos apresentados até agora são truques que agem sobre o comportamento padrão do Compilador. Até então, somente utilizando tais truques, era possível reproduzir o comportamento de escopo por blocos.

A instrução let é uma novidade do ES6. Esta nova instrução viabiliza a declaração de uma variável no escopo local de um determinado bloco. Vamos abordar o exemplo do post Blocos como escopo e substituir a instrução var por let :

var teste = true;
if (teste) {
let nome = 'Mario';
console.log(nome);
// mais código ...
}

console.log(nome); // Reference error

Com o uso da instrução let, a variável nome passou a pertencer ao escopo do If, ou seja, nome só existe dentro do contexto do if.

Diferenças entre let e var

Estudamos alguns comportamentos do Compilador em relação a instrução var. Pontualmente, estes comportamentos não serão os mesmos em relação ao let. Vamos analisar estes casos:

1 : JavaScript não eleva declarações de variáveis com a instrução let (em ECMAScript 6)

Sabemos que JavaScript eleva as declarações de variáveis com a instrução var. Isso quer dizer que independente de onde você tenha declarado a variável no código (considerando a cadeia de escopo), o Compilador realiza estas declarações antes que as demais instruções do seu programa sejam executadas. Lembrando que o que é elevado é a declaração da variável e não sua inicialização. Veja o exemplo:

function do_something() {
console.log(foo); // ReferenceError
let foo = 2;
}

Portanto, referenciar uma variável antes de sua declaração irá retornar ReferenceError.

2 : Redefinir uma variável dentro de um escopo de bloco retorna TypeError

Quando redefinimos uma variável com a instrução var, o compilador busca no escopo alguma variável com o mesmo identificador. Caso ele a encontre, a redefinição da variável não é realizada. Daí o seguinte exemplo funciona e não retorna erros :

if(true) {

var foo= 123;
var foo; // This works fine.

console.log(foo); // 123
}

Porém, o mesmo não funciona com a instrução let. A redefinição de uma variável com let não é realizada pelo Compilador, retornando um erro. Veja o exemplo:

if(true) {
let foo= 123;
let foo; // <span class="objectBox objectBox-errorMessage hasBreakSwitch "><span class="errorMessage ">TypeError: redeclaration of variable foo</span></span>

console.log(foo);
}

3 : Em um loop, variáveis declaradas com let pertencem somente ao escopo implícito do laço

No post Blocos como escopo, vimos que variáveis declaradas com a instrução var vazam do escopo implícito de um laço. Isso quer dizer que depois da execução do laço, a variável de contagem está registrada no escopo que envolve o laço. Veja o exemplo :

for(var i=0;i&lt;10;i++) {
//faça algo ...
}

alert(i); //retorna o valor 10

A instrução let altera este comportamento de modo que a variável i pertença ao escopo implícito do laço. Considere o exemplo abaixo :

for (let i = 0; i&lt;10; i++) {
console.log(i); // 0, 1, 2, 3, 4 ... 9
}

console.log(i); // i is not defined
Portanto, a instrução let nos deixa mais próximos de criar reais regras de acesso de variáveis em escopos definidos por blocos. Informe-se também sobre este novo recurso nas fontes abaixo:

Escopo e o uso de Eval

Vimos anteriormente que o escopo é definido de acordo com a estrutura proposta pelo programador, respeitando a ordem em que funções e variáveis foram declaradas. Vimos que funções possuem escopo próprio e variáveis contidas em uma função só podem ser acessadas através da própria função. Vimos também que variáveis no escopo global podem ser acessadas de qualquer ponto do código. Apesar do escopo ser definido pela ordem proposta pelo programador, JavaScript dispõe de recursos que permitem driblar esta definição. Um destes recursos é o eval.O método eval() executa qualquer código passado como string, ou seja, qualquer expressão passada como argumento será executada no momento em que eval for executado. Vejamos o exemplo:

function foo(str) {
    eval(str);
    console.log(a, b);
}

var b= 2;
foo("var b = 3;", 1)

A string “var b = 3” é interpretada no momento em que eval é executado. Desse modo, foo passa a conter uma variável b em seu escopo, que passa ser imediatamente a próxima referência à b na hierarquia do escopo, ao invés da declaração global de b. Portanto podemos concluir que houve uma intervenção no processo natural do escopo léxico. O uso de eval é desencorajado pois abre falhas de segurança e contribui para queda de performance.

O texto acima foi uma síntese do conteúdo do livro You Don’t Know JavaScript.

Blocos como escopo : a declaração “with”

Definimos escopo como o conjunto de regras que determinam como e quando as variáveis podem ser manipuladas. Em seguida, entendemos que o escopo léxico dita as regras de acesso de acordo com a ordem em que as variáveis foram dispostas pelo programador.

Em JavaScript, “with” é um recurso da linguagem capaz de driblar o escopo léxico, ou seja, independente da ordem de arranjo das variáveis, “with” tomará uma expressão passada como referência como seu escopo.
É a maneira nativa de estender o escopo de uma instrução, funcionando como um atalho para os acessos recorrentes à uma expressão.

Vamos estender o escopo de um dado objeto: respostas:

function foo(x, respostas) {
with (respostas) {
       x = 2; // Estamos atribuindo um novo valor à variável x dentro de o
}

if( (x + x) === respostas.x) { // 1 + 1 = 2 ?
       console.log('Sabemos somar');
}

console.log('Confira a resposta: ' + (x + x)); // Ooops ... retorna 4; isso pode não ser esperado
}

var o = {
       y : 2
};

f(1,o);

Caso o objeto passado por referência para o with não possua um dos atributos manipulados, o compilador elevará sua declaração para o escopo mais próximo na hierarquia, no nosso caso o escopo de foo. Nenhum aviso será lançado e a variável será alterada no escopo mais próximo. Caso este possua um identificador com a mesma denominação, o Compilador realizará uma atribuição (LHS: lefthand-side) e nosso valor no contexto de foo se perderá.

Podemos entender esta mudança de escopo e como o with dribla o escopo léxico a partir do seguinte esquema:

1 : Refere-se ao escopo global e as únicas referências são foo e o;

2 : Engloba o escopo de foo, o qual contêm a referência para obj;

3: Engloba o escopo do with; que é o próprio objeto obj;

Leia mais sobre este recurso em : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with

Atualmente, o uso de with é desencorajado.

O texto acima foi uma síntese do conteúdo do livro You Don’t Know JavaScript.

Blocos como escopo

No post anterior entendemos um pouco sobre escopo léxico e como o compilador interpreta a declaração das variáveis, de acordo com a ordem definida pelo programador. Com base no que foi apresentado até o momento, vamos introduzir a utilização de blocos como ferramenta para definição de escopo.

Apesar de funções serem a unidade padrão de definição de escopo em JavaScript, existem outras maneiras de definir escopo que podem nos auxiliar a escrever código limpo e de fácil manutenção.

Utilizamos blocos como escopo a fim de declarar variáveis dentro de um contexto, o mais próximo possível, de sua utilização. Considere o exemplo abaixo:

var teste = true;
if (teste) {
  var nome = 'Mario';
  nome = digaOla(nome);
  console.log(nome);
  // mais código ...
}

Estamos usando a variável “nome” somente no contexto do IF, logo declaramos ela dentro do IF. Porém não faz sentido utilizar var para este propósito, pois o compilador elevará sua declaração para o escopo mais próximo na hierarquia.

Ao definir um bloco como escopo, estamos estendendo o conceito “Principle of Least Privilege“, pois somente este é capaz de acessar o conjunto de recursos (variáveis e funções) necessários para seu propósito original.

Originalmente, JavaSript não oferece recursos para implementação de blocos como escopo. Considere o exemplo abaixo :

for(var i=0;i<10;i++) {
  //faça algo ...
}

alert(i); //retorna o valor 10

Apesar de i estar declarado no contexto do for, este é elevado para o escopo mais próximo da hierarquia e pode ser acessado em qualquer outra parte do código: a última linha retorna uma janela de alerta com 10. Este tipo de prática, nas mãos de programadores pouco cuidadosos, propicia a implementação de código de difícil manutenção.

Para ampliar a utilização de blocos como escopo em JavaScript, será necessário ir mais à fundo na linguagem. Os próximos posts estão reservados para estas técnicas e nuances.

O texto acima foi uma síntese do conteúdo do livro You Don’t Know JavaScript.

Escopo Léxico

No Post anterior definimos o que era escopo. Além disso, entendemos também o que era escopo de função. Agora podemos avançar e compreender o significado e propósito do Escopo Léxico.

Escopo léxico

Escopo Léxico é o modelo de definição de escopo empregado pelo JavaScript. Este escopo é definido na etapa de análise léxica, desempenhada pelo Compilador antes da execução do código. Portando, ele respeita a ordem em que os blocos e as variáveis foram escritas pelo programador.

Vamos analisar o exemplo abaixo:

function sucessor(a) {

 var b = a + 1;

 function imprimir(c) {
  alert('Buscando sucessores de ' + a);
  alert('Sucessor de ' + a + ' : ' + b);
  alert('Sucessor de ' + b + ' : ' + c);
 }

 imprimir(b + 1);
}

sucessor(1);

O exemplo acima demonstra como JavaScript se comporta em casos de bloco de escopo aninhados; a função “sucessor” está definida no escopo global. Em seguida, agora no escopo de “sucessor”, temos os identificadores, a, b e imprimir. Por último, no escopo de “imprimir”, temos acesso à a, b e a inclusão do identificador c.

Quando trabalhamos com escopo léxico, não há como definir um mesmo escopo em diferentes partes do código. A definição ocorre na análise léxica e segue o aninhamento definido pelo programador.

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Junte-se a 64 outros seguidores