4.1 Língua JavaScript início rápido

Autora:Bem-estar, Criado: 2019-04-26 11:46:12, Atualizado: 2019-04-27 11:53:43

História

Esta seção dá um pouco de conhecimento sobre JavaScript para ajudá-lo a entender por que é assim.

JavaScript versus ECMAScript

ECMAScript é o nome oficial do JavaScript. Um novo nome se tornou necessário porque há uma marca registrada no JavaScript (detida originalmente pela Sun, agora pela Oracle). No momento, a Mozilla é uma das poucas empresas autorizadas a usar oficialmente o nome JavaScript porque recebeu uma licença há muito tempo. Para uso comum, essas regras se aplicam:

  • JavaScript significa a linguagem de programação.
  • ECMAScript é o nome usado pela especificação da linguagem. Portanto, sempre que se refere a versões da linguagem, as pessoas dizem ECMAScript. A versão atual do JavaScript é ECMAScript 5; ECMAScript 6 está atualmente em desenvolvimento.

Influências e natureza da linguagem

O criador do JavaScript, Brendan Eich, não teve escolha a não ser criar a linguagem muito rapidamente (ou outras tecnologias piores teriam sido adotadas pela Netscape). Ele tomou emprestado de várias linguagens de programação: Java (sintaxe, valores primitivos versus objetos), Scheme e AWK (funções de primeira classe), Self (herança prototípica) e Perl e Python (strings, arrays e expressões regulares).

O JavaScript não tinha manipulação de exceções até o ECMAScript 3, o que explica por que a linguagem muitas vezes converte automaticamente valores e muitas vezes falha silenciosamente: inicialmente não podia lançar exceções.

Por um lado, o JavaScript tem peculiaridades e não possui muita funcionalidade (variáveis de escopo de blocos, módulos, suporte para subclasses, etc.). Por outro lado, ele tem vários recursos poderosos que permitem contornar esses problemas. Em outras linguagens, você aprende características da linguagem. No JavaScript, você muitas vezes aprende padrões em vez disso.

Dadas as suas influências, não é surpresa que o JavaScript permita um estilo de programação que é uma mistura de programação funcional (funções de ordem superior; mapa embutido, reduzir, etc.) e programação orientada a objetos (objetos, herança).

Sintática

Esta seção explica os princípios sintáticos básicos do JavaScript.

Uma visão geral da sintaxe

Alguns exemplos de sintaxe:

// Two slashes start single-line comments

var x;  // declaring a variable

x = 3 + y;  // assigning a value to the variable `x`

foo(x, y);  // calling function `foo` with parameters `x` and `y`
obj.bar(3);  // calling method `bar` of object `obj`

// A conditional statement
if (x === 0) {  // Is `x` equal to zero?
    x = 123;
}

// Defining function `baz` with parameters `a` and `b`
function baz(a, b) {
    return a + b;
}

Observe os dois usos diferentes do sinal igual:

  • Um único sinal igual (=) é usado para atribuir um valor a uma variável.
  • Um sinal triplo igual (===) é usado para comparar dois valores (ver Operadores de Igualdade).

Declarações contra expressões

Para entender a sintaxe do JavaScript, você deve saber que ele tem duas principais categorias sintáticas: declarações e expressões:

  • Um programa é uma sequência de instruções. Aqui está um exemplo de uma instrução, que declara (cria) uma variável foo:
var foo;
  • As expressões produzem valores. São argumentos de função, o lado direito de uma atribuição, etc. Aqui está um exemplo de uma expressão:
3 * 7

A distinção entre instruções e expressões é melhor ilustrada pelo fato de que o JavaScript tem duas maneiras diferentes de fazer if-then-else como uma instrução:

var x;
if (y >= 0) {
    x = y;
} else {
    x = -y;
}

ou como uma expressão:

var x = y >= 0 ? y : -y;

Você pode usar o último como um argumento de função (mas não o primeiro):

myFunction(y >= 0 ? y : -y)

Finalmente, onde quer que o JavaScript espere uma instrução, você também pode usar uma expressão; por exemplo:

foo(7, 1);

