Qual é a palavra-chave 'nova' em JavaScript?


1744

A newpalavra-chave em JavaScript pode ser bastante confusa quando é encontrada pela primeira vez, pois as pessoas tendem a pensar que o JavaScript não é uma linguagem de programação orientada a objetos.

  • O que é isso?
  • Que problemas ele resolve?
  • Quando é apropriado e quando não?

10
Também, thread relacionado - stackoverflow.com/questions/383402/…
Chetan Sastry

Respostas:


2145

Faz 5 coisas:

  1. Ele cria um novo objeto. O tipo deste objeto é simplesmente objeto .
  2. Ele define a propriedade interna, inacessível, [[prototype]] (ou seja, __proto__ ) desse novo objeto como o objeto externo, acessível e de protótipo da função do construtor (todo objeto de função possui automaticamente uma propriedade de protótipo ).
  3. Faz com que a thisvariável aponte para o objeto recém-criado.
  4. Ele executa a função construtora, usando o objeto recém-criado sempre que thisé mencionado.
  5. Ele retorna o objeto recém-criado, a menos que a função construtora retorne uma nullreferência não- objeto. Nesse caso, a referência ao objeto é retornada.

Nota: a função construtora refere-se à função após a newpalavra - chave, como em

new ConstructorFunction(arg1, arg2)

Uma vez feito isso, se uma propriedade indefinida do novo objeto for solicitada, o script verificará o objeto [[protótipo]] do objeto para a propriedade. É assim que você pode obter algo semelhante à herança de classe tradicional em JavaScript.

A parte mais difícil sobre isso é o ponto número 2. Todo objeto (incluindo funções) tem essa propriedade interna chamada [[prototype]] . Ele pode ser definido no momento da criação do objeto, seja com new , com Object.create ou com base no literal (funções padrão para Function.prototype, números para Number.prototype etc.). Só pode ser lido com Object.getPrototypeOf (someObject) . Não outra maneira de definir ou ler esse valor.

As funções, além da propriedade [[prototype]] oculta , também possuem uma propriedade chamada prototype , e é isso que você pode acessar e modificar para fornecer propriedades e métodos herdados para os objetos que você cria.


Aqui está um exemplo:

ObjMaker = function() {this.a = 'first';};
// ObjMaker is just a function, there's nothing special about it that makes 
// it a constructor.

ObjMaker.prototype.b = 'second';
// like all functions, ObjMaker has an accessible prototype property that 
// we can alter. I just added a property called 'b' to it. Like 
// all objects, ObjMaker also has an inaccessible [[prototype]] property
// that we can't do anything with

obj1 = new ObjMaker();
// 3 things just happened.
// A new, empty object was created called obj1.  At first obj1 was the same
// as {}. The [[prototype]] property of obj1 was then set to the current
// object value of the ObjMaker.prototype (if ObjMaker.prototype is later
// assigned a new object value, obj1's [[prototype]] will not change, but you
// can alter the properties of ObjMaker.prototype to add to both the
// prototype and [[prototype]]). The ObjMaker function was executed, with
// obj1 in place of this... so obj1.a was set to 'first'.

obj1.a;
// returns 'first'
obj1.b;
// obj1 doesn't have a property called 'b', so JavaScript checks 
// its [[prototype]]. Its [[prototype]] is the same as ObjMaker.prototype
// ObjMaker.prototype has a property called 'b' with value 'second'
// returns 'second'

É como herança de classe porque agora, todos os objetos que você criar usando new ObjMaker()também parecerão ter herdado a propriedade 'b'.

Se você quiser algo como uma subclasse, faça o seguinte:

SubObjMaker = function () {};
SubObjMaker.prototype = new ObjMaker(); // note: this pattern is deprecated!
// Because we used 'new', the [[prototype]] property of SubObjMaker.prototype
// is now set to the object value of ObjMaker.prototype.
// The modern way to do this is with Object.create(), which was added in ECMAScript 5:
// SubObjMaker.prototype = Object.create(ObjMaker.prototype);

SubObjMaker.prototype.c = 'third';  
obj2 = new SubObjMaker();
// [[prototype]] property of obj2 is now set to SubObjMaker.prototype
// Remember that the [[prototype]] property of SubObjMaker.prototype
// is ObjMaker.prototype. So now obj2 has a prototype chain!
// obj2 ---> SubObjMaker.prototype ---> ObjMaker.prototype

