Operador new em JavaScript
Aprenda como funções construtoras e o operador new criam objects em JavaScript: o que o new faz passo a passo, regra de retorno, new.target e convenções de nomes.
Introdução a Construtores e ao Operador new
Em JavaScript, construtores são funções projetadas para inicializar objects recém-criados. Eles desempenham um papel fundamental na programação orientada a objetos, permitindo que desenvolvedores definam propriedades e comportamentos que objects de uma determinada classe devem ter. O operador new é utilizado para criar uma instância de um object com base em uma função construtora, configurando um novo ambiente de object a partir do protótipo especificado e executando o construtor para inicializar o novo object.
Como os Construtores Funcionam
Uma função construtora em JavaScript se parece com qualquer outra função, mas é convencionalmente nomeada com uma letra maiúscula para diferenciá-la de funções comuns. Quando o operador new invoca uma função construtora, quatro coisas acontecem nos bastidores, equivalentes aproximadamente a este pseudocódigo:
function User(name) {
// this = {}; (1) an empty object is created and assigned to this
// this.__proto__ = User.prototype; (2) prototype is linked
this.name = name; // (3) the constructor body runs, adding properties to this
// return this; (4) this is returned automatically
}- Um novo object vazio é criado e atribuído a
this. - O protótipo é vinculado: o
[[Prototype]]interno do novo object é definido como a propriedadeprototypedo construtor, de modo que ele herda propriedades e métodos definidos ali. - O corpo do construtor é executado: a função é executada com os argumentos passados, e
thisrefere-se ao object recém-criado, de modo que atribuições comothis.name = nameanexam propriedades a ele. - O object é retornado:
thisé retornado automaticamente, a menos que o construtor retorne explicitamente um object diferente (veja A regra do valor de retorno abaixo).
O JavaScript moderno usa a sintaxe de class para definir construtores e métodos de forma mais intuitiva. Isso oferece uma abordagem mais direta e baseada em classes, semelhante a outras linguagens de programação.
Exemplo: Função Construtora Básica
Explicação: Neste exemplo, User é uma função construtora que inicializa name, age e um método greet nos objects recém-criados. A instrução new User('John', 30) cria uma nova instância de User com o nome "John" e idade 30. Dentro de greet, this refere-se ao object no qual o método foi chamado.
A regra do valor de retorno
Construtores normalmente não usam return — o novo object (this) é retornado automaticamente. Mas return tem um comportamento especial e fácil de não perceber dentro de um construtor:
- Se
returnfor seguido de um object, esse object é retornado no lugar dethis. - Se
returnfor seguido de um primitivo (string, number, boolean,undefined, etc.), ele é ignorado ethisé retornado normalmente.
Explicação: WithObject retorna um object simples, de modo que esse object substitui completamente a instância. WithPrimitive retorna uma string, que é ignorada, portanto o this original (com name: 'Alice') é retornado. Raramente você vai depender disso, mas isso explica resultados surpreendentes quando um construtor retorna acidentalmente um valor.
Detectando new com new.target
Dentro de qualquer função, new.target é undefined quando a função é chamada normalmente e igual à própria função quando chamada com new. Isso permite que um construtor detecte como foi invocado — útil para impor (ou silenciosamente permitir) o uso da palavra-chave new.
Explicação: Como Modal verifica new.target, chamar Modal('Without new') sem new é redirecionado de forma transparente para new Modal(...), de modo que b ainda é uma instância real de Modal. (Observação: muitas equipes preferem não fazer isso e deixar que a ausência de new gere um erro explícito.)
Quando devo usar um construtor?
Use uma função construtora (ou uma class) quando precisar criar muitos objects com a mesma estrutura — vários usuários, carros, widgets do DOM, etc. O construtor centraliza a lógica de configuração para que cada instância seja criada da mesma forma e compartilhe métodos por meio do protótipo.
Se você precisar de apenas um único object, um object literal simples { ... } é mais simples. Para um object único que ainda se beneficia de um corpo de construtor, você pode até usar um construtor anônimo:
Explicação: A function anônima é executada uma única vez como construtora e não é salva em lugar nenhum, portanto não pode ser reutilizada — é apenas uma forma de encapsular uma configuração complexa de uso único.
Funções construtoras vs. classes
O código moderno geralmente prefere a sintaxe class, que é essencialmente açúcar sintático sobre funções construtoras e protótipos. Os dois trechos abaixo são equivalentes:
As classes trazem benefícios reais em relação às funções construtoras: os métodos não são enumeráveis por padrão, o corpo é executado em modo estrito, chamar uma class sem new lança um erro, e extends/super tornam a herança muito mais limpa. As funções construtoras continuam sendo importantes de entender porque as classes são construídas sobre o mesmo mecanismo de protótipo, e você ainda as encontrará em código mais antigo.
Usando Construtores para Objects Complexos
Os construtores podem ser usados para estabelecer relações mais complexas entre objects, incluindo métodos que interagem com outras propriedades dos objects.
Exemplo: Construtor com Métodos
Explicação: O construtor Car configura cada object carro com propriedades específicas e um método que exibe informações sobre o carro.
Exemplo: Métodos no Protótipo
Explicação: Ao adicionar introduce ao protótipo de Employee, todas as instâncias compartilham o mesmo método, o que é mais eficiente em termos de memória do que defini-lo diretamente no construtor.
Recomenda-se usar classes ES6 para definir objects e construtores para obter um código mais limpo e legível.
Boas Práticas com Construtores
Ao trabalhar com construtores em JavaScript, seguir certas boas práticas pode melhorar significativamente a legibilidade, eficiência e escalabilidade do seu código. A seguir, as práticas são detalhadas com exemplos e explicações:
1. Convenção de Nomenclatura
Boa Prática: Sempre inicie os nomes de construtores com letra maiúscula para diferenciá-los de funções comuns. Esta é uma convenção amplamente adotada em JavaScript e em muitas outras linguagens de programação que ajuda os desenvolvedores a identificar rapidamente funções construtoras.
Exemplo:
Explicação: A função construtora Laptop começa com letra maiúscula, indicando que deve ser usada com o operador new para criar novos objects.
2. Separar a Lógica
Boa Prática: Para métodos que não precisam acessar dados individuais da instância, defina-os no protótipo do construtor em vez de dentro do próprio construtor. Essa abordagem economiza memória porque todas as instâncias compartilham o mesmo método, em vez de cada instância criar uma nova função na memória.
Exemplo:
Explicação: O método describe é adicionado ao protótipo de Book, o que significa que todas as instâncias de Book compartilham o mesmo método describe. Isso é mais eficiente do que definir describe dentro do construtor, o que criaria uma nova função para cada instância do livro.
3. Valores de Retorno
Boa Prática: Evite retornar valores de construtores. Os construtores JavaScript retornam automaticamente a nova instância do object, a menos que um object diferente seja explicitamente retornado. Retornar valores não-object (como uma string ou um número) não terá efeito, e a nova instância ainda será retornada.
Exemplo:
Explicação: Apesar de tentar retornar uma string do construtor Player, o JavaScript ignora esse valor de retorno porque não é um object. A nova instância de Player é retornada normalmente.
Conclusão
Compreender e utilizar construtores e o operador new em JavaScript é essencial para uma programação orientada a objetos eficaz. Seguindo as convenções e boas práticas descritas aqui, os desenvolvedores podem criar código organizado, eficiente e escalável. Os construtores fornecem um mecanismo poderoso para inicializar novos objects e definir seu comportamento de forma estruturada e intuitiva.
Para se aprofundar, explore como os construtores compartilham comportamento por meio da herança prototípica, como a sintaxe class se baseia nessas ideias, e como uma função também é um object com propriedades próprias.