A linha inteira é uma instrução (uma chamada instrução de expressão), mas a chamada de função foo ((7, 1) é uma expressão.

Pontos e vírgulas

Os pontos e vírgulas são opcionais no JavaScript. No entanto, recomendo sempre incluí-los, porque caso contrário o JavaScript pode adivinhar mal sobre o final de uma instrução. Os detalhes são explicados na inserção automática de pontos e vírgulas.

O ponto e vírgula terminam as instruções, mas não os blocos. Há um caso em que você verá um ponto e vírgula após um bloco: uma expressão de função é uma expressão que termina com um bloco.

// Pattern: var _ = ___;
var x = 3 * 7;
var f = function () { };  // function expr. inside var decl.

Observações

O JavaScript tem dois tipos de comentários: comentários de linha única e comentários multilíneos.

x++; // single-line comment

Os comentários de várias linhas são delimitados por /* e */:

/* This is
   a multiline
   comment.
 */

Variaveis e atribuição

As variáveis em JavaScript são declaradas antes de serem usadas:

var foo;  // declare variable `foo`

Atribuição

Pode declarar uma variável e atribuir um valor ao mesmo tempo:

var foo = 6;

Você também pode atribuir um valor a uma variável existente:

foo = 4;  // change variable `foo`

Operadores de atribuição composta

Existem operadores de atribuição compostos como +=. As duas atribuições seguintes são equivalentes:

x += 1;
x = x + 1;

Identificadores e nomes de variáveis

Identificadores são nomes que desempenham vários papéis sintáticos em JavaScript. Por exemplo, o nome de uma variável é um identificador.

Em geral, o primeiro caractere de um identificador pode ser qualquer letra Unicode, um sinal de dólar ($), ou um ponto abaixo (_).

arg0
_tmp
$elem
π

Os seguintes identificadores são palavras reservadas, fazem parte da sintaxe e não podem ser utilizados como nomes de variáveis (incluindo nomes de funções e nomes de parâmetros):

img

Os seguintes três identificadores não são palavras reservadas, mas devem ser tratados como se fossem:

img

Por fim, você também deve ficar longe dos nomes das variáveis globais padrão. Você pode usá-los para variáveis locais sem quebrar nada, mas seu código ainda fica confuso.

Valores

O JavaScript tem muitos valores que esperamos de linguagens de programação: booleans, números, strings, arrays, etc. Todos os valores no JavaScript têm propriedades. Cada propriedade tem uma chave (ou nome) e um valor. Você pode pensar em propriedades como campos de um registro. Você usa o operador ponto (.) para ler uma propriedade:

value.propKey

Por exemplo, a cadeia abc tem a propriedade de comprimento:

> var str = 'abc';
> str.length
3

O anterior também pode ser escrito como:

> 'abc'.length
3
The dot operator is also used to assign a value to a property:

> var obj = {};  // empty object
> obj.foo = 123; // create property `foo`, set it to 123
123
> obj.foo
123

E você pode usá-lo para invocar métodos:

> 'hello'.toUpperCase()
'HELLO'

No exemplo anterior, nós invocamos o método toUpperCase() no valor hello.

Valores primitivos versus objetos

O JavaScript faz uma distinção um pouco arbitrária entre valores:

  • Os valores primitivos são booleanos, números, strings, nulo e indefinido.
  • Todos os outros valores são objetos. Uma grande diferença entre os dois é como eles são comparados; cada objeto tem uma identidade única e é apenas (estritamente igual a si mesmo):
> var obj1 = {};  // an empty object
> var obj2 = {};  // another empty object
> obj1 === obj2
false
> obj1 === obj1
true

Em contraste, todos os valores primitivos que codificam o mesmo valor são considerados os mesmos:

> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true

As duas seções seguintes explicam os valores e objetos primitivos com mais detalhes.

Valores primitivos

Os seguintes são todos os valores primitivos (ou primitivos para abreviar):

  • Booleans: verdadeiro, falso (ver Booleans)
  • Números: 1736, 1.351 (ver Números)
  • Cordas: abc, abc (ver Cordas)
  • Dois não-valores: indefinido, nulo (ver indefinido e nulo)

Os primitivos têm as seguintes características:

Comparado por valor

O conteúdo é comparado:

> 3 === 3
true
> 'abc' === 'abc'
true

Sempre imutável As propriedades não podem ser alteradas, adicionadas ou removidas:

> var str = 'abc';

> str.length = 1; // try to change property `length`
> str.length      // ⇒ no effect
3

> str.foo = 3; // try to create property `foo`
> str.foo      // ⇒ no effect, unknown property
undefined

(Lendo uma propriedade desconhecida sempre retorna indefinido.)

Objetos

Todos os valores não-primitivos são objetos.

  • Objetos simples, que podem ser criados por objetos literais (ver Objetos únicos):
{
    firstName: 'Jane',
    lastName: 'Doe'
}

O objeto anterior tem duas propriedades: o valor da propriedade firstName é Jane e o valor da propriedade lastName é Doe.

  • Matrizes, que podem ser criadas por literais de matriz (ver Matrizes):
[ 'apple', 'banana', 'cherry' ]

A matriz anterior tem três elementos que podem ser acessados através de índices numéricos.

  • Expressões regulares, que podem ser criadas por expressões regulares literais (ver Expressões regulares):
/^a+b+$/

Os objectos têm as seguintes características:

Comparado por referência

As identidades são comparadas; cada valor tem a sua própria identidade:

> ({} === {})  // two different empty objects
false

> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true

Mutable por defeito

Normalmente, você pode alterar, adicionar e remover propriedades livremente (veja Objetos únicos):

> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123

indefinido e nulo

A maioria das linguagens de programação tem valores que denotam informações faltantes.

  • undefined significa nenhum valor. Variaveis não inicializadas são indefinidas:
> var foo;
> foo
undefined

Parâmetros em falta não definidos:

> function f(x) { return x }
> f()
undefined

Se você ler uma propriedade inexistente, você fica indefinido:

> var obj = {}; // empty object
> obj.foo
undefined
  • null significa nenhum objeto. É usado como um não valor sempre que um objeto é esperado (parâmetros, último em uma cadeia de objetos, etc.).

Alerta

undefined e null não têm propriedades, nem mesmo métodos padrão como toString().

Verificação de indefinido ou nulo

As funções normalmente permitem que você indique um valor faltante através de indefinido ou nulo.

if (x === undefined || x === null) {
    ...
}

Você também pode explorar o fato de que tanto indefinido quanto nulo são considerados falsos:

if (!x) {
    ...
}

Alerta

false, 0, NaN e também são considerados falsos (ver Truthy e Falsy).

Categorização dos valores utilizando tipo e instância de

Existem dois operadores para categorizar valores: typeof é usado principalmente para valores primitivos, enquanto instanceof é usado para objetos. O tipo é assim:

typeof value

Retorna uma cadeia que descreve o type do valor.

> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // empty object literal
'object'
> typeof [] // empty array literal
'object'

O quadro seguinte enumera todos os resultados do tipo:

img

typeof null retornar object é um bug que não pode ser corrigido, porque quebraria o código existente.

O exemplo é este:

value instanceof Constr

Retorna true se o valor for um objeto criado pelo construtor Constr (ver Construtores: fábricas para objetos).

> var b = new Bar();  // object created by constructor Bar
> b instanceof Bar
true

> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object  // Array is a subconstructor of Object
true

> undefined instanceof Object
false
> null instanceof Object
false

Booleanos

O tipo booleano primitivo compreende os valores true e false. Os seguintes operadores produzem booleanos:

  • Operadores lógicos binários: && (And),
  • Operador lógico com prefixo:! (Não)
  • Operadores de comparação:operadores de igualdade: ===,!==, ==,!=
  • Operadores de ordenação (para cadeias e números): >, >=, <, <=

Verdadeiros e falsos

Quando o JavaScript espera um valor booleano (por exemplo, para a condição de uma instrução if), qualquer valor pode ser usado. Ele será interpretado como verdadeiro ou falso. Os seguintes valores são interpretados como falsos:

  • não definido, nulo
  • Boolean: falso
  • Número: 0, NaN
  • Corda:

Todos os outros valores (incluindo todos os objetos!) são considerados verdadeiros. Os valores interpretados como falsos são chamados falsos e os valores interpretados como verdadeiros são chamados verdadeiros.

> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
> Boolean({}) // empty object
true
> Boolean([]) // empty array
true

Operadores lógicos binários

Os operadores lógicos binários em JavaScript são de curto-circuito. Ou seja, se o primeiro operando for suficiente para determinar o resultado, o segundo operando não é avaliado. Por exemplo, nas seguintes expressões, a função foo() nunca é chamada:

false && foo()
true  || foo()

Além disso, os operadores lógicos binários retornam um de seus operandos, que pode ou não ser um booleano.

E (&&)

Se o primeiro operando for falso, devolva-o. Caso contrário, devolva o segundo operando:

> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'

Ou (permanecer)

Se o primeiro operando for verdadeiro, devolva-o. Caso contrário, devolva o segundo operando:

> 'abc' || 123
'abc'
> '' || 123
123

Operadores da igualdade

O JavaScript tem dois tipos de igualdade:

  • Normal, ou leniente, (em) igualdade: == e!=
  • Estritamente (em) igualdade: === e!==

A igualdade normal considera (também) muitos valores iguais (os detalhes são explicados na igualdade normal (==,!=)), o que pode esconder erros.

Números

Todos os números em JavaScript são com ponto flutuante:

> 1 === 1.0
true

Os números especiais incluem os seguintes:

NaN (não um número) Valor de erro:

> Number('xyz')  // 'xyz' can’t be converted to a number
NaN

Infinito. Também principalmente um valor de erro:

> 3 / 0
Infinity
> Math.pow(2, 1024)  // number too large
Infinity

O infinito é maior do que qualquer outro número (exceto NaN). Da mesma forma, -Infinity é menor do que qualquer outro número (exceto NaN). Isso torna esses números úteis como valores padrão (por exemplo, quando você está procurando um mínimo ou um máximo).

Operadores

O JavaScript tem os seguintes operadores aritméticos (ver Operadores aritméticos):

  • Adição: número 1 + número 2
  • Subtração: número 1 - número 2
  • Multiplicação: número 1 * número 2
  • Divisão: número 1 / número 2
  • Resíduo: número1 % número2
  • Incremento: ++variavel, variável++
  • Decreto: variavel, variável
  • Negativo: - valor
  • Conversão para número: +valor

O objeto global Matemática (ver Matemática) fornece mais operações aritméticas, através de funções.

O JavaScript também possui operadores para operações bitwise (por exemplo, bitwise And; veja Bitwise Operators).

Cordas

As cadeias podem ser criadas diretamente através de literais de cadeia. Esses literais são delimitados por aspas simples ou duplas. A backslash () escapa de caracteres e produz alguns caracteres de controle.

'abc'
"abc"

'Did she say "Hello"?'
"Did she say \"Hello\"?"

'That\'s nice!'
"That's nice!"

'Line 1\nLine 2'  // newline
'Backlash: \\'

Os caracteres individuais são acessados através de parênteses quadrados:

> var str = 'abc';
> str[1]
'b'

O comprimento da propriedade conta o número de caracteres na cadeia:

> 'abc'.length
3

Como todos os primitivos, as cadeias são imutáveis; você precisa criar uma nova cadeia se quiser mudar uma existente.

Operadores de cadeia

As cadeias são concatenadas através do operador mais (+), que converte o outro operando em uma cadeia se um dos operandos for uma cadeia:

> var messageCount = 3;
> 'You have ' + messageCount + ' messages'
'You have 3 messages'

Para concatenar strings em várias etapas, use o operador +=:

> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'

Métodos de cadeia

As strings têm muitos métodos úteis (veja String Prototype Methods).

> 'abc'.slice(1)  // copy a substring
'bc'
> 'abc'.slice(1, 2)
'b'

> '\t xyz  '.trim()  // trim whitespace
'xyz'

> 'mjölnir'.toUpperCase()
'MJÖLNIR'

> 'abc'.indexOf('b')  // find a string
1
> 'abc'.indexOf('x')
-1

Declarações

Condicionais e loops em JavaScript são introduzidos nas seções seguintes.

Condições

A instrução if tem uma cláusula then e uma cláusula else opcional que são executadas dependendo de uma condição booleana:

if (myvar === 0) {
    // then
}

if (myvar === 0) {
    // then
} else {
    // else
}

if (myvar === 0) {
    // then
} else if (myvar === 1) {
    // else-if
} else if (myvar === 2) {
    // else-if
} else {
    // else
}

Eu recomendo sempre usar paraentes (eles denotam blocos de zero ou mais instruções). mas você não precisa fazer isso se uma cláusula é apenas uma única instrução (o mesmo vale para as instruções de fluxo de controle para e enquanto):

if (x < 0) return -x;

O seguinte é uma instrução de comutação. O valor de fruta decide qual caso é executado:

switch (fruit) {
    case 'banana':
        // ...
        break;
    case 'apple':
        // ...
        break;
    default:  // all other cases
        // ...
}

O operand após o caso pode ser qualquer expressão; é comparado através de === com o parâmetro de switch.

Loops

O loop for tem o seguinte formato:

for (⟦«init»⟧; ⟦«condition»⟧; ⟦«post_iteration»⟧)
    «statement»

init é executado no início do loop. a condição é verificada antes de cada iteração do loop; se for falsa, o loop é encerrado. post_iteration é executado após cada iteração do loop.

Este exemplo imprime todos os elementos da matriz arr no console:

for (var i=0; i < arr.length; i++) {
    console.log(arr[i]);
}

O loop enquanto continua a circular sobre o seu corpo enquanto a sua condição se mantém:

// Same as for loop above:
var i = 0;
while (i < arr.length) {
    console.log(arr[i]);
    i++;
}

O ciclo do do-while continua a circular sobre o seu corpo enquanto a sua condição se mantém.

do {
    // ...
} while (condition);

Em todos os circuitos:

  • A quebra deixa o ciclo.
  • Continuar inicia uma nova iteração de loop.

Funções

Uma maneira de definir uma função é através de uma declaração de função:

function add(param1, param2) {
    return param1 + param2;
}

O código anterior define uma função, adicionar, que tem dois parâmetros, param1 e param2, e retorna a soma de ambos os parâmetros.

> add(6, 1)
7
> add('a', 'b')
'ab'

Outra maneira de definir add() é atribuindo uma expressão de função a uma variável add:

var add = function (param1, param2) {
    return param1 + param2;
};

Uma expressão de função produz um valor e pode, portanto, ser usada para passar diretamente funções como argumentos para outras funções:

someOtherFunction(function (p1, p2) { ... });

As declarações de funções são levantadas

As declarações de funções são levantadas e transferidas na sua totalidade para o início do escopo atual.

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        ...
    }
}

