Estou tentando entender os bastidores do Javascript e meio que entendi a criação de objetos incorporados, especialmente Objeto e Função e a relação entre eles.
É complicado, é fácil entender mal, e muitos livros Javascript para iniciantes entendem errado, portanto, não confie em tudo que lê.
Eu fui um dos implementadores do mecanismo JS da Microsoft na década de 90 e no comitê de padronização, e cometi vários erros ao montar essa resposta. (Embora eu não trabalhe nisso há mais de 15 anos, talvez eu possa ser perdoado.) É uma coisa complicada. Mas depois que você entende a herança do protótipo, tudo faz sentido.
Quando li que todos os objetos incorporados, como Array, String etc., são extensão (herdada) do Object, assumi que Object é o primeiro objeto incorporado que é criado e o restante dos objetos herda.
Comece jogando fora tudo o que você sabe sobre herança baseada em classe. JS usa herança baseada em protótipo.
Em seguida, verifique se você tem uma definição muito clara do que "herança" significa. As pessoas acostumadas a linguagens OO como C # ou Java ou C ++ acham que herança significa subtipagem, mas herança não significa subtipagem. Herança significa que os membros de uma coisa também são membros de outra coisa . Isso não significa necessariamente que exista uma relação de subtipagem entre essas coisas! Muitos mal-entendidos na teoria dos tipos são o resultado de pessoas que não percebem que há uma diferença.
Mas não faz sentido quando você descobre que os Objetos só podem ser criados por funções, mas as funções também não passam de objetos da Função.
Isto é simplesmente falso. Alguns objetos não são criados chamando new F
por alguma função F
. Alguns objetos são criados pelo tempo de execução JS do nada. Existem ovos que não foram postos por nenhuma galinha . Eles foram criados apenas pelo tempo de execução quando ele foi iniciado.
Digamos quais são as regras e talvez isso ajude.
- Toda instância de objeto possui um objeto de protótipo.
- Em alguns casos, esse protótipo pode ser
null
.
- Se você acessar um membro em uma instância de objeto e o objeto não tiver esse membro, o objeto será diferido para seu protótipo ou será interrompido se o protótipo for nulo.
- O
prototype
membro de um objeto normalmente não é o protótipo do objeto.
- Em vez disso, o
prototype
membro de um objeto de função F é o objeto que se tornará o protótipo do objeto criado pornew F()
.
- Em algumas implementações, as instâncias obtêm um
__proto__
membro que realmente fornece seu protótipo. (Isso agora está obsoleto. Não confie nele.)
- Os objetos de função recebem um novo objeto padrão atribuído ao
prototype
serem criados.
- O protótipo de um objeto de função é, é claro
Function.prototype
.
Vamos resumir.
- O protótipo de
Object
éFunction.prototype
Object.prototype
é o objeto de protótipo de objeto.
- O protótipo de
Object.prototype
énull
- O protótipo de
Function
é Function.prototype
- esta é uma das raras situações em que Function.prototype
é realmente o protótipo de Function
!
Function.prototype
é o objeto de protótipo de função.
- O protótipo de
Function.prototype
éObject.prototype
Vamos supor que façamos uma função Foo.
- O protótipo de
Foo
é Function.prototype
.
Foo.prototype
é o objeto de protótipo Foo.
- O protótipo de
Foo.prototype
é Object.prototype
.
Vamos supor que dizemos new Foo()
- O protótipo do novo objeto é
Foo.prototype
Certifique-se de que faz sentido. Vamos desenhar. Ovais são instâncias de objetos. Arestas __proto__
significam "o protótipo de" ou prototype
significam "a prototype
propriedade de".
Tudo o que o tempo de execução precisa fazer é criar todos esses objetos e atribuir suas várias propriedades de acordo. Tenho certeza que você pode ver como isso seria feito.
Agora vamos ver um exemplo que testa seu conhecimento.
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
O que isso imprime?
Bem, o que instanceof
significa? honda instanceof Car
significa "é Car.prototype
igual a qualquer objeto emhonda
cadeia de protótipos?"
Sim, ele é. honda
O protótipo deCar.prototype
, então terminamos. Isso é verdadeiro.
E o segundo?
honda.constructor
não existe, então consultamos o protótipo, que é Car.prototype
. Quando o Car.prototype
objeto foi criado, recebeu automaticamente uma propriedade constructor
igual a Car
, portanto isso é verdade.
Agora e quanto a isso?
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
O que esse programa imprime?
Novamente, lizard instanceof Reptile
significa "é Reptile.prototype
igual a qualquer objeto na lizard
cadeia de protótipos?"
Sim, ele é. lizard
O protótipo éReptile.prototype
, então terminamos. Isso é verdadeiro.
Agora, que tal
print(lizard.constructor == Reptile);
Você pode pensar que isso também é verdadeiro, pois lizard
foi construído com, new Reptile
mas você estaria errado. Razão disso.
- Tem
lizard
uma constructor
propriedade? Não. Portanto, olhamos para o protótipo.
- O protótipo de
lizard
é Reptile.prototype
, que é Animal
.
- Tem
Animal
umconstructor
propriedade? Não. Então, olhamos para o seu protótipo.
- O protótipo de
Animal
é Object.prototype
e Object.prototype.constructor
é criado pelo tempo de execução e igual a Object
.
- Então isso é falso.
Deveríamos ter dito Reptile.prototype.constructor = Reptile;
em algum momento, mas não nos lembramos!
Certifique-se de que tudo faça sentido para você. Desenhe algumas caixas e setas se ainda estiver confuso.
A outra coisa extremamente confusa é que, se eu console.log(Function.prototype)
imprime uma função, mas quando imprimo console.log(Object.prototype)
, imprime um objeto. Por que Function.prototype
uma função quando deveria ser um objeto?
O protótipo de função é definido como uma função que, quando chamada, retorna undefined
. Já sabemos que esse Function.prototype
é o Function
protótipo, por incrível que pareça. Portanto, Function.prototype()
é legal, e quando você faz isso, você undefined
volta. Então é uma função.
O Object
protótipo não possui essa propriedade; não é exigível. É apenas um objeto.
quando você console.log(Function.prototype.constructor)
é novamente uma função.
Function.prototype.constructor
é apenas Function
, obviamente. E Function
é uma função.
Agora, como você pode usar algo para criar a si mesmo (Mente = queimado).
Você está pensando demais nisso . Tudo o que é necessário é que o tempo de execução crie vários objetos ao iniciar. Objetos são apenas tabelas de pesquisa que associam seqüências de caracteres a objetos. Quando o tempo de execução inicia-se, tudo o que tem a fazer é criar alguns objetos dúzia em branco e, em seguida, começar a atribuir a prototype
, __proto__
, constructor
, e assim por diante propriedades de cada objeto até que eles fazem o gráfico que eles precisam fazer.
Será útil se você pegar o diagrama que eu lhe dei acima e adicionar constructor
arestas a ele. Você verá rapidamente que este é um gráfico de objeto muito simples e que o tempo de execução não terá problemas para criá-lo.
Um bom exercício seria fazer você mesmo. Aqui, eu vou começar você. Usaremos my__proto__
para significar "o objeto protótipo de" e myprototype
significar "a propriedade protótipo de".
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
E assim por diante. Você pode preencher o restante do programa para construir um conjunto de objetos com a mesma topologia dos objetos internos "reais" do Javascript? Se você fizer isso, verá que é extremamente fácil.
Objetos em JavaScript são apenas tabelas de pesquisa que associam seqüências de caracteres a outros objetos . É isso aí! Não há mágica aqui. Você está se amarrando porque está imaginando restrições que realmente não existem, como se todo objeto tivesse que ser criado por um construtor.
Funções são apenas objetos que possuem uma capacidade adicional: a serem chamados. Portanto, siga seu pequeno programa de simulação e adicione uma .mycallable
propriedade a cada objeto que indique se é possível chamar ou não. É simples assim.
Function.prototype
pode ser uma função e ter campos internos. Portanto, não, você não executa a função prototype ao passar por sua estrutura. Por fim, lembre-se de que existe um mecanismo para interpretar Javascript; portanto, Objeto e Função provavelmente são criados dentro do mecanismo e não a partir de Javascript e referências especiais comoFunction.prototype
eObject.prototype
podem ser interpretados de maneira especial pelo mecanismo.