Artigo original: The Best JavaScript Examples

O JavaScript é a linguagem de script mais amplamente utilizada na Terra. Aqui estão alguns exemplos de padrões de sintaxe chave em JavaScript.

Exemplo de argumento

O objeto arguments é um objeto semelhante a um array (em que a estrutura do objeto é semelhante à de um array, mas que não deve ser considerado um array, pois tem toda a funcionalidade de um objeto) que armazena todos os argumentos que você passou para uma função e é proprietário dessa função em particular.

Se você passasse 3 argumentos para uma função, digamos storeNames(), esses 3 argumentos seriam armazenados dentro de um objeto chamado arguments e pareceria assim quando passássemos os argumentos storeNames("Mulder", "Scully", "Alex Krycek") para a nossa função:

  • Primeiro, declaramos uma função e fazemos com que ela retorne o objeto arguments
  • Então, quando executarmos essa função com n argumentos (neste caso, 3), ela retornará o objeto para nós e ele parecerá um array. Podemos convertê-lo em um array, mas falaremos mais sobre isso depois…
function storeNames() { return arguments; }
// Se executarmos a seguinte linha no console:
storeNames("Mulder", "Scully", "Alex Kryceck");
// A saída será { '0': 'Mulder', '1': 'Scully', '2': 'Alex Kryceck' }

Trate-o como se fosse um array