Observe que, embora as declarações var também sejam levantadas (ver Variáveis são levantadas), as atribuições executadas por elas não são:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // ...
    };
}

Os argumentos da variável especial

Você pode chamar qualquer função em JavaScript com uma quantidade arbitrária de argumentos; a linguagem nunca se queixará.

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'

Muitos ou poucos argumentos

Vamos usar a seguinte função para explorar como muitos ou poucos parâmetros são manuseados em JavaScript (a função toArray( é mostrada em Converter argumentos para um Array):

function f(x, y) {
    console.log(x, y);
    return toArray(arguments);
}

Os parâmetros adicionais serão ignorados (exceto pelos argumentos):

> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]

Parâmetros em falta farão com que o valor não seja definido:

> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]

Parâmetros opcionais

O seguinte é um padrão comum para atribuir valores padrão aos parâmetros:

function pair(x, y) {
    x = x || 0;  // (1)
    y = y || 0;
    return [ x, y ];
}

Na linha (1), o operador de referência retorna x se for verdadeiro (não é nulo, indefinido, etc.). caso contrário, retorna o segundo operando:

> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]

Forçar a Aridade

Se quiser aplicar uma aridade (um número específico de parâmetros), pode verificar arguments.length:

function pair(x, y) {
    if (arguments.length !== 2) {
        throw new Error('Need exactly 2 arguments');
    }
    ...
}