obj2.c;
// returns 'third', from SubObjMaker.prototype

obj2.b;
// returns 'second', from ObjMaker.prototype

obj2.a;
// returns 'first', from SubObjMaker.prototype, because SubObjMaker.prototype 
// was created with the ObjMaker function, which assigned a for us

Li uma tonelada de lixo sobre esse assunto antes de finalmente encontrar esta página , onde isso é explicado muito bem com bons diagramas.


47
Só queria acrescentar: De fato, existe uma maneira de acessar o [[protótipo]] interno, por __proto__. No entanto, isso não é padrão e é suportado apenas por navegadores relativamente novos (e nem todos). Existe uma maneira padronizada, a saber: Object.getPrototypeOf (obj), mas é o Ecmascript3.1 e é suportado apenas em novos navegadores - novamente. Geralmente, é recomendável não usar essa propriedade, porém, as coisas ficam complicadas muito rapidamente lá dentro.
Blub

9
Pergunta: o que acontece de diferente se ObjMakeré definido como uma função que retorna um valor?
Jim Blackler 27/02

13
O @LonelyPixel newexiste para que você não precise escrever métodos de fábrica para construir / copiar funções / objetos. Significa: "Copie isso, tornando-o exatamente como sua 'classe' pai; faça-o de maneira eficiente e correta; e armazene informações de herança acessíveis apenas a mim, JS, internamente". Para fazer isso, modifica o interno inacessível prototypedo novo objeto para encapsular opacamente os membros herdados, imitando as cadeias de herança OO clássicas (que não são modificáveis ​​em tempo de execução). Você pode simular isso sem new, mas a herança poderá ser modificada em tempo de execução. Boa? Ruim? Você decide.
Engenheiro de

11
um pequeno ponto a ser adicionado: uma chamada para um construtor, quando precedida pela nova palavra-chave, retorna automaticamente o objeto criado; não há necessidade de devolvê-lo explicitamente de dentro do construtor.
Charlie Roberts

7
Há uma nota que diz Notice that this pattern is deprecated!. Qual é o padrão atualizado correto para definir o protótipo de uma classe?
Tom Pažourek

400

Suponha que você tenha esta função:

var Foo = function(){
  this.A = 1;
  this.B = 2;
};

Se você chamar isso como uma função autônoma como esta:

Foo();

A execução desta função adicionará duas propriedades ao windowobjeto ( Ae B). Ele é adicionado ao windowporque windowé o objeto que chamou a função quando você a executa dessa maneira, e thisem uma função é o objeto que chamou a função. Em Javascript, pelo menos.

Agora, chame assim com new:

var bar = new Foo();

O que acontece quando você adiciona newuma chamada de função é que um novo objeto é criado (apenas var bar = new Object()) e que thisdentro da função aponta para o novo que Objectvocê acabou de criar, em vez do objeto que chamou a função. Então baragora é um objeto com as propriedades Ae B. Qualquer função pode ser um construtor, mas nem sempre faz sentido.


7
Depende do contexto de execução. No meu caso (script Qt), é apenas um objeto global.
Maxym

2
isso causará mais uso de memória?
Jürgen Paul

2
porque window é o objeto que chamou a função - deve ser: porque window é o objeto que contém a função.
Dávid Horváth

1
@Taurus Em um navegador da Web, uma função que não seja um método será windowimplicitamente um método . Mesmo em um fechamento, mesmo que anônimo. No entanto, no exemplo, é uma chamada de método simples na janela: Foo();=> [default context].Foo();=> window.Foo();. Nesta expressão windowestá o contexto (não apenas o chamador , o que não importa).
Dávid Horváth

1
@Taurus Basicamente sim. No entanto, no ECMA 6 e 7, as coisas são mais complexas (consulte lambdas, classes, etc.).
Dávid Horváth

164

Além da resposta de Daniel Howard, aqui está o que newfaz (ou pelo menos parece fazer):

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}

Enquanto

var obj = New(A, 1, 2);

é equivalente a

var obj = new A(1, 2);

