Esta seção dá um pouco de conhecimento sobre JavaScript para ajudá-lo a entender por que é assim.
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:
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).
Esta seção explica os princípios sintáticos básicos do JavaScript.
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:
Para entender a sintaxe do JavaScript, você deve saber que ele tem duas principais categorias sintáticas: declarações e expressões:
var foo;
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
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.
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.
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.
*/
As variáveis em JavaScript são declaradas antes de serem usadas:
var foo; // declare variable `foo`
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`
Existem operadores de atribuição compostos como +=. As duas atribuições seguintes são equivalentes:
x += 1;
x = x + 1;
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):
Os seguintes três identificadores não são palavras reservadas, mas devem ser tratados como se fossem:
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.
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
> 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
O JavaScript faz uma distinção um pouco arbitrária entre valores:
> 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.
Os seguintes são todos os valores primitivos (ou primitivos para abreviar):
Os primitivos têm as seguintes características:
O
> 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.)
Todos os valores não-primitivos são objetos.
{
firstName: 'Jane',
lastName: 'Doe'
}
O objeto anterior tem duas propriedades: o valor da propriedade firstName é
[ 'apple', 'banana', 'cherry' ]
A matriz anterior tem três elementos que podem ser acessados através de índices numéricos.
/^a+b+$/
Os objectos têm as seguintes características:
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
Normalmente, você pode alterar, adicionar e remover propriedades livremente (veja Objetos únicos):
> var obj = {};
> obj.foo = 123; // add property `foo`
> obj.foo
123
A maioria das linguagens de programação tem valores que denotam informações faltantes.
> 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
undefined e null não têm propriedades, nem mesmo métodos padrão como toString().
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) {
...
}
false, 0, NaN e
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
> 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:
typeof null retornar
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
O tipo booleano primitivo compreende os valores true e false. Os seguintes operadores produzem booleanos:
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:
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
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.
Se o primeiro operando for falso, devolva-o. Caso contrário, devolva o segundo operando:
> NaN && 'abc'
NaN
> 123 && 'abc'
'abc'
Se o primeiro operando for verdadeiro, devolva-o. Caso contrário, devolva o segundo operando:
> 'abc' || 123
'abc'
> '' || 123
123
O JavaScript tem dois tipos de igualdade:
A igualdade normal considera (também) muitos valores iguais (os detalhes são explicados na igualdade normal (==,!=)), o que pode esconder erros.
Todos os números em JavaScript são com ponto flutuante:
> 1 === 1.0
true
Os números especiais incluem os seguintes:
NaN (
> 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).
O JavaScript tem os seguintes operadores aritméticos (ver Operadores aritméticos):
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).
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.
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.'
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
Condicionais e loops em JavaScript são introduzidos nas seções seguintes.
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
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:
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 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 () {
// ...
};
}
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'
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
[]
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 ]
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');
}
...
}
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);
}
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 } ]
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
'use strict';
Você também pode ativar o modo rigoroso por função:
function functionInStrictMode() {
'use strict';
}
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.
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.
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
}
}
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.
À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.
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 });
}());
}
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).
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 (
> 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
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'
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'
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.
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
As matrizes são sequências de elementos que podem ser acessadas através de índices inteiros a partir de zero.
Os literais de matriz são úteis para criar matrizes:
> var arr = [ 'a', 'b', 'c' ];
A matriz anterior tem três elementos: as cadeias
> 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
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'
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 ]
O JavaScript tem suporte incorporado para expressões regulares.
/^abc$/
/[A-Za-z0-9]+/
> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false
> /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.
> '<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.
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