Qual é a diferença entre
var A = function () {
this.x = function () {
//do something
};
};
e
var A = function () { };
A.prototype.x = function () {
//do something
};
javascript
prototype
this
sw234
fonte
fonte
a1.x !== a2.x
:; no protótipo:a1.x === a2.x
Respostas:
Os exemplos têm resultados muito diferentes.
Antes de analisar as diferenças, deve-se observar o seguinte:
[[Prototype]]
propriedade privada da instância .myObj.method()
), isso dentro do método faz referência ao objeto. Quando isso não é definido pela chamada ou pelo uso de ligação , o padrão é o objeto global (janela em um navegador) ou no modo estrito, permanece indefinido.Então, aqui estão os trechos em questão:
Nesse caso,
A
é atribuído à variável um valor que é uma referência a uma função. Quando essa função é chamada usandoA()
, a função é esta não é definida pela chamada para o padrão é o objeto global e a expressãothis.x
é eficazwindow.x
. O resultado é que é atribuída uma referência à expressão da função no lado direitowindow.x
.No caso de:
algo muito diferente ocorre. Na primeira linha, a variável
A
é atribuída uma referência a uma função. Em JavaScript, todos os objetos de funções têm uma propriedade prototype por padrão, portanto, não há código separado para criar um objeto A.prototype .Na segunda linha, A.prototype.x recebe uma referência a uma função. Isso criará uma propriedade x , se não existir, ou atribuirá um novo valor, se existir. Portanto, a diferença com o primeiro exemplo em que a propriedade x do objeto está envolvida na expressão.
Outro exemplo está abaixo. É semelhante ao primeiro (e talvez o que você queria perguntar):
Neste exemplo, o
new
operador foi adicionado antes da expressão da função para que a função seja chamada como construtor. Quando chamada comnew
, a função this é configurada para referenciar um novo Object cuja[[Prototype]]
propriedade privada é configurada para referenciar o protótipo público do construtor . Portanto, na declaração de atribuição, ax
propriedade será criada nesse novo objeto. Quando chamada como construtora, uma função retorna esse objeto por padrão, portanto, não há necessidade de umareturn this;
instrução separada .Para verificar se A possui uma propriedade x :
Esse é um uso incomum do novo, pois a única maneira de referenciar o construtor é através do A.constructor . Seria muito mais comum fazer:
Outra maneira de obter um resultado semelhante é usar uma expressão de função chamada imediatamente:
Nesse caso,
A
atribua o valor de retorno da chamada da função no lado direito. Aqui, novamente, como isso não está definido na chamada, ele fará referência ao objeto global ethis.x
é eficazwindow.x
. Como a função não retorna nada,A
terá um valor deundefined
.Essas diferenças entre as duas abordagens também se manifestam se você estiver serializando e desserializando seus objetos Javascript de / para JSON. Os métodos definidos no protótipo de um objeto não são serializados quando você o serializa, o que pode ser conveniente quando, por exemplo, você deseja serializar apenas as partes de dados de um objeto, mas não os métodos:
Questões relacionadas :
Nota: Pode não haver economia significativa de memória entre as duas abordagens, no entanto, o uso do protótipo para compartilhar métodos e propriedades provavelmente usará menos memória do que cada instância com sua própria cópia.
JavaScript não é uma linguagem de baixo nível. Pode não ser muito valioso pensar em protótipos ou outros padrões de herança como uma maneira de alterar explicitamente a maneira como a memória é alocada.
fonte
null
), mas isso é muito diferente daprototype
propriedade - que está nas funções e na qual o protótipo de todas as instâncias é definido quando elas são construídasnew
. Não posso acreditar que isso realmente tenha 87"The language is functional"
Tem certeza de que é isso que significa funcional?A
como função, e a outra metade é sobre maneiras obscuras e não-ortodoxas de fazer algo simples.Como outros já disseram na primeira versão, usar "this" resulta em todas as instâncias da classe A com sua própria cópia independente do método da função "x". Enquanto o uso de "protótipo" significa que cada instância da classe A usará a mesma cópia do método "x".
Aqui está um código para mostrar essa diferença sutil:
Como outros já mencionaram, existem várias razões para escolher um método ou outro. Minha amostra é apenas para demonstrar claramente a diferença.
fonte
this
objeto, que é o proprietário do método. isto é, o método não possui nenhum objeto que seja seu proprietário. Nesse caso, há umthis
objeto, conforme mostrado na classe A no exemplo.Veja estes 2 exemplos:
vs.
A maioria das pessoas aqui (especialmente as respostas mais bem avaliadas) tentou explicar como elas são diferentes sem explicar POR QUE. Eu acho que isso está errado e se você entender os fundamentos primeiro, a diferença se tornará óbvia. Vamos tentar explicar os fundamentos primeiro ...
a) Uma função é um objeto em JavaScript. CADA objeto no JavaScript obtém uma propriedade interna (o que significa que você não pode acessá-lo como outras propriedades, exceto talvez em navegadores como o Chrome), geralmente chamado de
__proto__
(você pode realmente digitar oanyObject.__proto__
Chrome para ver o que ele faz referência. , uma propriedade, nada mais.Uma propriedade em JavaScript = uma variável dentro de um objeto, nada mais.O que as variáveis fazem? Elas apontam para coisas.Então, o que essa
__proto__
propriedade aponta? Bem, geralmente outro objeto (explicaremos o porquê mais tarde). A única maneira de forçar o JavaScript para que a__proto__
propriedade NÃO aponte para outro objeto é usarvar newObj = Object.create(null)
. Mesmo se você fizer isso, a__proto__
propriedade AINDA existe como uma propriedade do objeto, mas não aponta para outro objeto, aponta paranull
.Aqui é onde a maioria das pessoas se confunde:
Quando você cria uma nova função no JavaScript (que também é um objeto, lembra?), No momento em que é definida, o JavaScript cria automaticamente uma nova propriedade nessa função chamada
prototype
. Tente:A.prototype
é TOTALMENTE DIFERENTE da__proto__
propriedade. No nosso exemplo, 'A' agora tem DUAS propriedades chamadas 'protótipo' e__proto__
. Esta é uma grande confusão para as pessoas.prototype
e as__proto__
propriedades não têm relação alguma, são coisas separadas apontando para valores separados.Você pode se perguntar: Por que o JavaScript possui
__proto__
propriedades criadas em todos os objetos? Bem, uma palavra: delegação . Quando você chama uma propriedade em um objeto e o objeto não a possui, o JavaScript procura o objeto referenciado por__proto__
para ver se ele talvez o possua. Se não o possuir, ele examinará a__proto__
propriedade desse objeto e assim por diante ... até a cadeia terminar. Assim, o nome da cadeia de protótipos . Obviamente, se__proto__
não apontar para um objeto e, em vez disso, apontar paranull
, com muita sorte, o JavaScript perceberá isso e o retornaráundefined
para a propriedade.Você também pode se perguntar: por que o JavaScript cria uma propriedade chamada
prototype
para uma função quando você a define? Porque ele tenta enganar você, sim engane você que funciona como linguagens baseadas em classes.Vamos continuar com o nosso exemplo e criar um "objeto" de
A
:Há algo acontecendo em segundo plano quando isso aconteceu.
a1
é uma variável comum à qual foi atribuído um novo objeto vazio.O fato de você ter usado o operador
new
antes de uma chamada de funçãoA()
fez algo ADICIONAL em segundo plano. Anew
palavra-chave criou um novo objeto que agora faz referênciaa1
e esse objeto está vazio. Aqui está o que está acontecendo adicionalmente:Dissemos que em cada definição de função há uma nova propriedade criada chamada
prototype
(que você pode acessá-la, diferente da__proto__
propriedade) criada? Bem, essa propriedade está sendo usada agora.Então, agora estamos no ponto em que temos um
a1
objeto vazio recém-assado . Dissemos que todos os objetos em JavaScript têm uma__proto__
propriedade interna que aponta para algo (a1
também possui), seja nulo ou outro objeto. O que onew
operador faz é que ele defina essa__proto__
propriedade para apontar para aprototype
propriedade da função . Leia isso de novo. É basicamente isso:Dissemos que
A.prototype
nada mais é do que um objeto vazio (a menos que o alteremos para outra coisa antes de definira1
). Então agora basicamentea1.__proto__
aponta para a mesma coisaA.prototype
, que é esse objeto vazio. Ambos apontam para o mesmo objeto que foi criado quando essa linha aconteceu:Agora, há outra coisa acontecendo quando a
var a1 = new A()
instrução é processada. Basicamente,A()
é executado e se A é algo como isto:Todas essas coisas lá dentro
function() { }
serão executadas. Quando você atinge athis.hey..
linha,this
é alterado paraa1
e você obtém o seguinte:Não falarei sobre o porquê de
this
alterações,a1
mas esta é uma ótima resposta para saber mais.Então, para resumir, quando você faz,
var a1 = new A()
há três coisas acontecendo em segundo plano:a1
.a1 = {}
a1.__proto__
A propriedade é atribuída para apontar para a mesma coisa queA.prototype
aponta para (outro objeto vazio {})A função
A()
está sendo executada comthis
definido para o novo objeto vazio criado na etapa 1 (leia a resposta que eu referenciei acima sobre o motivo dethis
mudar paraa1
)Agora, vamos tentar criar outro objeto:
Os passos 1,2,3 serão repetidos. Você percebe alguma coisa? A palavra-chave é repetir. Etapa 1:
a2
será um novo objeto vazio, etapa 2: sua__proto__
propriedade apontará para a mesma coisaA.prototype
e, mais importante, etapa 3: a funçãoA()
é AGAIN executada, o que significa quea2
obterá ahey
propriedade que contém uma função.a1
ea2
tem duas propriedades SEPARATE nomeadashey
que apontam para 2 funções SEPARATE! Agora temos funções duplicadas nos mesmos dois objetos diferentes fazendo a mesma coisa, oops ... Você pode imaginar as implicações de memória disso se tivermos 1000 objetos criados comnew A
, depois de todas as declarações de funções ocuparem mais memória do que algo como o número 2. Então como podemos evitar isso?Lembra por que a
__proto__
propriedade existe em todos os objetos? Para que, se você recuperar ayoMan
propriedade ema1
(que não existe), sua__proto__
propriedade será consultada; se for um objeto (e na maioria dos casos, for), verificará se ela contémyoMan
e, se não, ele consultará o objeto__proto__
etc. etc. Se o fizer, ele pegará esse valor da propriedade e exibirá para você.Então, alguém decidiu usar esse fato + o fato de que, quando você cria
a1
, sua__proto__
propriedade aponta para o mesmo objeto (vazio)A.prototype
para:Legal! Agora, quando você cria
a1
, ele passa novamente por todas as 3 etapas acima e, na etapa 3, não faz nada, poisfunction A()
não tem nada para executar. E se fizermos:Ele verá que
a1
não contémhey
e verificará seu__proto__
objeto de propriedade para ver se o possui, qual é o caso.Com essa abordagem, eliminamos a parte da etapa 3, na qual as funções são duplicadas em cada nova criação de objeto. Em vez de
a1
ea2
ter umahey
propriedade separada , agora NENHUM deles a possui. Acho que você já se descobriu agora. Essa é a coisa legal ... se você entender__proto__
eFunction.prototype
, perguntas como essas serão bem óbvias.NOTA: Algumas pessoas tendem a não chamar a propriedade Prototype interna
__proto__
, pois usei esse nome na postagem para distingui-lo claramente daFunctional.prototype
propriedade como duas coisas diferentes.fonte
__proto__
e.prototype
são coisas totalmente diferentes.Na maioria dos casos, eles são essencialmente os mesmos, mas a segunda versão economiza memória porque há apenas uma instância da função em vez de uma função separada para cada objeto.
Uma razão para usar o primeiro formulário é acessar "membros privados". Por exemplo:
Devido às regras de escopo do javascript, private_var está disponível para a função atribuída a this.x, mas não fora do objeto.
fonte
O primeiro exemplo altera a interface apenas para esse objeto. O segundo exemplo altera a interface para todos os objetos dessa classe.
fonte
x
disponível para todos os objetos cujo protótipo é atribuída uma nova instância A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
O maior problema com o uso em
this
vez deprototype
é que, ao substituir um método, o construtor da classe base ainda fará referência ao método substituído. Considere isto:versus:
Se você acha que isso não é um problema, depende se você pode viver sem variáveis privadas e se possui experiência suficiente para conhecer um vazamento ao vê-lo. Além disso, ter que colocar a lógica do construtor após as definições do método é inconveniente.
versus:
fonte
Todo objeto está vinculado a um objeto de protótipo. Ao tentar acessar uma propriedade que não existe, o JavaScript procurará no objeto de protótipo do objeto e a retornará, se existir.
A
prototype
propriedade de um construtor de função refere-se ao objeto de protótipo de todas as instâncias criadas com essa função ao usarnew
.No seu primeiro exemplo, você está adicionando uma propriedade
x
a cada instância criada com aA
funçãoNo segundo exemplo, você está adicionando uma propriedade ao objeto de protótipo para o qual todas as instâncias criadas
A
apontam.Em conclusão, no primeiro exemplo, uma cópia da função é atribuída a cada instância . No segundo exemplo, uma única cópia da função é compartilhada por todas as instâncias .
fonte
Qual é a diferença? => Muito.
Eu acho que a
this
versão é usada para ativar o encapsulamento, ou seja, ocultar dados. Ajuda a manipular variáveis privadas.Vejamos o seguinte exemplo:
Agora, a
prototype
estrutura pode ser aplicada da seguinte maneira:Adultos diferentes têm idades diferentes, mas todos têm os mesmos direitos.
Então, nós o adicionamos usando protótipo, e não isso.
Vamos analisar a implementação agora.
Espero que isto ajude.
fonte
Protótipo é o modelo da classe; que se aplica a todas as instâncias futuras dele. Considerando que esta é a instância específica do objeto.
fonte
Eu sei que isso foi respondido até a morte, mas eu gostaria de mostrar um exemplo real de diferenças de velocidade.
Aqui, estamos criando 2.000.000 de novos objetos com um
print
método no Chrome. Estamos armazenando todos os objetos em uma matriz. A colocaçãoprint
do protótipo leva cerca de 1/2 do tempo.fonte
Deixe-me dar uma resposta mais abrangente que aprendi durante um curso de treinamento em JavaScript.
A maioria das respostas já mencionou a diferença, ou seja, ao prototipar a função é compartilhada com todas as instâncias (futuras). Considerando que declarar a função na classe criará uma cópia para cada instância.
Em geral, não há certo ou errado, é mais uma questão de gosto ou uma decisão de design, dependendo de suas necessidades. O protótipo, no entanto, é a técnica usada para desenvolver de maneira orientada a objetos, como espero que você veja no final desta resposta.
Você mostrou dois padrões em sua pergunta. Vou tentar explicar mais duas e tentarei explicar as diferenças, se relevante. Sinta-se livre para editar / estender. Em todos os exemplos, trata-se de um objeto de carro que possui um local e pode se mover.
Padrão Decorador de Objetos
Não tenho certeza se esse padrão ainda é relevante hoje em dia, mas existe. E é bom saber sobre isso. Você simplesmente passa um objeto e uma propriedade para a função decoradora. O decorador retorna o objeto com propriedade e método.
Classes funcionais
Uma função em JavaScript é um objeto especializado. Além de ser invocada, uma função pode armazenar propriedades como qualquer outro objeto.
Nesse caso,
Car
é uma função ( também pense em objeto ) que pode ser invocada como você costuma fazer. Possui uma propriedademethods
(que é um objeto com umamove
função). QuandoCar
é chamada, aextend
função é chamada, o que faz alguma mágica e estende aCar
função (objeto de reflexão) com os métodos definidos dentromethods
.Este exemplo, embora diferente, se aproxima do primeiro exemplo da pergunta.
Classes prototípicas
Os dois primeiros padrões permitem uma discussão sobre o uso de técnicas para definir métodos compartilhados ou o uso de métodos definidos em linha no corpo do construtor. Nos dois casos, cada instância tem sua própria
move
função.O padrão prototípico não se presta bem ao mesmo exame, porque o compartilhamento de funções por meio de uma delegação de protótipo é o próprio objetivo do padrão prototípico. Como outros apontaram, espera-se que tenha uma melhor pegada de memória.
No entanto, há um ponto interessante a saber: todo
prototype
objeto tem uma propriedade de conveniênciaconstructor
, que aponta para a função (objeto de reflexão) à qual ele foi anexado.Em relação às três últimas linhas:
Neste exemplo,
Car
vincula-se aoprototype
objeto, que vincula-seconstructor
aCar
ele próprio, ou seja,Car.prototype.constructor
éCar
ele próprio. Isso permite que você descubra qual função do construtor construiu um determinado objeto.amy.constructor
A pesquisa de falha e, portanto, é delegada paraCar.prototype
, que possui a propriedade construtora. E assimamy.constructor
éCar
.Além disso,
amy
é uminstanceof
Car
. Oinstanceof
operador trabalha verificando se o objeto protótipo do operando direito (Car
) pode ser encontrado em qualquer lugar daamy
cadeia protótipo ( ) do operando esquerdo .Alguns desenvolvedores podem ficar confusos no começo. Veja o exemplo abaixo:
O
instanceof
operador retornafalse
, porqueDog
o protótipo não pode ser encontrado em nenhum lugar dafido
cadeia de protótipos.fido
é um objeto simples criado com um literal de objeto, ou seja, apenas delega paraObject.prototype
.Padrões pseudoclássicos
Essa é realmente apenas outra forma do padrão prototípico de forma simplificada e mais familiar para quem programa em Java, por exemplo, uma vez que usa o
new
construtor.Ele realmente faz o mesmo que no padrão prototípico, é apenas uma sobreposição sintática de açúcar do padrão prototípico.
No entanto, a principal diferença é que há otimizações implementadas nos mecanismos JavaScript que só se aplicam ao usar o padrão pseudoclássico. Pense no padrão pseudoclássico uma versão provavelmente mais rápida do padrão prototípico; as relações de objeto nos dois exemplos são as mesmas.
Finalmente, não deve ser muito difícil perceber como a programação orientada a objetos pode ser feita. Existem duas seções.
Uma seção que define propriedades / métodos comuns no protótipo (cadeia).
E outra seção em que você coloca as definições que distinguem os objetos um do outro (
loc
variável nos exemplos).É isso que nos permite aplicar conceitos como superclasse ou subclasse em JavaScript.
Sinta-se livre para adicionar ou editar. Mais uma vez completo, talvez eu possa fazer deste um wiki da comunidade.
fonte
Acredito que @Matthew Crumley está certo. Eles são funcionalmente , se não estruturalmente, equivalentes. Se você usar o Firebug para examinar os objetos criados usando
new
, poderá ver que eles são iguais. No entanto, minha preferência seria a seguinte. Eu estou supondo que parece mais com o que estou acostumado em C # / Java. Ou seja, defina a classe, defina os campos, construtor e métodos.EDIT Não quis dizer que o escopo da variável era privado, apenas estava tentando ilustrar como defino minhas classes em javascript. O nome da variável foi alterado para refletir isso.
fonte
initialize
ex methods do not refer to the
_instance_var` em umaA
instância, mas em uma global. Usethis._instance_var
se você pretendia usar a_instance_var
propriedade de umaA
instância.Conforme discutido em outras respostas, é realmente uma consideração de desempenho porque a função no protótipo é compartilhada com todas as instanciações - em vez da função que está sendo criada para cada instanciação.
Eu montei um jsperf para mostrar isso. Há uma diferença dramática no tempo necessário para instanciar a classe, embora seja realmente relevante apenas se você estiver criando muitas instâncias.
http://jsperf.com/functions-in-constructor-vs-prototype
fonte
Pense em linguagem de tipo estaticamente, as coisas
prototype
são estáticas e as coisasthis
são relacionadas à instância.fonte