73
Descobri que javascript é mais fácil de entender do que Inglês: v
damphat

Excelente resposta. Eu tenho uma pequena pergunta: Como pode ser possível para func.prototypeser null? Poderia, por favor, elaborar um pouco sobre isso?
Tom Pažourek

6
@tomp você poderia substituir a propriedade protótipo, simplesmente escrevendo A.prototype = null;Nesse caso new A()resultará em no objeto, isso é pontos protótipos internos para o Objectobjeto: jsfiddle.net/Mk42Z
basilikum

2
A verificação typeof pode estar incorreta porque um objeto host pode produzir algo diferente de "objeto" ou "função". Para testar se algo é um objeto, eu prefiro Object(ret) === ret.
Oriol

2
@ Oriol obrigado pelo comentário. É verdade o que você diz e qualquer teste real deve ser feito de maneira mais robusta. No entanto, acho que, para essa resposta conceitual, o typeofteste facilita a compreensão do que está acontecendo nos bastidores.
basilikum

109

Para iniciantes entender melhor

experimente o seguinte código no console do navegador.

function Foo() { 
    return this; 
}

var a = Foo();       //returns window object
var b = new Foo();   //returns empty object of foo

a instanceof Window;  // true
a instanceof Foo;     // false

b instanceof Window;  // false
b instanceof Foo;     // true

Agora você pode ler a resposta do wiki da comunidade :)


4
Boa resposta. Além disso - deixar de fora return this;produz a mesma saída.
Nelu

37

então provavelmente não é para criar instâncias de objeto

É usado exatamente para isso. Você define um construtor de função da seguinte maneira:

function Person(name) {
    this.name = name;
}

var john = new Person('John');

No entanto, o benefício extra que o ECMAScript possui é que você pode estender a .prototypepropriedade, para que possamos fazer algo como ...

Person.prototype.getName = function() { return this.name; }

Todos os objetos criados a partir deste construtor agora terão uma getNamecausa da cadeia de protótipos à qual eles têm acesso.


6
Os construtores de funções são usados ​​como classes, não há classpalavra-chave, mas é possível fazer a mesma coisa.
meder omuraliev

Há kindof é uma palavra-chave classe - classe é reservado para uso futuro
Greg

11
Aliás é por isso que você usa .className não .class para definir uma classe CSS
Greg

23
Deve ser uma pessoa em maiúscula por convenção.
eomeroff

27

JavaScript é uma linguagem de programação orientada a objetos e é usada exatamente para criar instâncias. É baseado em protótipo, em vez de baseado em classe, mas isso não significa que não seja orientado a objetos.


6
Eu gosto de dizer que o JavaScript parece ser ainda mais orientado a objetos do que todas essas linguagens baseadas em classes. No JavaScript, tudo o que você escreve imediatamente se torna um objeto, mas nas linguagens baseadas em classes você primeiro escreve declarações e somente depois cria instâncias específicas (objetos) de classes. E o protótipo JavaScript parece lembrar vagamente todo esse material VTABLE para linguagens baseadas em classes.
precisa saber é o seguinte

14

Javascript é uma linguagem de programação dinâmica que suporta o paradigma de programação orientada a objetos e é usada para criar novas instâncias de objeto.

Classes não são necessárias para objetos - Javascript é uma linguagem baseada em protótipo .


12

Já existem respostas muito boas, mas estou postando uma nova para enfatizar minha observação no caso III abaixo sobre o que acontece quando você tem uma declaração de retorno explícita em uma função que está newdesenvolvendo. Veja os casos abaixo:

Caso I :

var Foo = function(){
  this.A = 1; 
  this.B = 2;
};
console.log(Foo()); //prints undefined
console.log(window.A); //prints 1

Acima está um caso simples de chamar a função anônima apontada por Foo. Quando você chama essa função, ela retorna undefined. Como não há uma declaração de retorno explícita, o intérprete de JavaScript insere forçosamente uma return undefined;declaração no final da função. Aqui janela é o objeto invocação (contextual this) que recebe novo Ae Bpropriedades.

Caso II :

var Foo = function(){
  this.A = 1;
  this.B = 2;
};
var bar = new Foo();
console.log(bar()); //illegal isn't pointing to a function but an object
console.log(bar.A); //prints 1