Conversão de argumentos para uma matriz

Array-Like Objects and Generic Methods (Objetos semelhantes a matriz e métodos genéricos). Tem um comprimento de propriedade, e você pode acessar seus elementos através de índices em parênteses quadrados. Você não pode, no entanto, remover elementos ou invocar qualquer um dos métodos de matriz nele. Assim, às vezes você precisa converter argumentos para uma matriz, que é o que a seguinte função faz (é explicada em Objetos semelhantes a matriz e métodos genéricos):

function toArray(arrayLikeObject) {
    return Array.prototype.slice.call(arrayLikeObject);
}

Tratamento de exceções

A forma mais comum de tratar as exceções (ver capítulo 14) é a seguinte:

function getPerson(id) {
    if (id < 0) {
        throw new Error('ID must not be negative: '+id);
    }
    return { id: id }; // normally: retrieved from database
}

function getPersons(ids) {
    var result = [];
    ids.forEach(function (id) {
        try {
            var person = getPerson(id);
            result.push(person);
        } catch (exception) {
            console.log(exception);
        }
    });
    return result;
}

A cláusula try envolve o código crítico, e a cláusula catch é executada se uma exceção for lançada dentro da cláusula try.

> getPersons([2, -5, 137])
[Error: ID must not be negative: -5]
[ { id: 2 }, { id: 137 } ]

