Como o __proto__ difere do constructor.prototype?

163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Ele sempre retorna o objeto com classificação = 3.

Mas se eu fizer o seguinte:

newtoy.__proto__.__proto__.__proto__

A corrente acaba retornando null.

Também no Internet Explorer, como verificaria o nulo se não houver uma __proto__propriedade?

xdevel2000
fonte
30
Este diagrama gráfico ajudará você a entender a diferença entre protótipo e proto . Você pode seguir o proto cadeia de objeto newtoy, e então você vai perceber por que razão o 3º Proto de newtoy é nulo.
bits
Também está claro no diagrama que newtoy.prototype não é igual newtoy.constructor.prototypee, portanto newtoy.constructor.prototype, não terá propriedade chamadarating . Da mesma forma newtoy.constructor.prototype.constructor.propertytambém não terá propriedade chamada rating.
bits
Erro de digitação no último comentário: portanto, newtoy.constructor.prototypehaverá propriedade chamada classificação. Da mesma forma newtoy.constructor.prototype.constructor.propertytambém terá propriedade chamada classificação.
bits
1
@Royi Namir Fiz upload jsViz no github. Aqui está o site de demonstração . Por favor, não se preocupe com o grau de manutenção (e sujeira) do código real. É um projeto super antigo que eu não toquei para sempre.
Bits

Respostas:

210

Eu estive tentando entender isso recentemente e finalmente criei esse "mapa" que acho que lança luz sobre o assunto

http://i.stack.imgur.com/KFzI3.png insira a descrição da imagem aqui

Eu sei que não sou o primeiro a inventar isso, mas foi mais interessante descobrir do que encontrar :-). Enfim, depois disso eu encontrei, por exemplo, outro diagrama que acho que diz basicamente o mesmo:

Layout de objeto Javascript

A coisa mais surpreendente para mim foi descobrir que Object.__proto__aponta para Function.prototype, em vez de Object.prototype, mas tenho certeza de que há uma boa razão para isso :-)

Eu colo o código mencionado na imagem aqui também para se alguém quiser testá-lo. Observe que algumas propriedades são adicionadas aos objetos para facilitar saber onde estamos depois de alguns saltos:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);
drodsou
fonte
2
@utsaina Muito legal. Confira outra representação gráfica do código que o OP postou. E acho que nossos diagramas estão de acordo em termos de detalhes técnicos.
bits
43
A razão pela qual Object.__proto__aponta para Function.prototypeé porque, Object()por si só, é uma função nativa que instancia um objeto vazio. Portanto, Object()é uma função. Você verá que todas as outras __proto__propriedades dos principais tipos nativos apontam para Function.prototype. Object, Function, String, Number, E Arraytodos herdar o protótipo da função.
giratória
@drodsou seu segundo link é incrível. Verifique agora, por favor;) mollypages.org/misc/js.mp Boa explicação: D
abhisekp 1/16/16
@Swivel "Portanto, Object () é uma função" - você quis dizer que Object é uma função? sem ()
giorgim 9/16/16
2
@GiorgiMoniava Correct. Objectem si é uma função; o resultado da execução de chamada Object(ou seja, valor de retorno da execução Object()) não é uma função.
giratória
67

constructoré uma propriedade [[DontEnum]] predefinida do objeto apontado pela prototypepropriedade de um objeto de função e inicialmente apontará para o próprio objeto de função.

__proto__ é equivalente à propriedade interna [[Prototype]] de um objeto, ou seja, seu protótipo real.

Quando você cria um objeto com o newoperador, sua propriedade [[Prototype]] interna será definida como o objeto apontado pela prototypepropriedade da função do construtor .

Isso significa que .constructoravaliaremos a .__proto__.constructorfunção construtora usada para criar o objeto e, como aprendemos, a protoypepropriedade dessa função foi usada para definir o [[Protótipo]] do objeto.

Daqui resulta que .constructor.prototype.constructoré idêntico a .constructor(desde que essas propriedades não tenham sido substituídas); veja aqui para uma explicação mais detalhada.

Se __proto__estiver disponível, você pode percorrer a cadeia de protótipos real do objeto. Não há como fazer isso no ECMAScript3 simples, porque o JavaScript não foi projetado para hierarquias profundas de herança.

Christoph
fonte
3
Esse link 'aqui' é o padrão-ouro. Vá para lá se quiser a descrição completa.
Ricalsin
Boa captura com .constructor.prototypeencadeamento. Eu também não estava claro para mim, enquanto não via que isso .constructoré igual .__proto__.constructor. O que significa simplesmente alternar entre a função do construtor e seu protótipo.
Johnny_D
30

A herança prototípica no JavaScript é baseada na __proto__propriedade, no sentido de que cada objeto está herdando o conteúdo do objeto referenciado por sua __proto__propriedade.

A prototypepropriedade é especial apenas para Functionobjetos e somente ao usar o newoperador para chamar um Functioncomo construtor. Nesse caso, os objetos criados __proto__serão configurados como construtores Function.prototype.