Aqui, o intérprete JavaScript, vendo a newpalavra - chave, cria um novo objeto que atua como o objeto de invocação (contextual this) da função anônima apontada por Foo. Nesse caso, torne A- Bse propriedades no objeto recém-criado (no lugar do objeto de janela). Como você não possui nenhuma declaração de retorno explícita, o intérprete JavaScript insere forçosamente uma declaração de retorno para retornar o novo objeto criado devido ao uso da newpalavra-chave.

Caso III :

var Foo = function(){
  this.A = 1;
  this.B = 2;
  return {C:20,D:30}; 
};
var bar = new Foo();
console.log(bar.C);//prints 20
console.log(bar.A); //prints undefined. bar is not pointing to the object which got created due to new keyword.

Aqui, novamente, o intérprete JavaScript, vendo a newpalavra - chave, cria um novo objeto que atua como o objeto de invocação (contextual this) da função anônima apontada por Foo. Novamente Ae Btorne - se propriedades no objeto recém-criado. Mas desta vez você tem uma declaração de retorno explícita para que o intérprete JavaScript não faça nada por conta própria.

O ponto a ser observado no caso III é que o objeto que está sendo criado devido à newpalavra - chave se perdeu do seu radar. barestá na verdade apontando para um objeto completamente diferente, que não é aquele criado pelo interpretador JavaScript devido à newpalavra - chave.

Citando David Flanagan de JavaScripit: The Definitive Guide (6th Edition), cap. 4, página 62:

Quando uma expressão de criação de objeto é avaliada, o JavaScript primeiro cria um novo objeto vazio, exatamente como o criado pelo inicializador de objetos {}. Em seguida, ele chama a função especificada com os argumentos especificados, passando o novo objeto como o valor da palavra-chave this. A função pode usar isso para inicializar as propriedades do objeto recém-criado. As funções escritas para uso como construtores não retornam um valor, e o valor da expressão de criação do objeto é o objeto recém-criado e inicializado. Se um construtor retornar um valor do objeto, esse valor se tornará o valor da expressão de criação do objeto e o objeto recém-criado será descartado.

--- Informações adicionais ---

As funções usadas no snippet de código dos casos acima têm nomes especiais no mundo JS, como abaixo:

Caso I e II - Função Construtora

Caso III - Função de fábrica. As funções de fábrica não devem ser usadas com a newpalavra - chave que fiz para explicar o conceito no encadeamento atual.

Você pode ler sobre a diferença entre eles neste tópico.


seu caso 3, é uma observação gr8
appu

5

Às vezes, o código é mais fácil que as palavras:

var func1 = function (x) { this.x = x; }                    // used with 'new' only
var func2 = function (x) { var z={}; z.x = x; return z; }   // used both ways
func1.prototype.y = 11;
func2.prototype.y = 12;

A1 = new func1(1);      // has A1.x  AND  A1.y
A2 =     func1(1);      // undefined ('this' refers to 'window')
B1 = new func2(2);      // has B1.x  ONLY
B2 =     func2(2);      // has B2.x  ONLY

para mim, contanto que eu não protótipo, uso o estilo de func2, pois ele me proporciona um pouco mais de flexibilidade dentro e fora da função.


3
B1 = new func2(2); <- Por que isso não vai ter B1.y ?
sunny_dev

@sunny_dev Não tenho um perito JS, mas provavelmente porque func2 está a devolver directamente um valor (objeto z), em vez de trabalhar / retornando com valores internos (este)
de Eagle

5

A newpalavra-chave altera o contexto no qual a função está sendo executada e retorna um ponteiro para esse contexto.

Quando você não usa a newpalavra - chave, o contexto no qual a função Vehicle()é executada é o mesmo contexto no qual você está chamando a Vehiclefunção. A thispalavra-chave se referirá ao mesmo contexto. Quando você usa new Vehicle(), um novo contexto é criado para que a palavra-chave thisdentro da função se refira ao novo contexto. O que você recebe em troca é o contexto recém-criado.


Essa é uma resposta muito perspicaz em termos de escopo. Adição de Gr8 à resposta.
App

3