Modo rígido

O modo rígido (ver Modo rígido) permite mais avisos e torna o JavaScript uma linguagem mais limpa (modo não rígido às vezes é chamado de sloppy mode).

'use strict';

Você também pode ativar o modo rigoroso por função:

function functionInStrictMode() {
    'use strict';
}

Variação do âmbito de aplicação e encerramento

Em JavaScript, você declara variáveis via var antes de usá-las:

> var x;
> x
undefined
> y
ReferenceError: y is not defined

Você pode declarar e inicializar várias variáveis com uma única instrução var:

var x = 1, y = 2, z = 3;

Mas eu recomendo usar uma declaração por variável (a razão é explicada na sintaxe).

var x = 1;
var y = 2;
var z = 3;

Por causa do levantamento (ver Variaveis são levantados), geralmente é melhor declarar variáveis no início de uma função.

As variáveis têm um escopo funcional

O escopo de uma variável é sempre a função completa (em oposição ao bloco atual).

function foo() {
    var x = -512;
    if (x < 0) {  // (1)
        var tmp = -x;
        ...
    }
    console.log(tmp);  // 512
}

Podemos ver que a variável tmp não está restrita ao bloco que começa na linha (1); ela existe até o final da função.

As variáveis são levantadas

Cada declaração de variável é levantada: a declaração é movida para o início da função, mas as atribuições que ela faz permanecem colocadas.