Isso significa que adicionar a Function.prototyperefletirá automaticamente sobre todos os objetos aos quais __proto__está fazendo referência Function.prototype.

Substituir construtores Function.prototypepor outro objeto não atualizará a __proto__propriedade de nenhum dos objetos já existentes.

Observe que a __proto__propriedade não deve ser acessada diretamente, Object.getPrototypeOf (object) deve ser usado.

Para responder à primeira pergunta, criei um diagrama __proto__e prototypereferências sob medida , infelizmente o stackoverflow não me permite adicionar a imagem com "menos de 10 reputação". Talvez outra hora.

[Editar] A figura usa em [[Prototype]]vez de __proto__porque é assim que a especificação ECMAScript se refere a objetos internos. Espero que você possa descobrir tudo.

Aqui estão algumas dicas para ajudar você a entender a figura:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Observe que a constructorpropriedade não existe nos objetos criados, mas é herdada do protótipo.

insira a descrição da imagem aqui

xorcus
fonte
@ xorcus Você pode explicar isso: new MyFunction()cria uma instância de objeto à qual __proto__deve se referir ao seu protótipo de ctor, que é MyFunction.prototype.Então, por que se MyFunction.prototype.__proto__refere Object.prototype? deve referene (como minha primeira amostra) para o protótipo da sua ctor que é MyFunction.prototype(note que MyFunction.prototypeé um instnace de Myfunction)
Royi Namir
@Royi Namir: MyFunction.prototype .__ proto__ refere-se a Object.prototype porque MyFunction.prototype é um objeto. O Object.prototype é herdado por todos os objetos (normalmente, é aí que a cadeia de herança do protótipo termina). Não concordo que MyFunction.prototype seja uma instância de MyFunction. obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype existe na cadeia de protótipos obj. Isso não é o caso para MyFunction.prototype objeto
xorcus
14

Objecté Eva, e Functioné Adão, Adão ( Function) usa seu osso ( Function.prototype) para criar Eva ( Object). Então quem criou Adam ( Function)? - O inventor da linguagem JavaScript :-).

De acordo com a resposta da utsaina, quero adicionar mais informações úteis.

A coisa mais surpreendente para mim foi descobrir que Object.__proto__ aponta para Function.prototype, em vez de Object.prototype, mas tenho certeza de que há uma boa razão para isso :-)

Não deveria ser. Object.__proto__NÃO deve apontar para Object.prototype. Em vez disso, a instância de Object o, o.__proto__deve apontar paraObject.prototype .

(Perdoe-me por usar os termos classeinstance em JavaScript, mas você sabe disso :-)

Eu acho que a classe em Objectsi é uma instância Function, é por isso Object.__proto__ === Function.prototype. Portanto: Objecté Eva, e Functioné Adão, Adão ( Function) usa seu osso ( Function.prototype) para criar Eva (Object ).

Além disso, até a Functionprópria classe é uma instância de Functionsi mesma, ou seja,Function.__proto__ === Function.prototype , é também por isso queFunction === Function.constructor

Além disso, a classe regular Caté uma instância de Function, isto é Cat.__proto__ === Function.prototype.

A razão para o exposto acima é que, quando criamos uma classe em JavaScript, na verdade, estamos apenas criando uma função, que deve ser uma instância de Function. Objecte Functionsão apenas especiais, mas ainda são aulas, enquanto Caté uma aula regular.

Por uma questão de fator, no mecanismo JavaScript do Google Chrome, os seguintes 4:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Eles são todos ===(absolutamente iguais) aos outros 3, e seu valor éfunction Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

ESTÁ BEM. Então quem cria o special function Empty() {}( Function.prototype)? Pense nisso :-)

Peter Lee
fonte
Concordo com isso, exceto pela última coisa: a que function Empty() {}você se refere como sendo igual a Function.prototype, etc ?, qual é o código que você usou no console do Chrome?
drodsou
2
Corrigi a última coisa que você apontou. O valor deles está function Empty() {}no Google Chrome. Eu também adicionei a saída do console.
31812 Peter Lee
todas as funções são instanceof Function e, portanto, todas as funções herdam ( _ _proto_ _) do Function.prototype. É tão simples como isso :)
xorcus
Desculpe por comentar sobre tópicos antigos. Mas eles são criados pelo Inventor of Language?
Patel Parth
6

Realmente não sei por que as pessoas não o corrigiram sobre o problema real de sua compreensão.

Isso facilitaria muito a localização do problema

Então, vamos ver o que está acontecendo:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Ótimo, agora vamos ver isso __proto__

Antes disso, lembre-se de duas coisas sobre __proto__ :

  1. Quando você cria um objeto com o newoperador, seu interno [[Prototype]]/ proto__propriedade será definido como a prototypepropriedade (1) do seu constructor functionou "criador", se desejar.

  2. Codificado no JS -: Object.prototype.__proto__is null.

Vamos nos referir a esses 2 pontos como " bill"

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Melhor?

Royi Namir
fonte
2

