mozilla
Os seus resultados da pesquisa

    Uma reintrodução ao JavaScript (Tutorial de JS)

    Introdução

    Por que uma reintrodução? Porque JavaScript é conhecido como a mais incompreendida linguagem de programação do mundo. Embora muitas vezes ridicularizada como um brinquedo, por baixo de sua simplicidade enganosa estão alguns recursos poderosos da linguagem, que agora é usado por um número incrível de aplicações de alto nível, mostrando que o conhecimento mais profundo desta tecnologia é uma habilidade importante para qualquer desenvolvedor web ou mobile.

    É sempre bom começar com a história da linguagem. A JavaScript foi criada em 1995 por Brendan Eich, um engenheiro da Netscape, e lançada pela primeira vez com o Netscape 2 no início de 1996. Foi inicialmente chamada de LiveScript, mas logo foi rebatizada, em uma decisão de marketing malfeita, para tentar crescer sobre a popularidade da linguagem Java da Sun Microsystem - apesar de os dois terem muito pouco em comum. Esta tem sido uma fonte de confusão desde então.

    A Microsoft lançou uma versão compatível com a maior parte da linguagem, chamado de JScript, junto com o IE, três meses mais tarde. A Netscape apresentou a linguagem a Ecma International, uma organização europeia de normalização, o que resultou na primeira edição do padrão ECMAScript em 1997. O padrão recebeu uma atualização significativa com o ECMAScript Edição 3 em 1999, e manteve-se praticamente estável desde então. A quarta edição foi abandonada, devido a diferenças políticas relativas à complexidade da linguagem. Muitas partes da quarta edição formam a base da nova edição ECMAScript 5, publicado em dezembro de 2009.

    Esta estabilidade foi uma grande notícia para os desenvolvedores, pois isto proporcionou que várias implementações em JavaScript tivessem muito tempo para se firmar. Eu vou focar quase exclusivamente no dialeto da edição 3. Para que seja facil se familiarizar, vou utilizar o termo JavaScript por todo o texto.

    Diferentemente da maioria das linguagens de programação , a linguagem JavaScript não possuio o conceito de entrada e saída. Ela é projetada para funcionar como uma linguagem de script em um ambiente de terceiros, e cabe ao ambiente fornecer mecanismos para a comunicação com o mundo exterior. O ambiente de terceiros (hospedeiro) mais comum é o navegador, mas interpretadores JavaScript também pode ser encontrados no Adobe Acrobat, Photoshop, imagens SVG, Widget engine do Yahoo! , bem como ambientes de servidor, como Node.js. No entanto, a lista aqui apresentada das áreas nas quais a JavaScript é utilizada é apenas o começo. Ele também inclui bancos de dados NoSQL, como o código-fonte aberto Apache CouchDB, computadores embarcados, ou ambientes de trabalho completos, como o GNOME (um dos GUIs mais populares para os sistemas operacionais GNU / Linux) .

    Visão Geral


    A JavaScript é uma linguagem dinâmica orientada a objetos; tem tipos e operadores, objetos e métodos. Sua sintaxe vem das linguagens Java e C, por isso tantas estruturas dessas linguagens se aplicam a JavaScript também. Uma das principais diferenças é que o JavaScript não tem classes; em vez disso, a funcionalidade de classe é realizada por protótipos de objetos. A outra diferença principal é que as funções são objetos, dando as funções a capacidade para armazenar código executável e serem passadas como parametro para qualquer outro objeto.

    Vamos começar pelo bloco de construção de qualquer linguagem: os tipos. Programas JavaScript manipulam valores, e esses valores todos pertencem a um tipo. Tipos de JavaScript são :

    •     números
    •     strings
    •     booleanos
    •     funções
    •     objetos

    ... Ops, e o "indefinido" e o "nulo"- , que parecem um pouco estranhos. E arrays (vetores), que são um tipo especial de objeto. E as datas e expressões regulares, que são objetos que você ganha de graça. E para ser tecnicamente preciso, as funções são apenas um tipo especial de objeto. Assim, a lista de tipos se parece mais com isto:

    • números (numbers)
    • strings (strings)
    • booleanos (booleans)
    • objetos (objects)
    • funções (functions)
    • vetores (arrays)
    • datas (dates)
    • expressoes regulares (regexp)
    • nulo (null)
    • indefinido (undefined)

    E existem também alguns tipos para erros. As coisas são muito mais fáceis se ficarmos com a primeira lista, no entanto.

    Números

    Números em JavaScript são "valores de precisão dupla no formato IEEE 754", de acordo com a especificação. Isto tem algumas consequências interessantes. Não existe essa coisa de inteiro em JavaScript, então você deve ser um pouco cuidadoso com seus cálculos se você está acostumado a matemática em C ou Java. Cuidado com coisas como:

    0.1 + 0.2 == 0.30000000000000004
    

    Na prática, valores inteiros são tratados como inteiros de 32 bits (e são armazenados dessa forma em algumas implementações do navegador), que podem ser importantes para as operações bit a bit. Para mais detalhes, consulte The Complete JavaScript Number Reference.

    Os operadores numéricos padrões são suportados, incluindo adição, subtração, módulo (ou resto) aritmético e assim por diante. Há também um objeto embutido que eu esqueci de mencionar mais cedo chamado Math para manipular funções e constantes matemáticas mais avançadas:

    Math.sin(3.5);
    var d = Math.PI * r * r;
    

    Você pode converter uma string em um integer usando a função embutida parseInt(). Ela tem um segundo parâmetro opcional para a base da conversão, parâmetro esse que você deveria sempre prover:

    > parseInt("123", 10)
    123
    > parseInt("010", 10)
    10
    

    Se você não prover a base, você pode ter um resultado surpreendente:

    > parseInt("010")
    8
    

    Isso acontece pois a função parseInt() decidiu tratar a string como octal devido ao leading 0.

    Se você quiser converter um número binário em um inteiro, basta mudar a base:

    > parseInt("11", 2)
    3
    

    Similarmente, você pode fazer a conversão de números de ponto flutuante usando a função embutida parseFloat() que usa a base 10 sempre, ao contrário de seu primo parseInt().

    Você também pode usar o operador unário + para converter valores em números:

    > + "42"
    42 
    

    Um valor especial chamado NaN (sigla de "Not a Number ou Não é Número") é retornado se a string não é um valor numérico:

    > parseInt("hello", 10)
    NaN
    

    NaN é tóxico: Se você provê-lo como uma entreda para qualquer operação matemática o resultado também será NaN:

    > NaN + 5
    NaN
    

    Você pode testar se é NaN usando a função embutida isNaN():

    > isNaN(NaN)
    true
    

    JavaScript também tem os valores especiais Infinity e -Infinity:

    > 1 / 0
    Infinity
    > -1 / 0
    -Infinity
    

    Você pode testar se o valor é Infinity, -Infinity e NaN usando a função embutida isFinite():

    > isFinite(1/0)
    false
    > isFinite(-Infinite)
    false
    > isFinite(NaN)
    false
    
    Nota: As funções parseInt() e parseFloat() fazem a conversão da string até alcançarem um caracter que não é válido para o formato numérico especificado, então elas retornam o número convertido até aquele ponto. Contudo, o operador "+" simplesmente converte a string em NaN  se tiver algum caracter inválido nela. Apenas tente por si mesmo converter a string "10.2abc" usando cada um desses métodos no console e entenderá melhor essas diferenças.

    Strings

    Strings em JavaScript são sequencias de caracteres. Para ser mais exato, elas são sequencias de Unicode characters, em que cada um deles é representado por um número de 16-bits. Isso deveria ser uma notícia bem-vinda para aqueles que tiveram que lidar com internacionalização.

    Se você quiser representar um único caracter, você só tem que usar uma string de tamanho 1.

    Para obter o tamanho de uma string, acesse sua propriedade length:

    > "hello".length
    5
    

    Essa é nossa primeira pincelada com objetos JavaScript! Eu mencionei que strings também são objetos? De modo que elas têm métodos:

    > "hello".charAt(0)
    h
    > "hello, world".replace("hello", "goodbye")
    goodbye, world
    > "hello".toUpperCase()
    HELLO
    

    Outros tipos

    No JavaScript há distinção entre null, que é um objeto do tipo 'object' para indicar deliberadamente uma ausência de valor, do undefined, que é um objeto do tipo 'undefined' para indicar um valor não inicializado — isto é, que um valor não foi atribuído ainda. Vamos falar sobre variáveis depois, mas em JavaScript é possível declarar uma variável sem atribuir um valor para a mesma. Se você fizer isso, a variável será do tipo undefined.

    JavaScript tem um tipo boolean, ao qual são possíveis os valores true e false (ambos são palavras-chave). Qualquer valor pode ser convertido em um boolean, de acordo com as seguintes regras:

    1. false, 0, a string vazia(""), NaN, null, e undefined todos tornam-se false
    2. todos os outros valores tornam-se true

    Você pode fazer essa conversão explicitamente usando a função Boolean():

    > Boolean("")
    false
    > Boolean(234)
    true
    

    Contudo, essa é uma necessidade rara, uma vez que JavaScript silenciosamente fará essa conversão quando for esperado um boolean, como em uma instrução if. Por isso, algumas vezes falamos simplesmente em "valores true" e "valores false" nos referindo a valores que se tornaram true e false, respectivamente, quando convertidos em boolean. Alternativamente, esses valores podem ser chamados de "truthy" e "falsy", respectivamente.

    Operações booleanas como && (and lógico), || (or lógico), e ! (not lógico) são suportadas.

    Variáveis

    Novas variáveis em JavaScript são declaradas usando a palavra-chave var:

    var a;
    var name = "simon";
    

    Se você declarar uma variável sem atribuir qualquer valor a ela, seu tipo será undefined

    Uma diferença importante de outras linguagens como Java é que em JavaScript, blocos não tem escopo; somente funções tem escopo. De modo que se uma variável é definida usando var numa instrução composta(por exemplo dentro de uma estrutura de controle if), ela será visível na inteira função.

    Operadores

    Operadores numéricos de JavaScript's são +, -, *, / e % - que é o operador resto. Valores são atribuídos usando =, e temos também as instruções de atribuição compostas, como += e -=. Essas são o mesmo que x = x operador y.

    x += 5
    x = x + 5
    

    Você pode usar ++ e -- para incrementar ou decrementar respectivamente. Eles podem ser usados como operadores tanto antes como depois.

    O operador + também faz concatenação de string:

    > "hello" + " world"
    hello world
    

    Se você adicionar uma sttring a uma número (ou outro valor) tudo será convertido em uma string primeiro. Isso talvez seja uma pegadinha para você:

    > "3" + 4 + 5
    345
    > 3 + 4 + "5"
    75
    

    Adicionar uma string em branco a algo é uma maneira melhor de fazer a conversão.

    Comparações em JavaScript podem ser feitas usando <, >, <= e >=. Isso funciona tanto para strings como para números. A igualdade é um pouco menos simples. O operador igual-duplo faz a coersão de tipo se você colocar tipos diferentes, algumas vezes com resultados interessantes:

    > "dog" == "dog"
    true
    > 1 == true
    true
    

    Para evitar a coersão de tipo, use o operador igual-triplo:

    > 1 === true
    false
    > true === true
    true
    

    Temos também os operadores != e !== .

    JavaScript também tem operações de bit-a-bit. Se quiser usá-las, elas estarão lá.

    Estruturas de Controle

    JavaScript tem um conjunto de estruturas de controle similar à outras linguagens na família do C. Instruções condicionais são suportadas pelo if e else; você pode encadeá-los se quiser:

    var name = "kittens";
    if (name == "puppies") {
      name += "!";
    } else if (name == "kittens") {
      name += "!!";
    } else {
      name = "!" + name;
    }
    name == "kittens!!"
    

    JavaScript tem as estruturas de repetição com os laços while e do-while. O primeiro é bom para repetições básicas; o segundo é para os casos em que você queira que o corpo da repetição seja executado pelo menos uma vez:

    while (true) {
      // an infinite loop!
    }
    
    var input;
    do {
      input = get_input();
    } while (inputIsNotValid(input))
    

    O laço for do JavaScript é o mesmo que no C e Java: ele lhe permite prover as informações para o seu laço em uma única linha.

    for (var i = 0; i < 5; i++) {
      // Will execute 5 times
    }
    

    Os operadores && e ||  usam a lógica de curto-circuito, o que quer dizer que ser o segundo operando executado dependerá do primeiro operando. Isso é útil para checagem de objetos null antes de acessar seus atributos:

    var name = o && o.getName();
    

    Ou para configuração de valores-padrões:

    var name = otherName || "default";
    

    JavaScript tem um operador ternário para expressões condicionais:

    var allowed = (age > 18) ? "yes" : "no";
    

    A instrução switch pode ser usada para múltiplas ramificações baseadas em um número ou uma string:

    switch(action) {
        case 'draw':
            drawit();
            break;
        case 'eat':
            eatit();
            break;
        default:
            donothing();
    }
    

    Se você não adicionar a instrução break, a execução irá "cair" no próximo nível. Isso é algo que raramente vai querer fazer — de fato vale mais a pena colocar um comentário especificando essa "queda" para o próximo nível, pois isso o ajudará na hora de fazer a depuração de seu código:

    switch(a) {
        case 1: // queda
        case 2:
            eatit();
            break;
        default:
            donothing();
    }
    

    A cláusula default é opicional. Se quiser, pode colocar expressões tanto no switch como nos cases; Comparações acontecem entre os dois usando o operador ===:

    switch(1 + 3) {
        case 2 + 2:
            yay();
            break;
        default:
            neverhappens();
    }
    

    Objetos

    Objetos JavaScript são simplesmente coleções de pares nome-valor. Como tal, eles são similares à:

    • Dictionários em Python
    • Hashes em Perl e Ruby
    • Hash tables em C e C++
    • HashMaps em Java
    • Vetores associativos em PHP

    O fato dessa estrutura de dados ser tão usada é uma prova de sua versatilidade. Por tudo em JavaScript ser um objeto, qualquer programa JavaScript naturalmente envolve uma grande quantidade de procuras em tabelas hash. Isso é algo bom, pois elas são bem rápidas!

    A parte "name" é uma string JavaScript, enquanto o valor pode ser qualquer valor JavaScript — incluindo mais objetos. Isso permite que você construa estruturas de dados de qualquer complexidade.

    Há basicamente duas formas de criar um objeto vazio:

    var obj = new Object();
    

    e:

    var obj = {};
    

    Elas são semanticamente equivalentes; a segunda é chamada de sintaxe de objeto literal e é mais conveniente. Essa sintaxe é também o coração do formato JSON e deveria ser sempre preferida.

    Uma vez criada, as propriedades de um objeto podem novamente ser acessadas de uma das seguintes formas:

    obj.name = "Simon";
    var name = obj.name;
    

    E...

    obj["name"] = "Simon";
    var name = obj["name"];
    

    Elas são semanticamente equivalentes. A segunda forma tem a vantagem que o nome da propriedade é provida como uma string, o que significa que pode ser calculada em tempo de execução, embora o uso deste método evite alguns mecanismos do JavaScript e que a otimização de minificação seja aplicada. També mpode ser usada para atribuir e obter propriedades com nomes que são palavras-reservadas:

    obj.for = "Simon"; // Erro de sintaxe, pois 'for' é uma palavra reservada
    obj["for"] = "Simon"; // Funciona bem
    

    A sintaxe de objeto literal pode ser usada para inicializar completamente um objeto:

    var obj = {
        name: "Carrot",
        "for": "Max",
        details: {
            color: "orange",
            size: 12
        }
    }
    

    O acesso aos atributos podem ser encadeados:

    > obj.details.color
    orange
    > obj["details"]["size"]
    12
    

    Vetores

    Vetores em JavaScript são, de fato, um tipo especial de objeto. Eles funcionam de forma muito similar à objetos regulares (propriedades numéricas podem naturalmente ser acessadas somente usando a sintaxe [], colchetes ), porém eles tem uma propriedade mágica chamada 'length'. Ela sempre é o maior índice de um vetor mais 1.

    A velha forma de criar um vetor é a seguinte:

    > var a = new Array();
    > a[0] = "dog";
    > a[1] = "cat";
    > a[2] = "hen";
    > a.length
    3
    

    A notação mais comum é usar um vetor literal:

    > var a = ["dog", "cat", "hen"];
    > a.length
    3
    

    Deixar uma vírgula à direita no final de um vetor literal gerará inconsistência entre os navegadores, portanto não faça isso.

    Note que array.length não é necessariamente o número de itens em um vetor. Considere o seguinte:

    > var a = ["dog", "cat", "hen"];
    > a[100] = "fox";
    > a.length
    101
    

    Lembre-se — o tamanho de um vetor é o maior índice mais 1.

    Se você fizer referência a um índice de vetor inexistente, obterá um undefined:

    > typeof a[90]
    undefined
    

    Se levar em conta o exposto, você pode iterar sobre um vetor da seguinte forma:

    for (var i = 0; i < a.length; i++) {
        // Faça algo com a[i]
    }
    

    Isso é um pouco ineficaz visto que você está procurando a propriedade length uma vez a cada laço. Uma melhoria seria:

    for (var i = 0, len = a.length; i < len; i++) {
        // Faça algo com a[i]
    }
    

    Uma forma melhor ainda seria:

    for (var i = 0, item; item = a[i++];) {
        // Faça algo com item
    }
    

    Aqui nós estamos declarando duas variáveis. A atribuição na parte do meio do laço for é também testada — se for verdadeira, o laço continuara. Uma vez que o i é incrementado toda vez, os itens do array serão atribuiídos a variável item sequencialmente. O laço para quando item "falsy" é encontrado (tal como o undefined).

    Note que esse truque só deveria ser usado em vetores que você sabe não conter valores "falsy" (vetores de objeto ou nós DOM por examplo). Se você iterar sobre dados numéricos que possam ter o 0 ou sobre dados string que possam ter uma string vazia, você deveria usar a segunda forma como alternativa.

    Uma outra forma de iterar é usar o laço for...in. Note que se alguém adicionou novas propriedades ao Array.prototype, elas também podem ser iteradas usando este laço:

    for (var i in a) {
      // Do something with a[i]
    }
    

    Se quiser adicionar um item a um vetor, simplesmente faça dessa assim:

    a[a.length] = item;                 // é o mesmo que a.push(item);
    

    Vetores vem com vários métodos:

    Nome do método Descrição
    a.toString() Retorna uma string com o toString()de cada elemento separado por vírgulas.
    a.toLocaleString() Retorna uma string com o toLocaleString()de cada elemento separado por vírgulas.
    a.concat(item[, itemN]) Retorna um novo vetor com os itens adicionados nele.
    a.join(sep) Converte um vetor em uma string com os valores do vetor separados pelo valor do parâmetro sep
    a.pop() Remove e retorna o último item.
    a.push(item[, itemN]) Push adiciona um ou mais itens ao final.
    a.reverse() Reverte o vetor
    a.shift() Remove e retorna o primeiro item
    a.slice(start, end) Retorna um sub-vetor.
    a.sort([cmpfn]) Prover uma função opcional para fazer a comparação.
    a.splice(start, delcount[, itemN]) Permite que você modifique um vetor por apagar uma seção e substituí-lo com mais itens.
    a.unshift([item]) Acrescenta itens ao começo do vetor.

    Funções

    Junto com objetos, funções são os componentes principais para o entendimento do JavaScript. A função mais básica não poderia ser mais simples:

    function add(x, y) {
        var total = x + y;
        return total;
    }
    

    Isso demonstra tudo o que há para se saber sobre funções básicas. Uma função JavaScript pode ter 0 ou mais parâmetros declarados. O corpo da função pode conter tantas instruções quantas quiser e pode declarar suas próprias variáveis que são de escopo local àquela função. A instrução return pode ser usada para retornar um valor em qualquer parte da função, finalizando a função. Se nenhuma instrução de retorno for usada(ou um retorno vazio sem valor), o JavaScript retorna undefined.

    Os parâmetros nomeados se parecem mais com orientações do que com outra coisa. Você pode chamar a função sem passar o parâmetro esperado, nesse caso eles receberão o valor undefined.

    > add()
    NaN // Você não pode executar adição em undefined
    

    Você também pode passar mais argumentos do que a função está esperando:

    > add(2, 3, 4)
    5 // adicionado os dois primeiros; 4 foi ignorado
    

    Pode parecer um pouco bobo, mas no corpo da função você tem acesso a uma variável adicional chamada arguments, que é um objeto parecido com um vetor que contém todos os valores passados para a função. Vamos rescrever a função add para tomarmos tantos valores quanto quisermos:

    function add() {
        var sum = 0;
        for (var i = 0, j = arguments.length; i < j; i++) {
            sum += arguments[i];
        }
        return sum;
    }
    
    > add(2, 3, 4, 5)
    14
    

    Isso realmente não é muito mais útil do que escrever 2 + 3 + 4 + 5. Vamos criar uma função para calcular média:

    function avg() {
        var sum = 0;
        for (var i = 0, j = arguments.length; i < j; i++) {
            sum += arguments[i];
        }
        return sum / arguments.length;
    }
    > avg(2, 3, 4, 5)
    3.5
    

    Isso é muito útil, mas introduz um novo problema. A função avg() precisa de uma lista de argumentos separados por vírgula — mas e se o que quiser for procurar a médica de um vetor? Você poderia apenas rescrever a função da seguinte forma:

    function avgArray(arr) {
        var sum = 0;
        for (var i = 0, j = arr.length; i < j; i++) {
            sum += arr[i];
        }
        return sum / arr.length;
    }
    > avgArray([2, 3, 4, 5])
    3.5
    

    Porém, seria legal se pudéssemos reusar a função que já tínhamos criado. Felizmente, JavaScript lhe permite chamar a função, e chamá-la com um conjunto arbitrário de argumentos, usando o método apply() presente em qualquer objeto função.

    > avg.apply(null, [2, 3, 4, 5])
    3.5
    

    O segundo argumento do apply() é o vetor para usar como argumento; o primeiro será discutido mais tarde. Isso enfatiza o fato que funções também são objetos.

    JavaScript lhe permite criar funções anônimas.

    var avg = function() {
        var sum = 0;
        for (var i = 0, j = arguments.length; i < j; i++) {
            sum += arguments[i];
        }
        return sum / arguments.length;
    }
    

    Isso é semanticamente equivalente a forma function avg(). É extremamente poderoso como ele lhe permite colocar a inteira definição de uma função em qualquer lugar que você normalmente poria uma expressão. Isso lhe permite toda sorte de truques engenhosos. Aqui está uma maneira de "esconder" algumas variáveis locais — como escopo de bloco em C:

    > var a = 1;
    > var b = 2;
    > (function() {
        var b = 3;
        a += b;
    })();
    > a
    4
    > b
    2
    

    JavaScript lhe permite chamar funções recursivamente. Isso é particularmente útil quando estamos lidando com estruturas de árvore, como quando estavamos navegando no DOM.

    function countChars(elm) {
        if (elm.nodeType == 3) { // TEXT_NODE
            return elm.nodeValue.length;
        }
        var count = 0;
        for (var i = 0, child; child = elm.childNodes[i]; i++) {
            count += countChars(child);
        }
        return count;
    }
    

    Isso destaca um problema potencial com funções anônimas: Como chamá-las recursivamente se elas não tem um nome? JavaScript lhe permite nomear expressões de função para isso. Você pode usar EFIIs nomeadas(Expressões Funcionais Imediatamente Invocadas), conforme abaixo:

    var charsInBody = (function counter(elm) {
        if (elm.nodeType == 3) { // TEXT_NODE
            return elm.nodeValue.length;
        }
        var count = 0;
        for (var i = 0, child; child = elm.childNodes[i]; i++) {
            count += counter(child);
        }
        return count;
    })(document.body);
    

    O nome provido para a função anônima conforme acima só é (ou no mínimo só deveria ser) visível ao escopo da própria função. Isso tanto permite que mais otimizações sejam feitas pela engine como deixa o código mais legível.

    Objetos Personalizados

    Nota: Para uma discursão mais detalhada de programação orientada a objetos em JavaScript, veja Introdução a JavaScript Orientado a Objeto.

    Na clássica Programação Orientada a Objetos, objetos são coleções de dados e métodos que operam sobre esses dados. JavaScript é uma linguagem baseada em protótipos que não contém a estrutura de classe, como tem em C++ e Java. (Algumas vezes isso é algo confuso para o programador acostumado a linguagens com estrutura de classe) Em vez disso, JavaScript usa funções como classes. Vamos considerar um objeto pessoa com os campos primeiro e último nome. Há duas formas em que o nome talvez possa ser exibido: como "primeiro nome segundo nome" ou como "último nome, primeiro nome". Usando as funções e objetos que discutimos anteriormente, aqui está uma forma de fazer isso:

    function makePerson(first, last) {
        return {
            first: first,
            last: last
        }
    }
    function personFullName(person) {
        return person.first + ' ' + person.last;
    }
    function personFullNameReversed(person) {
        return person.last + ', ' + person.first
    }
    > s = makePerson("Simon", "Willison");
    > personFullName(s)
    Simon Willison
    > personFullNameReversed(s)
    Willison, Simon
    

    Isso funciona, mas é muito feito. Você termina com dúzias de funções em seu escopo global. O que nós realmente precisamos é uma forma de anexar uma função a um objeto. Visto que funções são objetos, isso é fácil:

    function makePerson(first, last) {
        return {
            first: first,
            last: last,
            fullName: function() {
                return this.first + ' ' + this.last;
            },
            fullNameReversed: function() {
                return this.last + ', ' + this.first;
            }
        }
    }
    > s = makePerson("Simon", "Willison")
    > s.fullName()
    Simon Willison
    > s.fullNameReversed()
    Willison, Simon
    

    Há algo aqui que não havíamos visto anteriormente: a palavra-chave 'this'. Usada dentro de uma função, 'this' refere-se ao objeto corrente. O que aquilo de fato significa é especificado pelo modo em que você chamou aquela função. Se você chamou-a usando notação ponto ou notação colchete em um objeto, aquele objeto torna-se 'this'. Se a notação ponto não foi usada pela chamada, 'this' refere-se ao objeto global. Isso é uma frequente causa de erros. Por exemplo:

    > s = makePerson("Simon", "Willison")
    > var fullName = s.fullName;
    > fullName()
    undefined undefined
    

    Quando chamamos fullName(), 'this' está ligado ao objeto global. Visto que não há variáveis globais chamadas first ou last obtemos undefined para cada um.

    Podemos tirar vantagem da palavra chave 'this' para melhorar nossa função makePerson:

    function Person(first, last) {
        this.first = first;
        this.last = last;
        this.fullName = function() {
            return this.first + ' ' + this.last;
        }
        this.fullNameReversed = function() {
            return this.last + ', ' + this.first;
        }
    }
    var s = new Person("Simon", "Willison");
    

    Nós introduzimos uma outra palavra-chave: 'new'. new é fortemente relacionada a 'this'. O que ele faz é criar um novo objeto vazio, e então chamar a função especificada com 'this' para atribui aquele novo objeto. Funções que são desenhadas para ser chamadas pelo 'new' são chamadas de funções construtoras. Uma prática comum é capitular  essas funções como um lembrete de chamá-las com o new.

    Our person objects are getting better, but there are still some ugly edges to them. Every time we create a person object we are creating two brand new function objects within it — wouldn't it be better if this code was shared?

    function personFullName() {
        return this.first + ' ' + this.last;
    }
    function personFullNameReversed() {
        return this.last + ', ' + this.first;
    }
    function Person(first, last) {
        this.first = first;
        this.last = last;
        this.fullName = personFullName;
        this.fullNameReversed = personFullNameReversed;
    }
    

    That's better: we are creating the method functions only once, and assigning references to them inside the constructor. Can we do any better than that? The answer is yes:

    function Person(first, last) {
        this.first = first;
        this.last = last;
    }
    Person.prototype.fullName = function() {
        return this.first + ' ' + this.last;
    }
    Person.prototype.fullNameReversed = function() {
        return this.last + ', ' + this.first;
    }
    

    Person.prototype is an object shared by all instances of Person. It forms part of a lookup chain (that has a special name, "prototype chain"): any time you attempt to access a property of Person that isn't set, JavaScript will check Person.prototype to see if that property exists there instead. As a result, anything assigned to Person.prototype becomes available to all instances of that constructor via the this object.

    This is an incredibly powerful tool. JavaScript lets you modify something's prototype at any time in your program, which means you can add extra methods to existing objects at runtime:

    > s = new Person("Simon", "Willison");
    > s.firstNameCaps();
    TypeError on line 1: s.firstNameCaps is not a function
    > Person.prototype.firstNameCaps = function() {
        return this.first.toUpperCase()
    }
    > s.firstNameCaps()
    SIMON
    

    Interestingly, you can also add things to the prototype of built-in JavaScript objects. Let's add a method to String that returns that string in reverse:

    > var s = "Simon";
    > s.reversed()
    TypeError on line 1: s.reversed is not a function
    > String.prototype.reversed = function() {
        var r = "";
        for (var i = this.length - 1; i >= 0; i--) {
            r += this[i];
        }
        return r;
    }
    > s.reversed()
    nomiS
    

    Our new method even works on string literals!

    > "This can now be reversed".reversed()
    desrever eb won nac sihT
    

    As I mentioned before, the prototype forms part of a chain. The root of that chain is Object.prototype, whose methods include toString() — it is this method that is called when you try to represent an object as a string. This is useful for debugging our Person objects:

    > var s = new Person("Simon", "Willison");
    > s
    [object Object]
    > Person.prototype.toString = function() {
        return '<Person: ' + this.fullName() + '>';
    }
    > s
    <Person: Simon Willison>
    

    Remember how avg.apply() had a null first argument? We can revisit that now. The first argument to apply() is the object that should be treated as 'this'. For example, here's a trivial implementation of 'new':

    function trivialNew(constructor) {
        var o = {}; // Create an object
        constructor.apply(o, arguments);
        return o;
    }
    

    This isn't an exact replica of new as it doesn't set up the prototype chain. apply() is difficult to illustrate — it's not something you use very often, but it's useful to know about.

    apply() has a sister function named call, which again lets you set 'this' but takes an expanded argument list as opposed to an array.

    function lastNameCaps() {
        return this.last.toUpperCase();
    }
    var s = new Person("Simon", "Willison");
    lastNameCaps.call(s);
    // Is the same as:
    s.lastNameCaps = lastNameCaps;
    s.lastNameCaps();
    

    Inner functions

    JavaScript function declarations are allowed inside other functions. We've seen this once before, with an earlier makePerson() function. An important detail of nested functions in JavaScript is that they can access variables in their parent function's scope:

    function betterExampleNeeded() {
        var a = 1;
        function oneMoreThanA() {
            return a + 1;
        }
        return oneMoreThanA();
    }
    

    This provides a great deal of utility in writing more maintainable code. If a function relies on one or two other functions that are not useful to any other part of your code, you can nest those utility functions inside the function that will be called from elsewhere. This keeps the number of functions that are in the global scope down, which is always a good thing.

    This is also a great counter to the lure of global variables. When writing complex code it is often tempting to use global variables to share values between multiple functions — which leads to code that is hard to maintain. Nested functions can share variables in their parent, so you can use that mechanism to couple functions together when it makes sense without polluting your global namespace — 'local globals' if you like. This technique should be used with caution, but it's a useful ability to have.

    Closures

    This leads us to one of the most powerful abstractions that JavaScript has to offer — but also the most potentially confusing. What does this do?

    function makeAdder(a) {
        return function(b) {
            return a + b;
        }
    }
    x = makeAdder(5);
    y = makeAdder(20);
    x(6)
    ?
    y(7)
    ?
    

    The name of the makeAdder function should give it away: it creates new 'adder' functions, which when called with one argument add it to the argument that they were created with.

    What's happening here is pretty much the same as was happening with the inner functions earlier on: a function defined inside another function has access to the outer function's variables. The only difference here is that the outer function has returned, and hence common sense would seem to dictate that its local variables no longer exist. But they do still exist — otherwise the adder functions would be unable to work. What's more, there are two different "copies" of makeAdder's local variables — one in which a is 5 and one in which a is 20. So the result of those function calls is as follows:

    x(6) // returns 11
    y(7) // returns 27
    

    Here's what's actually happening. Whenever JavaScript executes a function, a 'scope' object is created to hold the local variables created within that function. It is initialised with any variables passed in as function parameters. This is similar to the global object that all global variables and functions live in, but with a couple of important differences: firstly, a brand new scope object is created every time a function starts executing, and secondly, unlike the global object (which in browsers is accessible as window) these scope objects cannot be directly accessed from your JavaScript code. There is no mechanism for iterating over the properties of the current scope object for example.

    So when makeAdder is called, a scope object is created with one property: a, which is the argument passed to the makeAdder function. makeAdder then returns a newly created function. Normally JavaScript's garbage collector would clean up the scope object created for makeAdder at this point, but the returned function maintains a reference back to that scope object. As a result, the scope object will not be garbage collected until there are no more references to the function object that makeAdder returned.

    Scope objects form a chain called the scope chain, similar to the prototype chain used by JavaScript's object system.

    A closure is the combination of a function and the scope object in which it was created.

    Closures let you save state — as such, they can often be used in place of objects.

    Memory leaks

    An unfortunate side effect of closures is that they make it trivially easy to leak memory in Internet Explorer. JavaScript is a garbage collected language — objects are allocated memory upon their creation and that memory is reclaimed by the browser when no references to an object remain. Objects provided by the host environment are handled by that environment.

    Browser hosts need to manage a large number of objects representing the HTML page being presented — the objects of the DOM. It is up to the browser to manage the allocation and recovery of these.

    Internet Explorer uses its own garbage collection scheme for this, separate from the mechanism used by JavaScript. It is the interaction between the two that can cause memory leaks.

    A memory leak in IE occurs any time a circular reference is formed between a JavaScript object and a native object. Consider the following:

    function leakMemory() {
        var el = document.getElementById('el');
        var o = { 'el': el };
        el.o = o;
    }
    

    The circular reference formed above creates a memory leak; IE will not free the memory used by el and o until the browser is completely restarted.

    The above case is likely to go unnoticed; memory leaks only become a real concern in long running applications or applications that leak large amounts of memory due to large data structures or leak patterns within loops.

    Leaks are rarely this obvious — often the leaked data structure can have many layers of references, obscuring the circular reference.

    Closures make it easy to create a memory leak without meaning to. Consider this:

    function addHandler() {
        var el = document.getElementById('el');
        el.onclick = function() {
            this.style.backgroundColor = 'red';
        }
    }
    

    The above code sets up the element to turn red when it is clicked. It also creates a memory leak. Why? Because the reference to el is inadvertently caught in the closure created for the anonymous inner function. This creates a circular reference between a JavaScript object (the function) and a native object (el).

    needsTechnicalReview();
    

    There are a number of workarounds for this problem. The simplest is not to use the el variable:

    function addHandler(){
        document.getElementById('el').onclick = function(){
            this.style.backgroundColor = 'red';
        }
    }
    

    Surprisingly, one trick for breaking circular references introduced by a closure is to add another closure:

    function addHandler() {
        var clickHandler = function() {
            this.style.backgroundColor = 'red';
        };
        (function() {
            var el = document.getElementById('el');
            el.onclick = clickHandler;
        })();
    }
    

    The inner function is executed straight away, and hides its contents from the closure created with clickHandler.

    Another good trick for avoiding closures is breaking circular references during the window.onunload event. Many event libraries will do this for you. Note that doing so disables bfcache in Firefox 1.5, so you should not register an unload listener in Firefox, unless you have other reasons to do so.

    Original Document Information

    • Author: Simon Willison
    • Last Updated Date: March 7, 2006
    • Copyright: © 2006 Simon Willison, contributed under the Creative Commons: Attribute-Sharealike 2.0 license.
    • More information: For more information about this tutorial (and for links to the original talk's slides), see Simon's Etech weblog post.

     

    Etiquetas do documento e colaboradores

    Contribuíram para esta página: arthur.luiz, angelorubin, ThiagoMatos, TheBorba, jlcarvalho, gtcarlos, teoli
    Última atualização por: ThiagoMatos,