function foo() {
    console.log(tmp); // undefined
    if (false) {
        var tmp = 3;  // (1)
    }
}

Internamente, a função anterior é executada assim:

function foo() {
    var tmp;  // hoisted declaration
    console.log(tmp);
    if (false) {
        tmp = 3;  // assignment stays put
    }
}

Fechamentos

Cada função permanece conectada às variáveis das funções que a cercam, mesmo depois de deixar o escopo em que foi criada.

function createIncrementor(start) {
    return function () {  // (1)
        start++;
        return start;
    }
}

A função que começa na linha (1) deixa o contexto em que foi criada, mas permanece conectada a uma versão ao vivo de start:

> var inc = createIncrementor(5);
> inc()
6
> inc()
7
> inc()
8

Um fechamento é uma função mais a conexão com as variáveis de seus escopos circundantes.

O modelo IIFE: introdução de um novo âmbito de aplicação

Às vezes, você quer introduzir um novo escopo de variável, por exemplo, para evitar que uma variável se torne global. Em JavaScript, você não pode usar um bloco para fazer isso; você deve usar uma função. Mas há um padrão para usar uma função de uma maneira semelhante a um bloco.

(function () {  // open IIFE
    var tmp = ...;  // not a global variable
}());  // close IIFE

Certifique-se de digitar o exemplo anterior exatamente como mostrado (exceto os comentários). Um IIFE é uma expressão de função que é chamada imediatamente após você defini-la. Dentro da função, um novo escopo existe, impedindo que o tmp se torne global. Consulte Introduzir um novo escopo através de um IIFE para obter detalhes sobre os IIFEs.

Caso de utilização do IIFE: partilha inadvertida através de fechamentos

Os fechamentos mantêm suas conexões com variáveis externas, o que às vezes não é o que você quer:

var result = [];
for (var i=0; i < 5; i++) {
    result.push(function () { return i });  // (1)
}
console.log(result[1]()); // 5 (not 1)
console.log(result[3]()); // 5 (not 3)