Todas as funções criam seu protótipo. E quando criamos um objeto usando esse construtor de funções, a propriedade __proto__ do meu objeto começa a apontar para o protótipo dessa função.

Apoorv Nag
fonte
1
Eu acho que você quis dizer a __proto__propriedade.
Demisx
Sim. Eu quis dizer propriedade proto de um objeto. Espero que a informação tenha sido útil.
Apoorv Nag
2

Se todos esses números forem impressionantes, vamos dar uma olhada no significado das propriedades.

STH.prototype

Ao criar uma nova função, há um objeto vazio sendo criado em paralelo e vinculado à função com [[Prototype]]cadeia. Para acessar esse objeto, usamos a prototypepropriedade da função

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Ter em mente que prototype propriedade está disponível apenas para funções.

STH.constructor

O objeto de protótipo mencionado acima não possui propriedades, exceto uma - constructor. Esta propriedade representa uma função que criou o objeto protótipo.

var toy = new Gadget();

Ao criar a Gadgetfunção, também criamos um objeto {constructor: Gadget}- que não é nada parecido Gadget.prototype. Como constructorse refere a uma função que criou um protótipo de objeto, toy.constructorrepresenta a Gadgetfunção. Nós escrevemos toy.constructor.prototypee estamos recebendo{constructor: Gadget} novamente.

Portanto, há um círculo vicioso: você pode usar toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypee sempre será Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

Enquanto prototypeé uma propriedade específica para funções, __proto__está disponível para todos os objetos em que está inserida Object.prototype. Refere-se ao protótipo de uma função que pode criar um objeto.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Aqui toy.__proto__está Gadget.prototype. Como Gadget.prototypeé um objeto ( {}) e os objetos são criados com a Objectfunção (veja o exemplo acima), obtemos Object.prototype. Este é o objeto mais alto no JavaScript e __proto__só pode indicar null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain
Damian Czapiewski
fonte
0

Resposta curta: __proto__é uma referência à prototypepropriedade do construtor que criou o objeto.

Objetos em JavaScript

Um objeto JavaScript é um tipo interno para uma coleção de zero ou mais propriedades. Propriedades são contêineres que contêm outros objetos, valores primitivos ou funções.

Construtores em JavaScript

Funções são objetos regulares (implementados [[Call]]nos termos ECMA-262) com a capacidade adicional de serem chamados, mas desempenham outro papel no JavaScript: eles se tornam construtores ( fábricas de objetos) se invocados por meio donew operador. Os construtores são, portanto, uma analogia grosseira às classes em outros idiomas.

Toda função JavaScript é na verdade uma instância do Functionobjeto de função interno que possui uma propriedade especial denominada prototypeusada para implementar a herança baseada em protótipo e as propriedades compartilhadas. Todo objeto criado por uma função construtora tem uma referência implícita (chamada protótipo ou __proto__) ao valor de seu construtor prototype.

O construtor prototypeé uma espécie de blueprint para a construção de objetos, pois todo objeto criado pelo construtor herda uma referência a suaprototype .

A cadeia de protótipos

Um objeto especifica seu protótipo através da propriedade interna [[Prototype]]ou __proto__. O relacionamento do protótipo entre dois objetos é sobre herança: todo objeto pode ter outro objeto como seu protótipo. O protótipo pode ser o nullvalor.

A cadeia de objetos conectados pela __proto__propriedade é chamada de cadeia de protótipo . Quando é feita uma referência a uma propriedade em um objeto, essa referência é à propriedade encontrada no primeiro objeto na cadeia de protótipos que contém uma propriedade com esse nome. A cadeia de protótipos se comporta como se fosse um único objeto.

Veja esta imagem (extraída deste blog ):

proto.jpg

Sempre que você tenta acessar uma propriedade em um objeto, o JavaScript inicia a busca por ela nesse objeto e continua com seu protótipo, o protótipo do protótipo e assim por diante até que a propriedade seja encontrada ou __proto__mantenha o valor null.

Esse tipo de herança usando a cadeia de protótipos geralmente é chamado de delegação para evitar confusão com outros idiomas usando a cadeia de classes.

Quase todos os objetos são instâncias de Object, porque Object.prototypeé o último em sua cadeia de protótipos. Mas Object.prototypenão é um exemplo de Objectporque Object.prototype.__proto__detém o valor null.

Você também pode criar um objeto com um nullprotótipo como este:

var dict = Object.create(null);

Tal objeto é um mapa melhor (dicionário) de um objeto literal, razão pela qual este padrão é às vezes chamado de dict padrão ( Dict para dicionário).

Nota: os objetos literais criados usando {}são instâncias de Objectsince ({}).__proto__é uma referência a Object.prototype.

eigenslacker
fonte
Cite a fonte das citações e artefatos que você está usando. A imagem parece vir de giamir.com/pseudoclasses-and-prototypal-herdance-in-JS , você tem direitos autorais?
Bergi 01/06
@ Bergi citei a fonte da imagem. A maioria das citações que usei são extraídas do padrão JS ou do MDN
eigenslacker