A sintaxe regular {...}
nos permite criar um objeto. Mas muitas vezes precisamos criar muitos objetos semelhantes, como vários usuários ou itens de menu e assim por diante.
Isso pode ser feito usando funções construtoras e o operador "new"
.
As funções construtoras são tecnicamente funções regulares. Existem duas convenções:
Eles são nomeados com letra maiúscula primeiro.
Eles devem ser executados apenas com o operador "new"
.
Por exemplo:
função Usuário(nome) { este.nome = nome; this.isAdmin = falso; } deixe usuário = novo usuário("Jack"); alerta(usuário.nome); // Jack alerta(usuário.isAdmin); // falso
Quando uma função é executada com new
, ela executa as seguintes etapas:
Um novo objeto vazio é criado e atribuído a this
.
O corpo da função é executado. Geralmente modifica this
, adiciona novas propriedades a ele.
O valor this
é retornado.
Em outras palavras, new User(...)
faz algo como:
função Usuário(nome) { // isto = {}; (implicitamente) //adiciona propriedades a isso este.nome = nome; this.isAdmin = falso; // retorna isso; (implicitamente) }
Então let user = new User("Jack")
dá o mesmo resultado que:
deixe usuário = { nome: "Jack", isAdmin: falso };
Agora, se quisermos criar outros usuários, podemos chamar new User("Ann")
, new User("Alice")
e assim por diante. Muito mais curto do que usar literais sempre e também fácil de ler.
Esse é o principal objetivo dos construtores – implementar código de criação de objetos reutilizáveis.
Observemos mais uma vez – tecnicamente, qualquer função (exceto funções de seta, pois elas não possuem this
) pode ser usada como construtor. Ele pode ser executado com new
e executará o algoritmo acima. A “letra maiúscula primeiro” é um acordo comum, para deixar claro que uma função deve ser executada com new
.
nova função() {…}
Se tivermos muitas linhas de código sobre a criação de um único objeto complexo, podemos envolvê-las em uma função construtora chamada imediatamente, como esta:
// cria uma função e chama-a imediatamente com new deixe usuário = nova função() { este.nome = "João"; this.isAdmin = falso; // ...outro código para criação de usuário // talvez lógica e instruções complexas //variáveis locais etc. };
Este construtor não pode ser chamado novamente, pois não é salvo em lugar nenhum, apenas criado e chamado. Portanto, esse truque visa encapsular o código que constrói o objeto único, sem reutilização futura.
Coisas avançadas
A sintaxe desta seção raramente é usada, pule-a a menos que queira saber tudo.
Dentro de uma função, podemos verificar se ela foi chamada com new
ou sem, usando uma propriedade especial new.target
.
É indefinido para chamadas regulares e é igual à função se chamada com new
:
função Usuário() { alerta(novo.alvo); } // sem "novo": Usuário(); // indefinido // com "novo": novo usuário(); //função Usuário {...}
Isso pode ser usado dentro da função para saber se ela foi chamada com new
, “em modo construtor”, ou sem ele, “em modo regular”.
Também podemos fazer chamadas new
e regulares para fazer o mesmo, assim:
função Usuário(nome) { if (!new.target) { // se você me executar sem new retornar novo usuário(nome); // ...vou adicionar novidades para você } este.nome = nome; } deixe joão = Usuário("João"); //redireciona a chamada para o novo usuário alerta(joão.nome); // John
Essa abordagem às vezes é usada em bibliotecas para tornar a sintaxe mais flexível. Para que as pessoas possam chamar a função com ou sem new
e ela ainda funcione.
Provavelmente não é uma boa coisa para usar em todos os lugares, porque omitir new
torna um pouco menos óbvio o que está acontecendo. Com new
todos sabemos que o novo objeto está sendo criado.
Normalmente, os construtores não possuem uma instrução return
. A tarefa deles é escrever todas as coisas necessárias this
, e isso automaticamente se torna o resultado.
Mas se houver uma instrução return
, a regra é simples:
Se return
for chamado com um objeto, então o objeto será retornado em vez de this
.
Se return
for chamado com um primitivo, ele será ignorado.
Em outras palavras, return
com um objeto retorna aquele objeto, em todos os outros casos this
é retornado.
Por exemplo, aqui return
substitui this
retornando um objeto:
função BigUser() { este.nome = "João"; retornar {nome: "Godzilla" }; // <-- retorna este objeto } alerta(new BigUser().nome); // Godzilla, peguei aquele objeto
E aqui está um exemplo com return
vazio (ou poderíamos colocar uma primitiva depois dele, não importa):
função UsuárioPequeno() { este.nome = "João"; retornar; // <-- retorna isso } alerta(new SmallUser().nome); // John
Normalmente os construtores não possuem uma instrução return
. Aqui mencionamos o comportamento especial com o retorno de objetos principalmente por uma questão de integridade.
Omitindo parênteses
A propósito, podemos omitir os parênteses após new
:
deixe usuário = novo usuário; // <-- sem parênteses // igual a deixe usuário = novo usuário();
Omitir parênteses aqui não é considerado um “bom estilo”, mas a sintaxe é permitida pela especificação.
Usar funções construtoras para criar objetos oferece muita flexibilidade. A função construtora pode ter parâmetros que definem como construir o objeto e o que colocar nele.
Claro, podemos adicionar a this
não apenas propriedades, mas também métodos.
Por exemplo, new User(name)
abaixo cria um objeto com o name
fornecido e o método sayHi
:
função Usuário(nome) { este.nome = nome; this.sayHi = function() { alert("Meu nome é: " + this.name ); }; } deixe joão = novo usuário("João"); john.sayOi(); //Meu nome é: João /* João = { nome: "João", digaOi: function() { ... } } */
Para criar objetos complexos, existe uma sintaxe mais avançada, as classes, que abordaremos mais tarde.
Funções construtoras ou, resumidamente, construtores, são funções regulares, mas há um acordo comum de nomeá-las com letra maiúscula primeiro.
Funções construtoras só devem ser chamadas usando new
. Tal chamada implica a criação de this
vazio no início e o retorno do preenchido no final.
Podemos usar funções construtoras para criar vários objetos semelhantes.
JavaScript fornece funções construtoras para muitos objetos de linguagem integrados: como Date
para datas, Set
para conjuntos e outros que planejamos estudar.
Objetos, estaremos de volta!
Neste capítulo, cobrimos apenas o básico sobre objetos e construtores. Eles são essenciais para aprender mais sobre tipos de dados e funções nos próximos capítulos.
Depois de aprendermos isso, voltaremos aos objetos e os abordaremos detalhadamente nos capítulos Protótipos, herança e Classes.
importância: 2
É possível criar funções A
e B
para que new A() == new B()
?
função UMA() {...} função B() {...} deixe a = new A(); seja b = novo B(); alerta(a == b); // verdadeiro
Se for, forneça um exemplo do código deles.
Sim, é possível.
Se uma função retornar um objeto, new
o retornará em vez de this
.
Assim, eles podem, por exemplo, retornar o mesmo objeto definido externamente obj
:
deixe obj = {}; função A() { return obj; } função B() {retorna objeto; } alerta(novo A() == novo B()); // verdadeiro
importância: 5
Crie uma função construtora Calculator
que cria objetos com 3 métodos:
read()
solicita dois valores e os salva como propriedades b
a
.
sum()
retorna a soma dessas propriedades.
mul()
retorna o produto da multiplicação dessas propriedades.
Por exemplo:
deixe calculadora = new Calculadora(); calculadora.read(); alerta( "Soma=" + calculadora.sum() ); alerta( "Mul=" + calculadora.mul() );
Execute a demonstração
Abra uma sandbox com testes.
função Calculadora() { isto.leia=função(){ isto.a = +prompt('a?', 0); isto.b = +prompt('b?', 0); }; esta.soma = função() { retorne isto.a + isto.b; }; isto.mul = função() { retorne isto.a * isto.b; }; } deixe calculadora = new Calculadora(); calculadora.read(); alerta( "Soma=" + calculadora.sum() ); alerta( "Mul=" + calculadora.mul() );
Abra a solução com testes em uma sandbox.
importância: 5
Crie uma função construtora Accumulator(startingValue)
.
O objeto que ele cria deve:
Armazene o “valor atual” na propriedade value
. O valor inicial é definido como o argumento do construtor startingValue
.
O método read()
deve usar prompt
para ler um novo número e adicioná-lo a value
.
Em outras palavras, a propriedade value
é a soma de todos os valores inseridos pelo usuário com o valor inicial startingValue
.
Aqui está a demonstração do código:
deixe acumulador = novo acumulador (1); // valor inicial 1 acumulador.read(); //adiciona o valor digitado pelo usuário acumulador.read(); //adiciona o valor digitado pelo usuário alerta(acumulador.valor); //mostra a soma desses valores
Execute a demonstração
Abra uma sandbox com testes.
function Acumulador(valorinicial) { this.value = valor inicial; isto.leia=função(){ this.value += +prompt('Quanto adicionar?', 0); }; } deixe acumulador = novo acumulador (1); acumulador.read(); acumulador.read(); alerta(acumulador.valor);
Abra a solução com testes em uma sandbox.