O valor devolvido na linha (1) é sempre o valor atual de i, não o valor que tinha quando a função foi criada. Depois que o loop é concluído, i tem o valor 5, e é por isso que todas as funções na matriz retornam esse valor.

for (var i=0; i < 5; i++) {
    (function () {
        var i2 = i; // copy current i
        result.push(function () { return i2 });
    }());
}

Objetos e Construtores

Esta seção abrange dois mecanismos básicos orientados a objetos do JavaScript: objetos individuais e construtores (que são fábricas para objetos, semelhantes a classes em outras linguagens).

Objectivos únicos

Como todos os valores, os objetos têm propriedades. Você poderia, de fato, considerar um objeto como um conjunto de propriedades, onde cada propriedade é um par (chave, valor). A chave é uma cadeia e o valor é qualquer valor JavaScript.

Em JavaScript, você pode criar diretamente objetos simples, através de object literals:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

O objeto anterior tem as propriedades name e describe. Você pode ler (get) e escrever (set) propriedades:

> jane.name  // get
'Jane'
> jane.name = 'John';  // set
> jane.newProperty = 'abc';  // property created automatically

As propriedades de valor de função como describe são chamadas de métodos.

> jane.describe()  // call method
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'

O operador in verifica se existe uma propriedade:

> 'newProperty' in jane
true
> 'foo' in jane
false

Se você ler uma propriedade que não existe, você obtém o valor indefinido.

> jane.newProperty !== undefined
true
> jane.foo !== undefined
false

O operador excluir remove uma propriedade:

> delete jane.newProperty
true
> 'newProperty' in jane
false

Chaves de propriedade arbitrárias

Uma chave de propriedade pode ser qualquer cadeia. Até agora, vimos chaves de propriedade em literais de objeto e depois do operador de ponto. No entanto, você pode usá-las dessa maneira apenas se forem identificadores (veja Identificadores e nomes de variáveis). Se você quiser usar outras cadeias como chaves, você deve citá-las em um literal de objeto e usar parênteses quadrados para obter e definir a propriedade:

> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;

Parênteses quadrados também permitem calcular a chave de uma propriedade:

> var obj = { hello: 'world' };
> var x = 'hello';

> obj[x]
'world'
> obj['hel'+'lo']
'world'

Métodos de extracção

Se você extrair um método, ele perde sua conexão com o objeto.

Como um exemplo, vamos voltar ao objeto jane anterior:

'use strict';
var jane = {
    name: 'Jane',

    describe: function () {
        return 'Person named '+this.name;
    }
};

Queremos extrair o método describe do jane, colocá-lo em uma função variável e chamá-lo.

> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined

A solução é usar o método bind() que todas as funções têm.

> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'

Funções dentro de um método

Cada função tem sua própria variável especial this. Isso é inconveniente se você aninhar uma função dentro de um método, porque você não pode acessar o método this a partir da função.

var jane = {
    name: 'Jane',
    friends: [ 'Tarzan', 'Cheeta' ],
    logHiToFriends: function () {
        'use strict';
        this.friends.forEach(function (friend) {
            // `this` is undefined here
            console.log(this.name+' says hi to '+friend);
        });
    }
}

A chamada do logHiToFriends produz um erro:

> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined

Vamos ver duas maneiras de resolver isso.

logHiToFriends: function () {
    'use strict';
    var that = this;
    this.friends.forEach(function (friend) {
        console.log(that.name+' says hi to '+friend);
    });
}

Ou, forEach tem um segundo parâmetro que permite que você forneça um valor para isso:

logHiToFriends: function () {
    'use strict';
    this.friends.forEach(function (friend) {
        console.log(this.name+' says hi to '+friend);
    }, this);
}

As expressões de função são frequentemente usadas como argumentos em chamadas de função em JavaScript.

Construtores: fábricas de objetos

Até agora, você pode pensar que os objetos JavaScript são apenas mapas de cadeias para valores, uma noção sugerida pelos literais de objetos JavaScript, que se parecem com os literais de mapa / dicionário de outras linguagens. No entanto, os objetos JavaScript também suportam um recurso que é verdadeiramente orientado a objetos: herança. Esta seção não explica completamente como funciona a herança JavaScript, mas mostra um padrão simples para você começar. Consulte o Capítulo 17 se quiser saber mais.