A newpalavra-chave é para criar novas instâncias de objetos. E sim, o javascript é uma linguagem de programação dinâmica, que suporta o paradigma de programação orientada a objetos. A convenção sobre a nomeação de objetos é sempre usar letras maiúsculas para objetos que devem ser instanciados pela nova palavra-chave.

obj = new Element();

2

Resumo:

A newpalavra-chave é usada em javascript para criar um objeto a partir de uma função de construtor. A newpalavra-chave deve ser colocada antes da chamada da função do construtor e fará o seguinte:

  1. Cria um novo objeto
  2. Define o protótipo deste objeto para a propriedade prototype da função construtora
  3. Vincula a thispalavra-chave ao objeto recém-criado e executa a função construtora
  4. Retorna o objeto recém-criado

Exemplo:

function Dog (age) {
  this.age = age;
}

const doggie = new Dog(12);

console.log(doggie);
console.log(doggie.__proto__ === Dog.prototype) // true

O que exatamente acontece:

  1. const doggie diz: Precisamos de memória para declarar uma variável.
  2. O operador de atribuição =diz: Vamos inicializar essa variável com a expressão após o=
  3. A expressão é new Dog(12). O mecanismo JS vê a nova palavra-chave, cria um novo objeto e define o protótipo como Dog.prototype
  4. A função construtora é executada com o thisvalor definido para o novo objeto. Nesta etapa, é onde a idade é atribuída ao novo objeto cachorrinho criado.
  5. O objeto recém-criado é retornado e atribuído à variável doggie.

1

A newpalavra-chave cria instâncias de objetos usando funções como construtor. Por exemplo:

var Foo = function() {};
Foo.prototype.bar = 'bar';

var foo = new Foo();
foo instanceof Foo; // true

Instâncias herdam da prototypefunção construtora. Então, dado o exemplo acima ...

foo.bar; // 'bar'

2
A nova palavra-chave basicamente associa a função como o construtor já; você não precisa retornar nada. Você pode simplesmente fazer: function foo (x) {this.bar = x; } var obj = novo foo (10); alerta (obj.bar);
21129 reko_t

Você não precisa retornar objetos da função construtora, a menos que deseje especificamente, para uma finalidade. Por exemplo, se você precisar retornar uma instância de objeto específica em vez de criar um novo objeto todas as vezes (por qualquer motivo). No seu exemplo, no entanto, é totalmente desnecessário.
Chetan Sastry

Bem, foi um exemplo. Você pode retornar um objeto. Existem muitos padrões usados ​​nesse cenário, eu forneci um como "por exemplo", daí minhas palavras "por exemplo".
Eyelidlessness 29/10/09

1

Bem, o JavaScript por si pode diferir bastante de plataforma para plataforma, pois é sempre uma implementação da especificação original EcmaScript.

De qualquer forma, independentemente da implementação, todas as implementações JavaScript que seguem a especificação EcmaScript corretamente fornecem uma Linguagem Orientada a Objetos. De acordo com o padrão ES:

O ECMAScript é uma linguagem de programação orientada a objetos para executar cálculos e manipular objetos computacionais em um ambiente host.

Então agora que concordamos que o JavaScript é uma implementação do EcmaScript e, portanto, é uma linguagem orientada a objetos. A definição da newoperação em qualquer linguagem orientada a objetos diz que essa palavra-chave é usada para criar uma instância de objeto a partir de uma classe de um determinado tipo (incluindo tipos anônimos, em casos como C #).

No EcmaScript, não usamos classes, como você pode ler nas especificações:

O ECMAScript não usa classes como aquelas em C ++, Smalltalk ou Java. Em vez disso, os objetos podem ser criados de várias maneiras, inclusive por meio de uma notação literal ou por meio de construtores que criam objetos e, em seguida, executam código que inicializa todos ou parte deles, atribuindo valores iniciais às suas propriedades. Cada construtor é uma função que possui uma propriedade denominada protótipo usada para implementar a herança baseada em protótipo e propriedades compartilhadas. Objetos são criados
usando construtores em novas expressões; por exemplo, nova data (2009,11) cria um novo objeto de data. Invocar um construtor sem usar new tem consequências que dependem do construtor. Por exemplo, Date () produz uma representação de string da data e hora atuais, em vez de um objeto.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.