Você pode invocar argumentos usando  arguments[n] (onde n é o índice do argumento no objeto do tipo array. Se, no entanto, você quiser usá-lo como um array para fins de iteração ou aplicar a ele métodos de array, você precisará convertê-lo em um array, declarando uma variável e usando o método Array.prototype.slice.call (porque arguments não é um array):

var args = Array.prototype.slice.call(arguments);

// ou no modelo do es6:
var args = Array.from(arguments)

Uma vez que slice() tem dois parâmetros (o parâmetro fim é opcional), você pode pegar uma certa parte dos argumentos especificando o início e o fim de sua parte (usar o método slice.call() renderiza estes dois parâmetros opcionais, não apenas o fim). Confira o código abaixo:

function getGrades() {
    var args = Array.prototype.slice.call(arguments, 1, 3);
    return args;
}

// Vamos mostrar isso no console!
console.log(getGrades(90, 100, 75, 40, 89, 95));

// A SAÍDA DEVE SER: //
// [100, 75] <- Por quê? Porque começou a partir do índice 1 e parou no 
// índice 3. Assim, o índice 3 (40) não foi levado em consideração.
// Se removermos o parâmetro "3", deixando apenas (argumentos, 1), obteremos
// cada argumento a partir do índice 1: [100, 75, 40, 89, 95].

Problemas de otimização com Array.slice()

Há um pequeno problema: não é recomendável usar slice no objeto arguments (por motivos de otimização)...

Importante: não se deve usar slice em argumentos, pois isso impede as otimizações em máquinas virtuais do JavaScript (a V8, por exemplo). Ao invés disso, tente criar um outro array, iterando através do objeto arguments.

Então, qual outro método está disponível para converter arguments em um array? Eu recomendo o laço for (não o laço for-in). Você pode fazer isso assim:

var args = []; // Array vazio, no início.
for (var i = 0; i < arguments.length; i++) {
    args.push(arguments[i])
} // Agora, 'args' é um array que mantém seus argumentos.

Para mais informações sobre os problemas de otimização: Optimization Killers: Managing Arguments (texto em inglês)

Parâmetro rest, do ES6, como um modo de contornar o objeto arguments

No ES2015/ES6, é possível utilizar o parâmetro rest (...) em vez do objeto arguments na maioria das situações. Digamos que temos a seguinte função (não ES6):

function getIntoAnArgument() {
    var args = arguments.slice();
    args.forEach(function(arg) {
        console.log(arg);
    });
}

Essa função pode ser substituída no ES6 por:

function getIntoAnArgument(...args) {
    args.forEach(arg => console.log(arg));
}

Observe que também usamos uma arrow function para encurtar a callback de forEach!

O objeto arguments não está disponível dentro do corpo de uma arrow function.

O parâmetro rest deve vir sempre como o último argumento na definição de sua função.

function getIntoAnArgument(arg1, arg2, arg3, ...restOfArgs /*no more arguments allowed here*/) { //function body }

Exemplo de operação aritmética

Os operadores são para adição, subtração, multiplicação, divisão e resto, respectivamente.

Adição

Sintaxe

a + b

Uso

2 + 3          // retorna 5
true + 2       // interpreta verdadeiro como 1 e retorna 3
false + 5      // interpreta falso como 0 e retorna 5
true + "bar"   // concatena o valor booleano e retorna "truebar"
5 + "foo"      // concatena a string e o número e retorna "5foo"
"foo" + "bar"  // concatena as strings e retorna "foobar"

Dica: há um operador prático de incremento, que é um ótimo atalho quando se adiciona 1 aos números.

Subtração

Sintaxe

a - b

Uso

2 - 3      // retorna -1
3 - 2      // retorna 1
false - 5  // interpreta falso como 0 e retorna -5
true + 3   // interpreta verdadeiro como 1 e retorna 4
5 + "foo"  // retorna NaN (Não é um Número)

Dica: há um prático operador de decremento que é um grande atalho quando você se subtrai 1 dos números.

Multiplicação

Sintaxe

a * b

Uso

2 * 3                // retorna 6
3 * -2               // retorna -6
false * 5            // interpreta falso como 0 e retorna 0
true * 3             // interpreta verdadeiro como 1 e retorna 3
5 * "foo"            // retorna NaN (Não é um Número)
Infinity * 0          // retorna NaN
Infinity * Infinity    // retorna Infinity

Dica: ao fazer cálculos, é possível usar parênteses para priorizar quais números devem ser multiplicados juntos.

Divisão

Sintaxe

a / b

Uso

3 / 2                // retorna 1.5
3.0 / 2/0            // retorna 1.5
3 / 0                // retorna Infinito
3.0 / 0.0            // retorna Infinito
-3 / 0               // retorna -Infinito
false / 5            // interpreta falso como 0 e retorna 0
true / 2             // interpreta verdadeiro como 1 and retorna 0.5
5 + "foo"            // retorna NaN (Não é um Número)
Infinity / Infinity    // retorna NaN

Resto

Sintaxe

a % b

Uso

3 % 2          // retorna 1
true % 5       // interpreta verdadeiro como 1 e retorna 1
false % 4      // interpreta falso como 0 e retorna 0
3 % "bar"      // retorna NaN

Incremento

Sintaxe

a++ or ++a

Uso

// Sufixo 
x = 3; // declare uma variável 
y = x++; // y = 4, x = 3 

// Prefixo 
var a = 2; 
b = ++a; // a = 3, b = 3

Decremento

Sintaxe

a-- or --a

Uso

// Sufixo 
x = 3; // declare uma variável 
y = x—; // y = 3, x = 3 

// Prefixo 
var a = 2; 
b = —a; // a = 1, b = 1

Importante: como você pode ver, você não pode realizar nenhum tipo de operação com Infinity.

Exemplo de arrow function

As arrow functions (em português, funções de seta) são uma nova sintaxe ES6 para escrever expressões a partir de funções do JavaScript. A sintaxe mais curta economiza tempo, além de simplificar o escopo da função.

O que são as arrow functions?

Uma expressão de arrow function é uma sintaxe mais concisa para escrever expressões de função usando um símbolo de "seta grande" (=>).

A sintaxe básica

Abaixo, temos um exemplo básico de arrow function:

// Sintaxe ES5
var multiply = function(x, y) {
  return x * y;
};

// Arrow function do ES6 
var multiply = (x, y) => { return x * y; };

// Ou ainda mais simples
var multiply = (x, y) => x * y;  

Você não precisa mais das palavras-chave function e return, nem das chaves.

Um this simplificado

Antes das arrow functions, as funções novas definiam seu próprio valor de this. Para usar this, dentro de uma expressão de função tradicional, temos que escrever uma solução alternativa assim:

// Sintaxe ES5 
function Person() {
  // Designamos 'this' para 'self' para que possamos usá-lo mais tarde
  var self = this;
  self.age = 0;

  setInterval(function growUp() {
    // `self` refere-se ao objeto esperado
    self.age++;
  }, 1000);
}

Uma arrow function não define seu próprio valor de this. Ela herda o this da função de contexto de execução onde está inserido:‌

// Sintaxe ES6
function Person(){
  this.age = 0;

  setInterval(() => {
    // `this` agora se refere ao objeto Person - brilhante!
    this.age++;
  }, 1000);
}

var p = new Person();

Operadores de atribuição

Exemplo de operador de atribuição

Os operadores de atribuição, como o nome sugere, atribuem (ou reatribuem) valores a uma variável. Enquanto existam algumas variações sobre os operadores de atribuição, todos eles se baseiam no operador de atribuição básico.

Sintaxe x = y;

Descrição: a variável (x) é necessária. Também é necessário o operador de atribuição (=), bem como o valor (y) a ser atribuído à variável.

Exemplos

initialVar = 5;   // A inicialização da variável requer o uso de um operador de atribuição

let newVar = 5;
newVar = 6;   // Os valores das variáveis podem ser modificados usando um operador de atribuição

Variações

Os outros operadores de atribuição são uma forma abreviada de realizar alguma operação utilizando a variável (indicada por x acima) e o valor (indicado por y acima) e, então, atribuir o resultado à própria variável.

Por exemplo, abaixo temos a sintaxe para o operador da tarefa de adição:

x += y;

Isso é o mesmo que aplicar o operador de adição e reatribuir a soma à variável original (ou seja, x), que pode ser expressa pelo seguinte código:

x = x + y;

Para ilustrar isso utilizando valores reais, aqui está outro exemplo de utilização do operador de atribuição de adição:

let myVar = 5;   // Valor de myVar: 5
myVar += 7;      // Valor de myVar: 12 = 5 + 7

Lista completa dos operadores de atribuição em JavaScript

Operador Sintaxe Long version
Atribuição x = y x = y
Atribuição de adição x += y x = x + y
Atribuição de subtração x -= y x = x - y
Atribuição de multiplicação x *= y x = x * y
Atribuição de divisão x /= y x = x / y
Atribuição de resto x %= y x = x % y
Atribuição de exponenciação x **= y x = x ** y
Atribuição com passagem de dígito à esquerda x <<= y x = x << y
Atribuição com passagem de dígito à direita x >>= y x = x >> y
Atribuição com passagem de dígito à direita sem sinal x >>>= y x = x >>> y
Atribuição de bitwise AND x &= y x = x & y
Atribuição de bitwise XOR x ^= y x = x ^ y
Atribuição de bitwise OR x |= y x = x | y

Exemplo de booleano

Os booleanos são um tipo de dados primitivo comumente usado em linguagens de programação de computadores. Por definição, um booleano tem dois valores possíveis: true ou false.

No JavaScript, muitas vezes, há coerção de tipo implícito para booleano. Se, por exemplo, você tiver uma declaração com if que verifique uma determinada expressão, essa expressão será convertida em um booleano:

var a = 'a string';
if (a) {
  console.log(a); // mostra 'a string'
}

Há apenas alguns valores que serão convertidos para false:

  • false (não realmente convertido, pois já é falso)
  • null
  • undefined
  • NaN (Não é um número)
  • 0 (o número zero)
  • " " (uma string vazia)

Todos os outros valores serão convertidos para verdadeiro. Quando um valor é convertido a um booleano, também chamamos isso de valores "falsy" ou "truthy" (tendendo ao falso ou tendendo ao verdadeiro, respectivamente).

Uma forma de converter é com o uso dos operadores ou (||) e (&&) :

var a = 'palavra';
var b = false;
var c = true;
var d = 0
var e = 1
var f = 2
var g = null

console.log(a || b); // 'palavra'
console.log(c || a); // verdadeiro
console.log(b || a); // 'palavra'
console.log(e || f); // 1
console.log(f || e); // 2
console.log(d || g); // nulo
console.log(g || d); // 0
console.log(a && c); // verdadeiro
console.log(c && a); // 'palavra'

Como você pode ver, o operador or verifica o primeiro operando. Se isso for verdadeiro ou verdade, ele o retorna imediatamente (e é por isso que recebemos 'palavra' no primeiro caso e verdadeiro no segundo caso). Se não for verdadeiro ou verdade, ele retorna o segundo operando (e é por isso que obtemos 'palavra' no terceiro caso).

Com o operador and funciona de forma similar, mas para que 'and' seja verdade, ambos os operandos precisam ser verdades. Assim, ele sempre retornará o segundo operando se ambos forem verdadeiros/verdade. Caso contrário, retornará false. É por isso que, no quarto caso, temos true e, no último caso, temos 'palavra'.

O objeto boolean

Há também um objeto Boolean nativo JavaScript, que envolve um valor e converte o primeiro parâmetro em um valor booleano. Se um valor for omitido ou falso –0, -0, null, false, NaN, undefined ou uma string vazia ("") – o valor do objeto é falso. Passe todos os outros valores, incluindo a string "false", o valor do objeto é definido como verdadeiro.

Observe que os valores primitivos booleanos (true e false) são diferentes daqueles do objeto boolean.

Mais detalhes

Lembre-se de que qualquer objeto cujo valor não seja undefined ou null é avaliado como verdadeiro se usado em uma declaração condicional. Por exemplo, mesmo que esse objeto Boolean seja explicitamente definido como falso, ele é avaliado como verdadeiro e o código é executado:

var greeting = new Boolean(false);
if (greeting) {
  console.log("Olá mundo");
}

// Olá mundo

Isso não se aplica aos primitivos booleanos:

var greeting = false;
if (greeting) {
  console.log("Olá mundo"); // O código não será executado.
}

Para converter um valor não booleano em booleano, use Boolean como uma função e não como um objeto:

var x = Boolean(expression);     // Uso preferencial como uma função
var x = new Boolean(expression); // Não o faça desse modo

Funções de callback

Esta seção dá uma breve introdução ao conceito e uso das funções de callback em JavaScript.

Funções são objetos

A primeira coisa que precisamos saber é que, em JavaScript, as funções são objetos de primeira classe. Como tal, podemos trabalhar com elas do mesmo modo como trabalhamos com outros objetos, atribuindo-as a variáveis e passando-as como argumentos para outras funções. Isso é importante, porque esta última técnica nos permite estender a funcionalidade em nossas aplicações.

Exemplo de função de callback

Uma função de callback é uma função que é passada como um argumento para outra função, para ser "chamada de volta" (em inglês, callback) em um momento posterior.

Uma função que aceita outras funções como argumentos é chamada de função de ordem superior, e contém a lógica para quando a função de callback é executada. É a combinação dessas duas funções que nos permite estender nossa funcionalidade.

Para ilustrar as callbacks, vamos começar com um exemplo simples:

function createQuote(quote, callback){ 
  var myQuote = "Como eu sempre digo, " + quote;
  callback(myQuote); // 2
}

function logQuote(quote){
  console.log(quote);
}

createQuote("coma seus vegetais!", logQuote); // 1

// Resultado no console: 
// Como eu sempre digo, coma seus vegetais!

No exemplo acima, createQuote é a função de ordem superior que aceita dois argumentos, sendo o segundo a callback. A função logQuote está sendo usada para passar como nossa função de callback. Quando executamos a função createQuote(1), observe que não estamos anexando parênteses ao logQuote quando o passamos como um argumento. Isso porque não queremos executar nossa função de callback imediatamente, queremos simplesmente passar a definição da função junto com a função de ordem superior para que ela possa ser executada mais tarde.

Além disso, precisamos garantir que, se a função de callback que passamos espera argumentos, fornecemos esses argumentos ao executar a callback (2). No exemplo acima, isso seria a declaração callback(myQuote);, já que sabemos que logQuote espera que uma citação (quote, em inglês) seja passada.

Além disso, podemos passar funções anônimas como callbacks. A chamada abaixo para createQuote teria o mesmo resultado que o exemplo acima:

createQuote("coma seus vegetais!", function(quote){ 
  console.log(quote); 
});

A propósito, você não precisa usar a palavra "callback" como o nome de seu argumento. O JavaScript só precisa saber que é o nome correto do argumento. Com base no exemplo acima, a função abaixo se comportará exatamente da mesma forma.

function createQuote(quote, functionToCall) { 
  var myQuote = "Como eu sempre digo, " + quote;
  functionToCall(myQuote);
}

Por que usar callbacks?

Na maioria das vezes, estamos criando programas e aplicações que operam de forma síncrona. Em outras palavras, algumas de nossas operações são iniciadas somente após as anteriores terem sido concluídas.

Frequentemente, quando solicitamos dados de outras fontes, como uma API externa, não sabemos quando nossos dados serão servidos de volta. Nesses casos, queremos esperar pela resposta, mas nem sempre queremos que toda a nossa aplicação seja interrompida enquanto nossos dados são buscados. Essas situações são onde as funções de callback são úteis.

Vamos dar uma olhada em um exemplo que simula uma solicitação a um servidor:

function serverRequest(query, callback){
  setTimeout(function(){
    var response = query + "cheio!";
    callback(response);
  },5000);
}

function getResults(results){
  console.log("Resposta do servidor: " + results);
}

serverRequest("O copo está meio ", getResults);

// Resultado em console após 5 segundos de atraso:
// Resposta do servidor: O copo está meio cheio!

No exemplo acima, fazemos um pedido falso a um servidor. Após 5 segundos, a resposta é modificada e então a nossa função de callback getResults é executada. Para ver isso em ação, você pode copiar/colar o código acima na ferramenta de desenvolvimento do seu navegador e executá-lo.

Além disso, se você já está familiarizado com o setTimeout, então você tem usado funções de callback durante todo esse tempo. O argumento da função anônima passado para a chamada de função setTimeout do exemplo acima também é uma callback! Portanto, a função de callback original do exemplo é realmente executada por outra callback. Tome cuidado para não fazer muitas callbacks aninhadas se possível, pois isso pode levar a algo chamado de "inferno de callback" (em inglês, callback hell)! Como o nome implica, não é uma alegria lidar com isso.

Exemplo de classes em JavaScript

O JavaScript não tem o conceito de classes inerentemente.

Poderíamos, no entanto, simular as funcionalidades de uma classe aproveitando a natureza prototípica do JavaScript.

Essa seção assume que você tem uma compreensão básica de protótipos.

Por uma questão de clareza, vamos supor que queremos criar uma classe que possa fazer o seguinte:

var p = new Person('James','Bond'); // cria uma nova instância da classe Pessoa
	p.log() // Saída: 'Eu sou o James Bond' // Acesso a uma função da classe
	// Usando setters and getters 
	p.profession = 'espião'
	p.profession // Saída: James bond é um espião

Usando a palavra-chave class

Como em qualquer outra linguagem de programação, agora você pode usar a palavra-chave class para criar uma classe.

Isso não é suportado em navegadores mais antigos e foi introduzido no ECMAScript 2015.

class é apenas uma adição à sintaxe sobre o modelo de herança com base no protótipo existente do JavaScript.

Em geral, os programadores utilizam os métodos abaixo para criar uma classe em JavaScript.

Usando métodos adicionados aos protótipos:

Aqui, todos os métodos são adicionados ao protótipo‌:

function Person(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
}

Person.prototype.log = function() {
    console.log('I am', this._firstName, this._lastName);
}

// Essa linha acrescenta getters e setters para o objeto da profissão. Observe que, em geral, você poderia simplesmente escrever suas próprias funções get e set como o método 'log' acima..
// Como neste exemplo estamos tentando imitar a classe acima, tentamos usar a propriedade getters e setters fornecidas pelo JavaScript
Object.defineProperty(Person.prototype, 'profession', {
    set: function(val) {
        this._profession = val;
    },
    get: function() {
        console.log(this._firstName, this._lastName, 'is a', this._profession);
    }
})

Você também poderia escrever métodos de protótipo sobre a função Person, como abaixo:

Person.prototype = {
    log: function() {
        console.log('I am ', this._firstName, this._lastName);
    }
    set profession(val) {
        this._profession = val;
    }

    get profession() {
        console.log(this._firstName, this._lastName, 'é um(a)', this._profession);
    }

}

Usando métodos adicionados internamente

Aqui os métodos são adicionados internamente ao invés de protótipo:

function Person(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;

    this.log = function() {
        console.log('Eu sou ', this._firstName, this._lastName);
    }

    Object.defineProperty(this, 'profissão', {
        set: function(val) {
            this._profession = val;
        },
        get: function() {
            console.log(this._firstName, this._lastName, 'é um(a)', this._profession);
        }
    })
}

Ocultar detalhes em classes com símbolos

Na maioria das vezes, algumas propriedades e métodos têm que ser ocultados para prevenir o acesso de fora da função.

Com as classes, para obter essa funcionalidade, uma forma de fazer isso é usando símbolos. O símbolo é um novo tipo de JavaScript incorporado, que pode ser invocado para dar um novo valor ao símbolo. Cada símbolo é único e pode ser usado como uma chave no objeto.

Portanto, um caso de uso de símbolos é que você pode adicionar algo a um objeto que você não tenha, e talvez não queira colidir com quaisquer outras chaves de objeto. Portanto, criar uma nova e adicioná-la como uma propriedade a esse objeto usando o símbolo é o mais seguro. Além disso, quando o valor do símbolo é adicionado a um objeto, ninguém mais saberá como obtê-lo.

class Person {
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    log() {
        console.log('Eu sou', this._firstName, this._lastName);
    }

    // setters
    set profession(val) {
        this._profession = val;
    }
    // getters
    get profession() {
        console.log(this._firstName, this._lastName, 'é um(a)', this._profession);
    }
// Com o código acima, mesmo que possamos acessar as propriedades fora da função para mudar os seus conteúdos, e se não quisermos isso.
// É quando os símbolos são mais úteis.
let s_firstname  = new Symbol();

class Person {
    constructor(firstName, lastName) {
        this[s_firstName] = firstName;
        this._lastName = lastName;
    }

    log() {
        console.log('Eu sou', this._firstName, this._lastName);
    }

    // setters
    set profession(val) {
        this._profession = val;
    }
    // getters
    get profession() {
        console.log(this[s_firstName], this._lastName, 'é um(a)', this._profession);
    }

Exemplo de closure no JavaScript

Uma closure é a combinação de uma função e o ambiente léxico (escopo) com a qual essa função foi declarada. As closures são uma propriedade fundamental e poderosa do Javascript. Essa seção discute o "como" e o "porquê" das closures:

Exemplo

// Temos uma função externa chamada walk e an função interna chamada fly
function walk (){
  
  var dist = '1780 pés';
  
  function fly(){
    console.log('A '+dist);
  }
  
  return fly;
}

var flyFunc = walk(); //Chamando walk retorna o fly function que está sendo atribuída a flyFunc
//Você esperaria que uma vez que a função walk acima é executado
//Você pensaria que o JavaScript se livrou da var 'dist'.

flyFunc(); // Mostra no console 'A 1780 pés'
// Mas você ainda pode usar a função como acima 
// Esse é o poder das closures

Outro exemplo

function by(propName) {
    return function(a, b) {
        return a[propName] - b[propName];
    }
}

const person1 = {name: 'joe', height: 72};
const person2 = {name: 'rob', height: 70};
const person3 = {name: 'nicholas', height: 66};

const arr_ = [person1, person2, person3];

const arr_sorted = arr_.sort(by('height')); // [ { name: 'nicholas', height: 66 }, { name: 'rob', height: 70 },{ name: 'joe', height: 72 } ]

A closure 'lembra' o ambiente em que foi criada. Esse ambiente consiste em quaisquer variáveis locais que estavam dentro do escopo no momento em que a closure foi criada.

function outside(num) {
  var rememberedVar = num; // Nesse exemplo, rememberedVar é o ambiente lexical que a closure 'remembers'
  return function inside() { // Essa é a função da qual a closure 'se lembra'
    console.log(rememberedVar)
  }
}

var remember1 = outside(7); // remember1 é agora uma closure que contém rememberedVar = 7 em seu ambiente lexical, bem como a função 'inside'
var remember2 = outside(9); // remember2 é agora uma closure que contém rememberedVar = 9 em seu ambiente lexical, bem como a função 'inside'

remember1(); // Isso agora executa a função 'inside', resultando em um console.log(rememberedVar) => 7
remember2(); // Isso agora executa a função 'inside', resultando em um console.log(rememberedVar) => 9

As closures são úteis porque permitem "lembrar" os dados e, em seguida, permitem que você opere com esses dados através de funções retornadas. Isso permite que o Javascript emule métodos privados que são encontrados em outras linguagens de programação. Os métodos privados são úteis para restringir o acesso ao código, bem como para gerenciar seu namespace global.

Variáveis privadas e métodos

As closures também podem ser usadas para encapsular dados privados/métodos. Dê uma olhada neste exemplo:

const bankAccount = (initialBalance) => {
  const balance = initialBalance;

  return {
    getBalance: function() {
      return balance;
    },
    deposit: function(amount) {
      balance += amount;
      return balance;
    },
  };
};

const account = bankAccount(100);

account.getBalance(); // 100
account.deposit(10); // 110

Neste exemplo, não poderemos acessar o balance (em português, o saldo) de qualquer lugar fora da função bankAccount, o que significa que acabamos de criar uma variável privada.

Onde está a closure? Bem, pense no que a bankAccount()está retornando. Ele realmente retorna um Objeto com diversas funções dentro dele. Além disso, quando chamamos account.getBalance(), a função é capaz de "lembrar" sua referência inicial ao balance.

Esse é o poder da closure, onde uma função "lembra" seu escopo léxico (tempo de compilação do escopo), mesmo quando a função é executada fora desse escopo léxico.

Emulando variáveis em escopo de bloco

O Javascript não possuía um conceito de variáveis em escopo de bloco. Isso significa que, ao definir uma variável dentro de um laço for, por exemplo, esta variável também era visível de fora do laço for. Então, como as closures podem nos ajudar a resolver esse problema? Vamos dar uma olhada.

    var funcs = [];
    
    for(var i = 0; i < 3; i++){
        funcs[i] = function(){
            console.log('Meu valor é ' + i);  //Criando três funções diferentes com diferentes valores paramétricos.
        }
    }
    
    for(var j = 0; j < 3; j++){
        funcs[j]();             // Meu valor é 3
                                // Meu valor é 3
                                // Meu valor é 3
    }

Como a variável i não tem escopo de bloco, o seu valor dentro de todas as três funções foi atualizado com o contador do laço e criou valores inesperados. As closures podem nos ajudar a resolver esse problema criando uma snapshot do ambiente em que a função estava quando ela foi criada, preservando seu estado.

    var funcs = [];
    
    var createFunction = function(val){
	    return function() {console.log("Meu valor: " + val);};
    }

    for (var i = 0; i < 3; i++) {
        funcs[i] = createFunction(i);
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();                 // Meu valor é 0
                                    // Meu valor é 1
                                    // Meu valor é 2
    }

As versões posteriores do Javascript (ES6+) têm uma nova palavra-chave chamada let, que pode ser usada para dar à variável um escopo de bloco. Há também muitas funções (como a forEach) e bibliotecas inteiras (como a lodash.js) que são dedicadas a resolver problemas como os explicados acima. Elas certamente podem aumentar sua produtividade, porém continua sendo extremamente importante ter conhecimento de todas estas questões quando se tenta criar algo grande.

As closures têm muitas aplicações especiais que são úteis na criação de programas maiores em Javascript.

  1. Emulação de variáveis privadas ou encapsulamento
  2. Chamadas assíncronas ao servidor
  3. Criação de uma variável em escopo de bloco.

Emulando variáveis privadas

Ao contrário de muitas outras linguagens, o Javascript não tem um mecanismo que permite criar variáveis de instância encapsuladas dentro de um objeto. Ter variáveis de instância públicas pode causar muitos problemas ao construir programas de médio a grande porte. Entretanto, com as closures, esse problema pode ser mitigado.

Como no exemplo anterior, você pode construir funções que retornam os objetos literais com métodos que têm acesso às variáveis locais do objeto sem expô-los. Assim, você as torna efetivamente privadas.

As closures também podem ajudá-lo a gerenciar seu namespace global para evitar colisões com dados compartilhados globalmente. Geralmente, todas as variáveis globais são compartilhadas entre todos os scripts em seu projeto, o que definitivamente lhe dará muitos problemas ao construir programas de médio a grande porte.

É por isso que os autores de bibliotecas e módulos usam closures para esconder os métodos e dados de um módulo inteiro. Isso é chamado de padrão de módulo, ele usa uma expressão de função imediatamente invocada que exporta apenas certas funcionalidades para o mundo exterior, reduzindo significativamente a quantidade de referências globais.

Aqui está uma pequena amostra do esqueleto de um módulo.

var myModule = (function() = {
    let privateVariable = 'Eu sou uma variável privada';
    
    let method1 = function(){ console.log('Eu sou o método 1'); };
    let method2 = function(){ console.log('Eu sou o método 2, ', privateVariable); };
    
    return {
        method1: method1,
        method2: method2
    }
}());

myModule.method1(); // Eu sou o método 1
myModule.method2(); // Eu sou o método 2, Eu sou uma variável privada

As closures são úteis para capturar novas instâncias de variáveis privadas contidas no ambiente 'lembrado'. Essas variáveis só podem ser acessadas através da função retornada ou métodos.

Exemplo de comentário em JavaScript

Os programadores usam comentários para adicionar dicas, notas, sugestões ou avisos ao seu código-fonte; eles não têm efeito sobre a saída real do código. Os comentários podem ser muito úteis para explicar a intenção do que seu código está ou deveria estar fazendo.

A melhor prática é sempre quando se começa a comentar com mais frequência do que não comentar, pois pode ajudar quem lê seu código a entender exatamente o que seu código pretende fazer.

O JavaScript tem dois modos de atribuir comentários em seu código.

A primeira forma é o comentário com //; todo o texto após o //, na mesma linha, é um comentário. Por exemplo:

function hello() {
  // Esse é um comentário JavaScript de uma linha
  console.log("Olá mundo!");
}
hello();

A segunda forma é o comentário /* */ , que pode ser usado tanto para comentários de uma linha como para comentários de várias linhas. Por exemplo:

function hello() {
  /* Esse é um comentário JavaScript de uma linha */
  console.log("Olá mundo!");
}
hello();

function hello() {
  /* Esse comentário se estende por múltiplas linhas. Observe
     que não precisamos terminar o comentário até estarmos prontos. */
  console.log("Olá mundo!");
}
hello();

Você também pode prevenir a execução do código Javascript apenas comentando as linhas de código, assim:

function hello() {
  /*console.log("Olá mundo!");*/
}
hello();

Mais informações

Como escrever comentários em JavaScript (texto em inglês)

Muitos IDEs vêm com um atalho de teclado para comentar linhas de saída.

  1. Realce o texto a ser comentado
  2. Mac: pressione a tecla Command (tecla da Apple) e "/"
  3. Windows: pressione Control e "/"
  4. Você também pode remover código de comentários fazendo as mesmas etapas

Um atalho para comentar uma seção do Javascript em muitos editores de código é realçar as linhas de código que você deseja comentar e depois pressionar Cmd/Ctrl + /.

Os comentários também são muito úteis para testes de código, pois você pode prevenir que uma certa linha/bloco de código funcione:

function hello() {
  // A declaração abaixo não vai ser executada
  // console.log('oi')
  }
hello();
function hello() {
  // As declarações abaixo não vão ser executadas
  /*
  console.log('oi');
  console.log('código-teste');
  */
}
hello();

Exemplo de operador de comparação em JavaScript

O JavaScript tem as comparações estrita e de conversão de tipo.

  • A comparação estrita (===) só avalia se ambos os operandos são do mesmo tipo.
  • A comparação abstrata (==) tenta converter ambos os operandos para o mesmo tipo antes de compará-los.
  • Com comparações abstratas relacionais (<=), ambos os operandos são convertidos em primitivos e, depois, para o mesmo tipo antes da comparação.
  • As strings são comparadas usando valores Unicode com base em pedidos padrão.

Características das comparações:

  • Duas strings são consideradas estritamente iguais quando têm os caracteres na mesma sequência e no mesmo comprimento.
  • Dois números são considerados estritamente iguais quando ambos são do mesmo tipo de número e são numericamente iguais. Isso significa que ambos 0 e -0 são estritamente iguais, pois ambos são avaliados como 0. Observe que NaN é um valor especial e não é igual a nada, incluindo NaN.
  • Dois operandos booleanos são considerados estritamente iguais se ambos forem true ou false.
  • Dois objetos nunca são considerados iguais tanto em comparações estritas quanto em comparações abstratas.
  • Expressões que comparam objetos só são consideradas verdadeiras se ambos os operandos se referirem a mesma instância exata do objeto.
  • Null e undefined são ambos considerados estritamente iguais a si mesmos (null === null) e abstratamente iguais um ao outro (null == undefined)

Operadores de igualdade

Igualdade (==)

O operador de igualdade converte primeiro os operandos que não são do mesmo tipo, depois os compara estritamente com um outro.

Sintaxe

 x == y

Exemplos

 1   ==  1        // verdadeiro
"1"  ==  1        // verdadeiro
 1   == '1'       // verdadeiro
 0   == false     // verdadeiro
 0   == null      // falso

 0   == undefined   // falso
 null  == undefined // verdadeiro

Desigualdade (!=)

O operador da desigualdade avalia a verdade se ambos os operandos não são iguais. Se os operandos não forem do mesmo tipo, ele tentará convertê-los para o mesmo tipo antes de fazer a comparação.

Sintaxe

x != y

Exemplos

1 !=   2     // verdadeiro
1 !=  "1"    // falso
1 !=  '1'    // falso
1 !=  true   // falso
0 !=  false  // falso

Identidade/igualdade estrita (===)

A identidade ou o operador de igualdade estrita retorna verdadeiro se ambos os operandos forem estritamente iguais em termos de valor e tipo. Ao contrário do operador de igualdade (==), ele não tentará converter os operandos para o mesmo tipo.

Sintaxe

x === y

Exemplos

3 === 3   // verdadeiro
3 === '3' // falso

Não identidade/desigualdade estrita (!==)

O operador de não identidade ou de desigualdade estrita retorna verdadeiro se ambos os operandos não forem estritamente iguais em termos de valor ou tipo.

Sintaxe

x !== y

Exemplos

3 !== '3' // verdadeiro
4 !== 3   // verdadeiro

Operadores relacionais

Operador maior do que (>)

O operador maior do que retorna verdadeiro se o operando da esquerda for maior que o da direita.

Sintaxe

x > y

Exemplos

4 > 3 // verdadeiro

Operador maior ou igual (>=)

O operador maior ou igual retorna verdadeiro se o operando da esquerda for maior ou igual ao da direita.

Sintaxe

x >= y

Exemplos

4 >= 3 // verdadeiro
3 >= 3 // verdadeiro

Operador menor do que (<)

O operador menor do que retorna verdadeiro se o operando da esquerda for menor do que o da direita.

Sintaxe

x < y

Exemplos

3 < 4 // verdadeiro

Operador menor ou igual (<=)

O operador menor ou igual retorna verdadeiro se o operando da esquerda for menor ou igual ao da direita.

Sintaxe

x <= y

Exemplos

3 <= 4 // verdadeiro

Exemplo de validação de formulário em JavaScript

A validação de formulário costumava ocorrer no servidor, após o cliente ter inserido todos os dados necessários e então pressionado o botão Enviar (ou Submit, em inglês). Se os dados enviados pelo client estivessem incorretos ou simplesmente faltando, o servidor teria que enviar todos os dados de volta para o client e solicitar que o formulário fosse reenviado com as informações corretas. Esse era realmente um processo longo que costumava sobrecarregar o servidor.

O JavaScript fornece uma forma de validar os dados do formulário no computador do client antes de enviá-lo para o servidor web. A validação do formulário geralmente desempenha duas funções:

Validação básica

Em primeiro lugar, o formulário deve ser verificado para garantir que todos os campos obrigatórios sejam preenchidos. Basta um laço através de cada campo do formulário para verificar os dados.

Validação do formato dos dados

Em segundo lugar, os dados que são inseridos devem ser verificados quanto à forma e ao valor correto. Seu código deve incluir a lógica apropriada para testar a forma correta dos dados.

Exemplo:

<html>
   
   <head>
      <title>Formulário de Validação</title>
      
      <script type="text/javascript">
         <!--
            // O código de validação do formulário virá aqui.
         //-->
      </script>
      
   </head>
   
   <body>
      <form action="/cgi-bin/test.cgi" name="myForm" onsubmit="return(validate());">
         <table cellspacing="2" cellpadding="2" border="1">
            
            <tr>
               <td align="right">Nome</td>
               <td><input type="text" name="Name" /></td>
            </tr>
            
            <tr>
               <td align="right">Email</td>
               <td><input type="text" name="EMail" /></td>
            </tr>
            
            <tr>
               <td align="right">Código Postal</td>
               <td><input type="text" name="Zip" /></td>
            </tr>
            
            <tr>
               <td align="right">País</td>
               <td>
                  <select name="País">
                     <option value="-1" selected>[escolha o seu]</option>
                     <option value="1">EUA</option>
                     <option value="2">UK</option>
                     <option value="3">ÍNDIA</option>
                  </select>
               </td>
            </tr>
            
            <tr>
               <td align="right"></td>
               <td><input type="submit" value="Enviar" /></td>
            </tr>
            
         </table>
      </form>
      
   </body>
</html>

Validação do formulário básico

Primeiro, vamos ver como fazer uma validação básica do formulário. No formulário acima, estamos chamando validate() para validar dados quando o evento onsubmit estiver ocorrendo. O código a seguir mostra a implementação dessa função validate.

<script type="text/javascript">
   // O código de validação do formulário virá aqui.
   function validate()
      {
      
         if( document.myForm.Name.value == "" )
         {
            alert( "Por favor, forneça seu nome!" );
            document.myForm.Name.focus() ;
            return false;
         }
         
         if( document.myForm.EMail.value == "" )
         {
            alert( "Por favor, forneça seu e-mail!" );
            document.myForm.EMail.focus() ;
            return false;
         }
         
         if( document.myForm.Zip.value == "" ||
         isNaN( document.myForm.Zip.value ) ||
         document.myForm.Zip.value.length != 5 )
         {
            alert( "Por favor, fornecer um código postal no formato #####." );
            document.myForm.Zip.focus() ;
            return false;
         }
         
         if( document.myForm.Country.value == "-1" )
         {
            alert( "Por favor, forneça seu país!" );
            return false;
         }
         return( true );
      }
</script>

Validação do formato dos dados

Agora, veremos como podemos validar nossos dados de formulário inseridos antes de enviá-los para o servidor web.

O exemplo a seguir mostra como validar um endereço de e-mail inserido. Um endereço de e-mail deve conter pelo menos um sinal ‘@’ e um ponto (.). Além disso, o '@' não deve ser o primeiro caractere do endereço de e-mail, e o último ponto deve conter pelo menos um caractere após o sinal '@'.

Exemplo:

<script type="text/javascript">
    function validateEmail()
      {
         var emailID = document.myForm.EMail.value;
         atpos = emailID.indexOf("@");
         dotpos = emailID.lastIndexOf(".");
         
         if (atpos < 1 || ( dotpos - atpos < 2 )) 
         {
            alert("Por favor, digite a identificação correta do e-mail")
            document.myForm.EMail.focus() ;
            return false;
         }
         return( true );
      }
</script>

Restrições dos formulários em HTML5

Algumas das restrições HTML5 são comumente usadas para o atributo <input> (por exemplo, type="password", maxlength, required e disabled). Uma restrição menos usada, normalmente, é o atributo pattern que recebe uma expressão regular em JavaScript.

Exemplo de instrução if em JavaScript

A instrução if executa uma declaração se uma condição especificada for verdadeira. Se a condição for falsa, outra declaração pode ser executada usando a declaração else.

Observação: a declaração else é opcional.

if (condição)
    /* faça algo */
else
    /* faça outra coisa */

Múltiplas declarações if...else podem ser encadeadas criando uma cláusula else if. Isso especifica uma nova condição para teste e pode ser repetida para testar várias condições. A verificação continua até que uma instrução verdadeira seja apresentada para execução.

if (condição1)
    /* faça algo */
else if (condição2)
    /* faça outra coisa */
else if (condição3)
    /* faça outra coisa */
else
    /* declaração final */

Observação: se você quiser executar mais de uma declaração na parte if, else ou else if, são necessárias chaves em torno das declarações:

if (condição) {
    /* faça */
    /* algo */
    /* com declarações múltiplas */
} else {
    /* faça outra coisa */
    /* else */
}

Link na MDN

Exemplos

Usando if...else:

    // Se x=5, z=7 e q=42. Se x não é 5, então z=19.
    if (x == 5) {
      z = 7;
      q = 42
    else
      z = 19;

Usando else if:

if (x < 10)
    return "Número menor";
else if (x < 50)
    return "Número médio";
else if (x < 100)
    return "Número maior";
else {
    flag = 1;
    return "Número inválido";
}

Exemplo de protótipo em JavaScript

O JavaScript é uma linguagem com base em protótipos. Portanto, compreender o objeto prototype é um dos conceitos mais importantes que os profissionais de JavaScript precisam conhecer.

Esta seção dará uma breve visão geral do objeto prototype através de vários exemplos. Antes de ler esta parte, você precisará ter uma compreensão básica de referência em JavaScript do this (texto em inglês).

Objeto prototype

Por uma questão de clareza, vamos examinar o seguinte exemplo:

function Point2D(x, y) {
  this.x = x;
  this.y = y;
}

Como a função Point2D é declarada, uma propriedade padrão chamada prototype será criada para ela (observe que, em JavaScript, uma função também é um objeto).

A propriedade prototype é um objeto que contém uma propriedade constructor e o seu valor é a função Point2D: Point2D.prototype.constructor = Point2D. Quando você chama o Point2D com a palavra-chave new, os objetos recém-criados herdarão todas as propriedades do Point2D.prototype.

Para verificar isso, você pode adicionar um método chamado move a Point2D.prototype, como segue:

Point2D.prototype.move = function(dx, dy) {
  this.x += dx;
  this.y += dy;
}

var p1 = new Point2D(1, 2);
p1.move(3, 4);
console.log(p1.x); // 4
console.log(p1.y); // 6

O Point2D.prototype é chamado de objeto protótipo ou protótipo de objeto p1 e, para qualquer outro objeto criado com a sintaxe new Point2D(...), você pode adicionar mais propriedades ao objeto protótipo Point2D.prototype como quiser. O padrão comum é declarar os métodos ao Point2D.prototype e as outras propriedades serão declaradas na função construtora.

Os objetos integrados em JavaScript são construídos de maneira semelhante. Por exemplo:

  • Protótipo de objetos criado com new Object() ou {}, a sintaxe é Object.prototype.
  • Protótipo de arrays criado com new Array() ou [], a sintaxe é Array.prototype.
  • E assim por diante, com outros objetos integrados, tais como Date and RegExp.

Object.prototype é herdado por todos os objetos e não tem nenhum protótipo (seu protótipo é null).

Protótipo em cadeia

O mecanismo do protótipo em cadeia é simples: quando você acessa uma propriedade p no objeto obj, a engine do JavaScript pesquisará essa propriedade dentro do objeto obj. Se a engine falhar na busca, continuará pesquisando no protótipo do objeto obj e assim por diante até chegar ao Object.prototype. Se, após a busca tiver terminado, nada tiver sido encontrado, o resultado será undefined. Por exemplo:

var obj1 = {
  a: 1,
  b: 2
};

var obj2 = Object.create(obj1);
obj2.a = 2;

console.log(obj2.a); // 2
console.log(obj2.b); // 2
console.log(obj2.c); // undefined

No trecho acima, a declaração var obj2 = Object.create(obj1) criará o objeto obj2  com o protótipo do objetoobj1 . Em outras palavras, obj1 torna-se o protótipo de obj2 ao invés de Object.prototype por padrão. Como você pode ver, b não é uma propriedade de obj2; você ainda pode acessá-la através do protótipo em cadeia. Para a propriedade c, entretanto, você recebe um valor undefined, pois ela não pode ser encontrada no obj1 ou no Object.prototype.

Classes

No ES2016, agora podemos usar a palavra-chave Class, bem como os métodos mencionados acima para manipular o prototype. A Class em JavaScript é interessante para os desenvolvedores com histórico em programação orientada a objetos, mas essencialmente faz a mesma coisa que acima.

class Rectangle {
  constructor(height, width) {
    this.height = height
    this.width = width
  }

  get area() {
    return this.calcArea()
  }

  calcArea() {
    return this.height * this.width
  }
}

const square = new Rectangle(10, 10)

console.log(square.area) // 100

Isso é basicamente o mesmo que:

function Rectangle(height, width) {
  this.height = height
  this.width = width
}

Rectangle.prototype.calcArea = function calcArea() {
  return this.height * this.width
}

Os métodos getter e setter nas classes ligam uma propriedade de Object a uma função que será chamada quando essa propriedade for procurada. É apenas um adendo sintático para facilitar a procura ou a definição de propriedades.

Exemplo de escopo em JavaScript

Se você tem programado em JavaScript há algum tempo, sem dúvida você encontrou um conceito conhecido como escopo. O que é escopo? Por que você deveria dedicar um tempo para aprendê-lo?

Na linguagem do programador, o escopo é o contexto atual da execução. Confuso? Vamos dar uma olhada no seguinte trecho de código:

var foo = 'Oi, eu sou o foo!';

var baz = function () {
  var bar = 'Oi, eu sou o bar também!';
    console.log(foo);
}

baz(); // Oi, eu sou o foo!
console.log(bar); // ReferênciaErro...

Esse é um exemplo simples, mas faz um bom trabalho de ilustração do que é conhecido como escopo lexical. O JavaScript, e quase todas as outras linguagens de programação, tem um escopo lexical. Há outro tipo de escopo, conhecido como escopo dinâmico, mas não vamos discutir isso.

O termo escopo lexical parece extravagante, mas, como você verá, é realmente simples, em princípio. Em um escopo lexical, há dois tipos de escopo: o escopo global e um escopo local.

Antes de você digitar a primeira linha de código em seu programa, é criado um escopo global para você. Ele contém todas as variáveis que você declara em seu programa fora de qualquer função.

No exemplo acima, a variável foo está no escopo global do programa, enquanto a variável bar  é declarada dentro de uma função e, portanto, está no escopo local dessa função.

Vamos quebrar o exemplo linha por linha. Pode ser que você esteja confuso neste ponto, mas prometo que terá uma compreensão muito melhor quando terminar de ler isto.

Na linha 1, estamos declarando a variável foo. Nada muito extravagante aqui. Vamos chamar isso de uma referência à esquerda de foo, porque estamos atribuindo um valor a foo e ele está no lado esquerdo do sinal de igual.

Na linha 3, estamos declarando uma função e atribuindo-a para a variável baz . Essa é outra referência à esquerda a baz. Estamos atribuindo um valor a ela (lembre-se, funções também são valores!). Essa função é então chamada na linha 8. Essa é uma referência à direita a baz. Estamos recuperando o valor de baz, que neste caso é uma função e depois a invocamos.

Outra referência à direita a baz seria se atribuíssemos o seu valor a outra variável, por exemplo foo = baz. Esta seria uma referência à esquerda para foo e uma referência à direita para baz.

As referências à esquerda e à direita podem parecer confusas, mas são importantes para discutir o escopo. Pense desse modo: uma referência à esquerda está atribuindo um valor à variável, enquanto uma referência à direita está recuperando o valor da variável. Elas são apenas uma forma mais curta e conveniente de dizer 'recuperando o valor' e 'atribuindo um valor'.

Vamos agora quebrar em partes o que está acontecendo dentro da própria função.

Quando o compilador compila o código dentro de uma função, ele entra no escopo local da função.

Na linha 4, a variável bar é declarada. Essa é uma referência à esquerda a bar. Na linha seguinte, temos uma referência à direita para foo dentro do console.log(). Lembre-se, estamos recuperando o valor do foo e depois passando-o como um argumento para o método console.log().

Quando temos uma referência à direita a foo, o compilador procura a declaração da variável foo. O compilador não a encontra na própria função, ou no escopo local da função, então sobe um nível: para o escopo global.

Nesse ponto, você provavelmente está pensando que o escopo tem algo a ver com variáveis. Isso é correto. Um escopo pode ser pensado como um recipiente para variáveis. Todas as variáveis que são criadas dentro de um escopo local só são acessíveis nesse escopo local. Entretanto, todos os escopos locais podem acessar o escopo global (sei que você provavelmente está ainda mais confuso neste momento, mas tenha paciência comigo por mais alguns parágrafos).

Assim, o compilador vai até o escopo global para encontrar uma referência à esquerda para a variável foo. Ele encontra uma na linha 1, então recupera o valor da referência à esquerda, que é uma string: 'Olá, eu sou o foo!'. Essa string é enviada para o método console.log(), e enviada ao console.

O compilador terminou de executar o código dentro da função, então voltamos para a linha 9. Na linha 9, temos uma referência à direita para a variável bar.

Agora, bar foi declarada no escopo local do baz, mas há uma referência à direita para ela no escopo global. Como não há referência à esquerda para bar no escopo global, o compilador não consegue encontrar um valor para bar e lança um ReferenceError.

Você pode perguntar, no entanto: se a função pode procurar variáveis fora de si mesma, ou se um escopo local pode olhar o escopo global para encontrar referências à esquerda, por que o escopo global não pode olhar para um escopo local? Bem, é assim que o escopo lexical funciona!

... // Escopo global
var baz = function() {
  ... // Escopo do baz
}
... /// Escopo global

Esse é o mesmo código de cima que ilustra o escopo. Isso forma uma tipo de hierarquia que vai até o escopo global:

baz -> global.

Portanto, se houver uma referência à direita para uma variável baz dentro do escopo, ela pode ser cumprida por uma referência à esquerda para essa variável no escopo global. O oposto, no entanto, não é verdade.

E se tivéssemos outra função dentro do baz?

... // Escopo global
var baz = function() {
  ... // Escopo do baz

  var bar = function() {
     ... // Escopo do baz
  }

}
... /// Escopo global

Nesse caso, a hierarquia ou o escopo em cadeia ficaria assim:

bar -> baz -> global

Qualquer referência à direita dentro do escopo local do baz pode ser cumprida por referências à esquerda no escopo global ou no escopo do baz, mas uma referência à direita no escopo do baz não pode ser cumprida por uma referência à esquerda no escopo do baz.

Você só pode atravessar uma cadeia de escopo para baixo, não para cima.

Há outras duas coisas importantes que você deve saber sobre os escopos JavaScript.

  1. Os escopos são declarados por funções, não por blocos.
  2. As funções podem ser referenciadas para o futuro, as variáveis não podem.

Observe (cada comentário descreve o escopo na linha em que está escrito):

    // outer() está no escopo aqui, porque as funções podem ser referenciadas para o futuro
    
    function outer() {
    
        // só inner() está no escopo aqui
        // porque apenas as funções são referenciadas para o futuro
    
        var a = 1;
        
        // Agora 'a' e inner() estão no escopo
        
        function inner() {
            var b = 2
            
            if (a == 1) {
                var c = 3;
            }
            
            // 'c' está ainda no escopo porque o JavaScript não se importa
            // sobre o fim do bloco 'if', só a função inner()
        }
        
        // Agora b e c estão fora do escopo
        // a e inner() ainda estão no escopo
        
    }
    
    // Aqui, só outer() está no escopo

Exemplo de laço for em JavaScript

Sintaxe

for ([inicialização]); [condição]; [expressão final]) {
   // Declaração
}

A declaração for em JavaScript consiste em três expressões e uma declaração:

  • inicialização - executa antes da primeira execução no laço. Essa expressão é comumente usada para criar contadores. As variáveis criadas aqui são colocadas em escopo para o laço. Uma vez que o laço tenha terminado sua execução, elas são destruídas.
  • condição - a expressão que é verificada antes da execução de cada iteração. Se omitida, essa expressão é avaliada como verdadeira e a declaração do laço é executada. Se ela for avaliada como falsa, o laço para.
  • Expressão final - a expressão que é executada após cada iteração. Ela geralmente é usada para incrementar um contador., mas também pode ser usada para decrementar um contador.
  • declaração - O código a ser repetido no laço

Qualquer uma destas três expressões ou a declaração pode ser omitida. Laços for são comumente usados para contar um certo número de iterações para repetir uma declaração. Use uma declaração de break para sair do laço antes que a expressão da condição seja avaliada como falsa.

Armadilhas comuns

Excedendo os limites de um array

Quando indexar sobre um array muitas vezes, é fácil exceder os limites do array (por exemplo, tente fazer referência ao 4º elemento de um array de 3 elementos).

    // Isso causará um erro.
    // Os limites do array serão excedidos.
    var arr = [ 1, 2, 3 ];
    for (var i = 0; i <= arr.length; i++) {
       console.log(arr[i]);
    }

    output:
    1
    2
    3
    undefined

Há duas formas de corrigir este código. Defina a condição para i < arr.length ou i <= arr.length - 1

Exemplos

Iterar através de números inteiros de 0-8

for (var i = 0; i < 9; i++) {
   console.log(i);
}

output:
0
1
2
3
4
5
6
7
8

Quebra de um laço antes que a expressão da condição seja falsa

for (var elephant = 1; elephant < 10; elephant+=2) {
    if (elephant === 7) {
        break;
    }
    console.info('O elefante é ' + elephant);
}

Output:
O elefante é 1
O elefante é 3
O elefante é 5

Exemplo de declaração de break em JavaScript

A declaração de break encerra o laço atual, da declaração de switch ou de label e transfere o controle do programa para a declaração seguinte à declaração terminada.

break;

Se a declaração de break for usada em uma declaração como label (rótulo), a sintaxe é a seguinte:

break nomeDoLabel;

Exemplos

A seguinte função tem uma declaração de break que encerra o laço while quando i é 3, e então retorna o valor 3 * x.

function testBreak(x) {
  var i = 0;

  while (i < 6) {
    if (i == 3) {
      break;
    }
    i += 1;
  }

  return i * x;
}

No exemplo a seguir, o contador é configurado para contar de 1 a 99; no entanto, a declaração de break encerra o laço após 14 contagens.

for (var i = 1; i < 100; i++) {
  if (i == 15) {
    break;
  }
}

Exemplo do laço do while em JavaScript

O laço do...while está intimamente relacionado com o laço while. No laço do...while, a condição é verificada no final do laço.

Aqui está a sintaxe para o laço do...while:

Sintaxe:

 do {

   *Declaração(ões);*

} while (*condição*);

Declaração(ões): uma declaração que é executada pelo menos uma vez antes da condição ou expressão booleana ser avaliada e é executada novamente cada vez que a condição é avaliada como verdadeira.

Condição: aqui, uma condição é uma expressão booleana. Se a expressão booleana for avaliada como verdadeira, a declaração é executada novamente. Quando a expressão booleana é avaliada como falsa, o laço termina.

Exemplo:

var i = 0;
do {
  i = i + 1;
  console.log(i);
} while (i < 5);

Output:
1
2
3
4
5

Exemplo de laço for in em JavaScript

A declaração for...in  itera sobre as propriedades enumeráveis de um objeto, em ordem arbitrária. Para cada propriedade distinta, as declarações podem ser executadas.

for (variável in objeto) {
...
}

Um nome de propriedade diferente é atribuído à variável em cada iteração. Um objeto cujas propriedades enumeráveis são iteradas pode ser usado como opcional aqui.

Exemplos

// Inicializa objeto.
a = { "a": "Atenas", "b": "Belgrado", "c": "Cairo" }

// Itera sobre as propriedades.
var s = ""
for (var key in a) {
    s += key + ": " + a[key];
    s += "<br />";
    }
document.write (s);

// Saída:
// a: Atenas
// b: Belgrado
// c: Cairo

// Inicializa o array.
var arr = new Array("zero", "um", "dois");

// Adiciona algumas propriedades expando para o array.
arr["orange"] = "fruit";
arr["carrot"] = "vegetable";

// Itera sobre as propriedades e elementos.
var s = "";
for (var key in arr) {
    s += key + ": " + arr[key];
    s += "<br />";
}

document.write (s);

// Saída:
//   0: zero
//   1: um
//   2: dois
//   laranja: fruta
//   cenoura: vegetal

// Forma eficiente de obter as chaves de um objeto usando uma expressão dentro das condições do laço for-in loop
var myObj = {a: 1, b: 2, c:3}, myKeys = [], i=0;
for (myKeys[i++] in myObj);

document.write(myKeys);

//Saída:
//   a
//   b
//   c

Exemplo de laço for of em JavaScript

A declaração do laço for...of  cria uma iteração de laço sobre objetos iteráveis (incluindo arrays, maps, sets, o objeto arguments e assim por diante.), invocando um hook de iteração personalizado com declarações a serem executadas para o valor de cada propriedade distinta.

    for (variável of objeto) {
        declaração
    }

Descrição da variável: em cada iteração, um valor de uma propriedade diferente é atribuído ao objeto variable.object, cujas propriedades enumeradas são iteradas.

Exemplos

Array

    let arr = [ "fred", "tom", "bob" ];

    for (let i of arr) {
        console.log(i);
    }

    // Saída:
    // fred
    // tom
    // bob

Map

    var m = new Map();
    m.set(1, "preto");
    m.set(2, "vermelho");

    for (var n of m) {
        console.log(n);
    }

    // Saída:
    // 1,preto
    // 2,vermelho

Set

    var s = new Set();
    s.add(1);
    s.add("vermelho");

    for (var n of s) {
        console.log(n);
    }

    // Saída:
    // 1
    // vermelho

Objeto arguments

    // seu navegador deve suportar o laço for..of
    // e as variáveis let (com escopo) para laços

    function displayArgumentsObject() {
        for (let n of arguments) {
            console.log(n);
        }
    }


    displayArgumentsObject(1, 'red');

    // Saída:
    // 1
    // vermelho

Exemplo de laço while em JavaScript

O laço while começa com a avaliação da condição. Se a condição for verdadeira, as declarações são executadas. Se a condição for falsa, as declarações não são executadas. Depois disso, o laço while termina.

Aqui está a sintaxe para o laço while:

Sintaxe:

while (condição)

{

  declaração(ões);

}

Declaração(ões): uma declaração é executada desde que a condição seja avaliada como verdadeira.

Condição: aqui, a condição é uma expressão booleana que é avaliada antes de cada passagem através do laço. Se esta condição for avaliada como verdadeira, as declarações são executadas. Quando a condição é avaliada como falsa, a execução continua com a declaração após o laço while.

Exemplo:

    var i = 1;
    while (i < 10) 
    {
      console.log(i);
       i++; // i=i+1 a mesma coisa
    }

    Output:
    1 
    2 
    3 
    4
    5
    6
    7
    8
    9