Além de serem funções e métodos reais, as funções desempenham outro papel no JavaScript: elas se tornam construtoras para objetos se invocadas através do novo operador.

// Set up instance data
function Point(x, y) {
    this.x = x;
    this.y = y;
}
// Methods
Point.prototype.dist = function () {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

Podemos ver que um construtor tem duas partes. Primeiro, a função Point define os dados da instância. Segundo, a propriedade Point.prototype contém um objeto com os métodos. Os primeiros dados são específicos de cada instância, enquanto os últimos dados são compartilhados entre todas as instâncias.

Para usar o ponto, nós o invocamos através do novo operador:

> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301

p é uma instância do ponto:

> p instanceof Point
true

Arrays

As matrizes são sequências de elementos que podem ser acessadas através de índices inteiros a partir de zero.

Array Literals

Os literais de matriz são úteis para criar matrizes:

> var arr = [ 'a', 'b', 'c' ];

A matriz anterior tem três elementos: as cadeias a, b e c. Você pode acessá-las através de índices inteiros:

> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]

A propriedade length indica quantos elementos tem uma matriz.

> var arr = ['a', 'b'];
> arr.length
2

> arr[arr.length] = 'c';
> arr
[ 'a', 'b', 'c' ]
> arr.length
3

> arr.length = 1;
> arr
[ 'a' ]

O operador in também funciona para matrizes:

> var arr = [ 'a', 'b', 'c' ];
> 1 in arr // is there an element at index 1?
true
> 5 in arr // is there an element at index 5?
false

Observe que os arrays são objetos e, portanto, podem ter propriedades de objeto:

> var arr = [];
> arr.foo = 123;
> arr.foo
123

Métodos de matriz

As matrizes possuem muitos métodos (ver Métodos de protótipo de matriz).

> var arr = [ 'a', 'b', 'c' ];

> arr.slice(1, 2)  // copy elements
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]

> arr.push('x')  // append an element
4
> arr
[ 'a', 'b', 'c', 'x' ]

> arr.pop()  // remove last element
'x'
> arr
[ 'a', 'b', 'c' ]

> arr.shift()  // remove first element
'a'
> arr
[ 'b', 'c' ]

> arr.unshift('x')  // prepend an element
3
> arr
[ 'x', 'b', 'c' ]

> arr.indexOf('b')  // find the index of an element
1
> arr.indexOf('y')
-1

> arr.join('-')  // all elements in a single string
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'

Iteração sobre Arrays

Existem vários métodos de matriz para iteração sobre elementos (ver Iteração (Nondestrutiva)). Os dois mais importantes são forEach e map.

forEach itera sobre uma matriz e entrega o elemento atual e seu índice para uma função:

[ 'a', 'b', 'c' ].forEach(
    function (elem, index) {  // (1)
        console.log(index + '. ' + elem);
    });

O código anterior produz a seguinte saída:

0. a
1. b
2. c

Observe que a função na linha (1) é livre para ignorar argumentos.

mapa cria uma nova matriz aplicando uma função a cada elemento de uma matriz existente:

> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]

Expressões Regulares

O JavaScript tem suporte incorporado para expressões regulares.

/^abc$/
/[A-Za-z0-9]+/

Teste de método: Existe correspondência?

> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false

Método exec ((): Grupos de correspondência e captura

> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]

A matriz retornada contém a correspondência completa no índice 0, a captura do primeiro grupo no índice 1, e assim por diante.

Método substituir ((): Pesquisar e substituir

> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'

O primeiro parâmetro de substituir deve ser uma expressão regular com uma bandeira /g; caso contrário, apenas a primeira ocorrência é substituída.

Matemática

A matemática é um objeto com funções aritméticas.

> Math.abs(-2)
2

> Math.pow(3, 2)  // 3 to the power of 2
9

> Math.max(2, -1, 5)
5

> Math.round(1.9)
2

> Math.PI  // pre-defined constant for π
3.141592653589793

> Math.cos(Math.PI)  // compute the cosine for 180°
-1

Mais.