Não gosto muito de linguagens de programação dinâmicas, mas escrevi meu quinhão de código JavaScript. Eu realmente nunca entendi essa programação baseada em protótipo, alguém sabe como isso funciona?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
Lembro-me de muita discussão que tive com as pessoas há um tempo (não sei exatamente o que estou fazendo), mas, pelo que entendi, não há conceito de classe. É apenas um objeto, e as instâncias desses objetos são clones do original, certo?
Mas qual é o objetivo exato dessa propriedade ".prototype" em JavaScript? Como isso se relaciona a instanciar objetos?
Atualização: maneira correta
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
Também esses slides realmente ajudaram bastante.
javascript
dynamic-languages
prototype-oriented
John Leidegren
fonte
fonte
Respostas:
Todo objeto JavaScript possui um "slot" interno chamado
[[Prototype]]
cujo valor é umnull
ou umobject
. Você pode pensar em um slot como uma propriedade em um objeto, interno ao mecanismo JavaScript, oculto no código que você escreve. Os colchetes ao redor[[Prototype]]
são deliberados e são uma convenção de especificação ECMAScript para indicar slots internos.O valor apontado pelo
[[Prototype]]
de um objeto é conhecido coloquialmente como "o protótipo desse objeto".Se você acessar uma propriedade através da notação dot (
obj.propName
) ou bracket (obj['propName']
), e o objeto não tiver essa propriedade diretamente (ou seja, uma propriedade própria , verificável viaobj.hasOwnProperty('propName')
), o tempo de execução procurará uma propriedade com esse nome no objeto referenciado pelo[[Prototype]]
invés. Se o[[Prototype]]
também não tiver essa propriedade, ela[[Prototype]]
será verificada por sua vez, e assim por diante. Dessa forma, a cadeia de protótipos do objeto original é percorrida até que uma correspondência seja encontrada ou seu fim seja alcançado. No topo da cadeia de protótipos está onull
valor.As implementações modernas de JavaScript permitem acesso de leitura e / ou gravação ao
[[Prototype]]
, das seguintes maneiras:new
operador (configura a cadeia de protótipo no objeto padrão retornado de uma função de construtor),extends
palavra-chave (configura a cadeia de protótipos ao usar a sintaxe da classe),Object.create
definirá o argumento fornecido como o[[Prototype]]
do objeto resultante,Object.getPrototypeOf
eObject.setPrototypeOf
(obter / definir a criação do objeto[[Prototype]]
após ), e__proto__
(semelhante a 4.)Object.getPrototypeOf
eObject.setPrototypeOf
são preferidos__proto__
, em parte porque o comportamento deo.__proto__
é incomum quando um objeto possui um protótipo denull
.Um objeto
[[Prototype]]
é definido inicialmente durante a criação do objeto.Se você criar um novo objeto via
new Func()
,[[Prototype]]
por padrão, o objeto será definido como o objeto referenciado porFunc.prototype
.Observe que, portanto, todas as classes e todas as funções que podem ser usadas com o
new
operador têm uma propriedade nomeada.prototype
além de seu próprio[[Prototype]]
slot interno. Esse uso duplo da palavra "protótipo" é fonte de confusão sem fim entre os recém-chegados ao idioma.O uso
new
com funções de construtor nos permite simular herança clássica em JavaScript; embora o sistema de herança do JavaScript seja - como vimos - prototípico, e não baseado em classes.Antes da introdução da sintaxe da classe ao JavaScript, as funções do construtor eram a única maneira de simular classes. Podemos pensar nas propriedades do objeto referenciado pela
.prototype
propriedade da função construtora como membros compartilhados; ie membros iguais para cada instância. Em sistemas baseados em classes, os métodos são implementados da mesma maneira para cada instância, portanto, métodos são conceitualmente adicionados ao.prototype
propriedade; os campos de um objeto, no entanto, são específicos da instância e, portanto, são adicionados ao próprio objeto durante a construção.Sem a sintaxe da classe, os desenvolvedores precisavam configurar manualmente a cadeia de protótipos para obter funcionalidade semelhante à herança clássica. Isso levou a uma preponderância de diferentes maneiras de conseguir isso.
Aqui está uma maneira:
... e aqui está outra maneira:
A sintaxe da classe introduzida no ES2015 simplifica as coisas, fornecendo
extends
como "a única maneira verdadeira" de configurar a cadeia de protótipos para simular a herança clássica em JavaScript.Portanto, semelhante ao código acima, se você usar a sintaxe da classe para criar um novo objeto como este:
... o objeto resultante
[[Prototype]]
será definido como uma instância deParent
cuja[[Prototype]]
, por sua vez, éParent.prototype
.Finalmente, se você criar um novo objeto via
Object.create(foo)
, o objeto resultante[[Prototype]]
será definido comofoo
.fonte
Em uma linguagem que implementa herança clássica como Java, C # ou C ++, você começa criando uma classe - um blueprint para seus objetos - e pode criar novos objetos a partir dessa classe ou pode estender a classe, definindo uma nova classe que aprimora a classe original.
No JavaScript, você primeiro cria um objeto (não há conceito de classe) e, em seguida, pode aumentar seu próprio objeto ou criar novos objetos a partir dele. Não é difícil, mas um pouco estranho e difícil de metabolizar para alguém acostumado à maneira clássica.
Exemplo:
Até agora estendi o objeto base, agora crio outro objeto e depois herdo de Person.
Mostrar snippet de código
Enquanto, como disse, não posso chamar setAmountDue (), getAmountDue () em uma Pessoa.
fonte
Customer.prototype = new Person();
linha, o MDN mostra um exemplo usandoCustomer.prototype = Object.create(Person.prototype)
e afirma que 'Um erro comum aqui é usar "new Person ()"' . fonteEste é um modelo de objeto baseado em protótipo muito simples que seria considerado uma amostra durante a explicação, sem comentários ainda:
Há alguns pontos cruciais que devemos considerar antes de passar pelo conceito de protótipo.
1- Como as funções JavaScript realmente funcionam:
Para dar o primeiro passo, precisamos descobrir como as funções JavaScript realmente funcionam, como uma classe como uma função usando
this
palavra-chave ou apenas como uma função regular com seus argumentos, o que faz e o que retorna.Digamos que queremos criar um
Person
modelo de objeto. mas nesta etapa tentarei fazer exatamente a mesma coisa sem usar uma palavraprototype
-new
chave .Portanto, nesta etapa
functions
,objects
ethis
palavra - chave, são tudo o que temos.A primeira pergunta seria como a
this
palavra-chave poderia ser útil sem usar anew
palavra-chave .Então, para responder isso, digamos que temos um objeto vazio e duas funções como:
e agora sem usar a
new
palavra-chave como poderíamos usar essas funções. Portanto, o JavaScript tem três maneiras diferentes de fazer isso:uma. A primeira maneira é apenas chamar a função como uma função regular:
nesse caso, esse seria o objeto de contexto atual, que geralmente é o
window
objeto global no navegador ouGLOBAL
noNode.js
. Isso significa que teríamos window.name no navegador ou GLOBAL.name no Node.js, com "George" como seu valor.b. Podemos anexá- los a um objeto, como suas propriedades
- A maneira mais fácil de fazer isso é modificar o
person
objeto vazio , como:Desta forma, podemos chamá-los como:
e agora o
person
objeto é como:- A outra maneira de anexar uma propriedade a um objeto é usar o
prototype
objeto que pode ser encontrado em qualquer objeto JavaScript com o nome de__proto__
, e tentei explicar um pouco na parte de resumo. Para que pudéssemos obter o resultado semelhante, fazendo:Mas, dessa maneira, o que realmente estamos fazendo é modificar o arquivo
Object.prototype
, porque sempre que criamos um objeto JavaScript usando literals ({ ... }
), ele é criado com base emObject.prototype
, o que significa que ele é anexado ao objeto recém-criado como um atributo chamado__proto__
, portanto, se o alterarmos , como fizemos no snippet de código anterior, todos os objetos JavaScript seriam alterados, não é uma boa prática. Então, qual poderia ser a melhor prática agora:e agora outros objetos estão em paz, mas ainda não parece ser uma boa prática. Portanto, ainda temos mais uma solução, mas, para usá-la, devemos retornar à linha de código em que o
person
objeto foi criado (var person = {};
) e alterá-lo da seguinte forma:o que ele faz é criar um novo JavaScript
Object
e anexápropertiesObject
-lo ao__proto__
atributo Portanto, para garantir que você possa fazer:Mas o ponto complicado aqui é que você tem acesso a todas as propriedades definidas no
__proto__
primeiro nível doperson
objeto (leia a parte do resumo para obter mais detalhes).como você vê, usando qualquer uma dessas duas direções
this
apontaria exatamente para operson
objeto.c. O JavaScript tem outra maneira de fornecer a função
this
, que está usando call ou apply para invocar a função.e
dessa maneira, que é a minha favorita, podemos chamar facilmente nossas funções como:
ou
esses três métodos são as etapas iniciais importantes para descobrir a funcionalidade do protótipo.
2- Como a
new
palavra - chave funciona?este é o segundo passo para entender a
.prototype
funcionalidade. é isso que eu uso para simular o processo:Nesta parte, tentarei executar todas as etapas que o JavaScript executar, sem usar a
new
palavra - chave eprototype
, quando você usar anew
palavra-chave. então, quando o fazemosnew Person("George")
, aPerson
função serve como construtor. É isso que o JavaScript faz, um por um:uma. Antes de tudo, ele cria um objeto vazio, basicamente um hash vazio como:
b. a próxima etapa que o JavaScript executa é anexar todos os objetos de protótipo ao objeto recém-criado
temos
my_person_prototype
aqui semelhante ao objeto protótipo.Não é assim que o JavaScript realmente anexa as propriedades definidas no protótipo. A maneira atual está relacionada ao conceito de cadeia de protótipos.
uma. & b. Em vez destas duas etapas, você pode obter exatamente o mesmo resultado:
agora podemos chamar a
getName
função em nossomy_person_prototype
:c. então ele fornece esse objeto ao construtor,
podemos fazer isso com nossa amostra como:
ou
então o construtor pode fazer o que quiser, porque esse interior desse construtor é o objeto que acabou de ser criado.
agora o resultado final antes de simular as outras etapas: Objeto {nome: "George"}
Resumo:
Basicamente, quando você usa a nova palavra-chave em uma função, você está chamando isso e essa função serve como construtor; portanto, quando você diz:
O JavaScript cria internamente um objeto, um hash vazio e, em seguida, fornece esse objeto ao construtor; então, o construtor pode fazer o que quiser, porque isso dentro desse construtor é o objeto que acabou de ser criado e, em seguida, fornece esse objeto, é claro se você não usou a instrução de retorno em sua função ou se você colocou um
return undefined;
no final do corpo da função.Portanto, quando o JavaScript procura uma propriedade em um objeto, a primeira coisa que faz é procurá-lo nesse objeto. E depois há uma propriedade secreta da
[[prototype]]
qual costumamos ter__proto__
e é essa a aparência do JavaScript a seguir. E quando olha através do__proto__
, na medida em que é outro objeto JavaScript, ele tem seu próprio__proto__
atributo, sobe e sobe até chegar ao ponto em que o próximo__proto__
é nulo. O ponto é o único objeto no JavaScript que seu__proto__
atributo é nulo é oObject.prototype
objeto:e é assim que a herança funciona em JavaScript.
Em outras palavras, quando você tem uma propriedade prototype em uma função e chama uma nova, depois que o JavaScript terminar de examinar o objeto recém-criado por propriedades, ele analisará as propriedades da função
.prototype
e também é possível que esse objeto tenha sua propriedade. próprio protótipo interno. e assim por diante.fonte
__proto__
você primeiro apagará todas as propriedades de protótipo de nível superior e, em seguida, terá uma nova base de protótipos que não será mais compartilhada, a menos que você as compartilhe.Os sete Koans do protótipo
Enquanto Ciro San descia o Monte Fire Fox após meditação profunda, sua mente estava clara e pacífica.
Sua mão, no entanto, estava inquieta, e por si mesma pegou um pincel e anotou as seguintes notas.
0) Duas coisas diferentes podem ser chamadas de "protótipo":
a propriedade prototype, como em
obj.prototype
a propriedade interna do protótipo, indicada como
[[Prototype]]
no ES5 .Pode ser recuperado através do ES5
Object.getPrototypeOf()
.O Firefox o torna acessível através da
__proto__
propriedade como uma extensão. O ES6 agora menciona alguns requisitos opcionais para__proto__
.1) Esses conceitos existem para responder à pergunta:
Intuitivamente, a herança clássica deve afetar a pesquisa de propriedades.
2)
__proto__
é usado para a.
pesquisa de propriedades do ponto como emobj.property
..prototype
não é usado para pesquisa diretamente, apenas indiretamente, pois determina__proto__
na criação do objetonew
.A ordem de pesquisa é:
obj
propriedades adicionadas comobj.p = ...
ouObject.defineProperty(obj, ...)
obj.__proto__
obj.__proto__.__proto__
e assim por diante__proto__
houvernull
, volteundefined
.Essa é a chamada cadeia de protótipos .
Você pode evitar a
.
pesquisa comobj.hasOwnProperty('key')
eObject.getOwnPropertyNames(f)
3) Existem duas maneiras principais de definir
obj.__proto__
:new
:então
new
definiu:Este é o lugar onde
.prototype
se acostuma.Object.create
:conjuntos:
4) O código:
Corresponde ao seguinte diagrama (algumas
Number
coisas são omitidas):Este diagrama mostra muitos nós de objetos predefinidos por idioma:
null
Object
Object.prototype
Function
Function.prototype
1
Number.prototype
(pode ser encontrado com(1).__proto__
parênteses obrigatórios para satisfazer a sintaxe)Nossas duas linhas de código criaram apenas os seguintes novos objetos:
f
F
F.prototype
i
agora é uma propriedade def
porque quando você faz:avalia
F
comothis
sendo o valor quenew
retornará, o qual será atribuídof
.5)
.constructor
normalmente vemF.prototype
através da.
pesquisa:Quando escrevemos
f.constructor
, o JavaScript faz a.
pesquisa como:f
não tem.constructor
f.__proto__ === F.prototype
tem.constructor === F
, então pegueO resultado
f.constructor == F
é intuitivamente correto, poisF
é usado para construirf
, por exemplo, definir campos, como nos idiomas clássicos de POO.6) A sintaxe clássica de herança pode ser alcançada através da manipulação de cadeias de protótipos.
O ES6 adiciona as palavras
class
-extends
chave e , que são principalmente açúcar de sintaxe para a loucura de manipulação de protótipo anteriormente possível.Diagrama simplificado sem todos os objetos predefinidos:
Vamos dedicar um momento para estudar como o seguinte funciona:
Os primeiros conjuntos de linha
c.i
para1
como explicado em "4)".Na segunda linha, quando fazemos:
.inc
é encontrado através da[[Prototype]]
cadeia:c
->C
->C.prototype
->inc
X.Y()
, o JavaScript automaticamente éthis
igual aX
dentro daY()
chamada de função!A mesma lógica exata também explica
d.inc
ed.inc2
.Este artigo https://javascript.info/class#not-just-a-syntax-sugar menciona outros efeitos que
class
vale a pena conhecer. Alguns deles podem não ser alcançáveis sem aclass
palavra - chave (TODO, verifique qual):[[FunctionKind]]:"classConstructor"
, o que força o construtor a ser chamado com o novo: Qual é o motivo pelo qual os construtores da classe ES6 não podem ser chamados como funções normais?Object.defineProperty
.use strict
. Pode ser feito com um explícitouse strict
para cada função, que é reconhecidamente entediante.fonte
.
pesquisa funciona (e quantas cópias dos dados são feitas) . Então, decidi entender esse ponto. O resto são Google + posts do blog + um intérprete Js em mãos. :)sets f.constructor = F
peça estava descaradamente errada e contraditória com outras seções:.constructor
é encontrada através da.
pesquisa na cadeia de protótipos. Corrigido agora.f
o protótipo é definidoF
apenas no momento da construção;f
não saberá nem se preocuparáF.prototype
a qualquer momento após sua primeira construção.prototype
permite que você faça aulas. se você não usarprototype
, ele se tornará estático.Aqui está um pequeno exemplo.
No caso acima, você tem um teste de chamada de função estática. Esta função pode ser acessada apenas por obj.test, onde você pode imaginar obj como uma classe.
onde como no código abaixo
O obj tornou-se uma classe que agora pode ser instanciada. Várias instâncias de obj podem existir e todas elas têm a
test
função.O acima é o meu entendimento. Estou criando um wiki da comunidade, para que as pessoas possam me corrigir se eu estiver errado.
fonte
prototype
é uma propriedade das funções do construtor, não das instâncias, ou seja, seu código está errado! Talvez você quis dizer a propriedade não-padrão__proto__
de objetos, mas isso é um animal totalmente diferente ...Depois de ler este tópico, sinto-me confuso com a cadeia de protótipos JavaScript, e encontrei esses gráficos
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
é um gráfico claro para mostrar a herança de JavaScript pela cadeia de protótipos
e
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
este contém um exemplo com código e vários diagramas agradáveis.
Espero que também seja útil para você entender a cadeia de protótipos JavaScript.
fonte
[[Prototype]]
significa?Todo objeto tem uma propriedade interna, [[Prototype]] , vinculando-a a outro objeto:
No javascript tradicional, o objeto vinculado é
prototype
propriedade de uma função:Alguns ambientes expõem [[Prototype]] como
__proto__
:Você cria o link [[Prototype]] ao criar um objeto.
Portanto, essas instruções são equivalentes:
Você não pode ver o link target (
Object.prototype
) em uma nova declaração; em vez disso, o destino é implícito pelo construtor (Object
).Lembrar:
prototype
propriedade, inicialmente mantendo um objeto vazio.prototype
propriedade de seu construtor.prototype
propriedade não será usada.new
.fonte
Object.create()
documentos , @sam. Links para__proto__
eObject.prototype
seriam ótimos aprimoramentos. E gostei dos seus exemplos de como os protótipos funcionam com construtores eObject.create()
, mas provavelmente eram a parte mais longa e menos relevante da qual você queria se livrar.Javascript não tem herança no sentido usual, mas possui a cadeia de protótipos.
cadeia de protótipos
Se um membro de um objeto não puder ser encontrado no objeto, ele será procurado na cadeia de protótipos. A cadeia consiste em outros objetos. O protótipo de uma determinada instância pode ser acessado com a
__proto__
variável Todo objeto tem um, pois não há diferença entre classes e instâncias em javascript.A vantagem de adicionar uma função / variável ao protótipo é que ele precisa estar na memória apenas uma vez, e não em todas as instâncias.
Também é útil para herança, porque a cadeia de protótipos pode consistir em muitos outros objetos.
fonte
Este artigo é longo. Mas tenho certeza de que limpará a maioria das suas consultas sobre a natureza "prototípica" da herança do JavaScript. E ainda mais. Por favor, leia o artigo completo.
JavaScript basicamente tem dois tipos de dados
Não objetos
A seguir estão os tipos de dados Não-objeto
Esses tipos de dados retornam a seguir quando você usa o operador typeof
typeof "string literal" (ou uma variável que contém string literal) === 'string'
typeof 5 (ou qualquer literal numérico ou uma variável contendo literal numérico ou NaN ou Infynity ) === 'número'
typeof true (ou false ou uma variável contendo true ou false ) === 'booleano'
tipo de indefinido (ou uma variável indefinida ou uma variável contendo indefinido ) === 'indefinido'
A string , o número e os tipos de dados booleanos podem ser representados como Objetos e Não objetos . Quando eles são representados como objetos, seu tipo é sempre === 'objeto'. Voltaremos a isso assim que entendermos os tipos de dados do objeto.
Objetos
Os tipos de dados do objeto podem ser divididos em dois tipos
Os objetos do tipo Function são os que retornam a string 'function' com o operador typeof . Todas as funções definidas pelo usuário e todo o JavaScript construído em objetos que podem criar novos objetos usando o novo operador se enquadram nessa categoria. Por exemplo.
Então, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === 'função'
Na verdade, todos os objetos do tipo Function são instâncias do objeto JavaScript incorporado Function (incluindo o objeto Function, isto é, definido recursivamente). É como se esses objetos tivessem sido definidos da seguinte maneira
Como mencionado, os objetos do tipo Função podem criar novos objetos usando o novo operador . Por exemplo, um objeto do tipo Objeto , String , Número , Booleano , Matriz , RegExp ou UserDefinedFunction pode ser criado usando
Os objetos assim criados são todos os objetos do tipo Non Function e retornam seu tipo de === 'objeto' . Em todos esses casos, o objeto "a" não pode mais criar objetos usando o operador new. Então, o seguinte está errado
O objeto interno Math é typeof === 'objeto' . Portanto, um novo objeto do tipo Math não pode ser criado por um novo operador.
Observe também que as funções Objeto , Matriz e RegExp podem criar um novo objeto sem usar o operador novo . No entanto, os seguintes não.
As funções definidas pelo usuário são um caso especial.
Como os objetos do tipo Função podem criar novos objetos, eles também são chamados de Construtores .
Todo Construtor / Função (incorporado ou definido pelo usuário) quando definido automaticamente possui uma propriedade chamada "prototype" cujo valor por padrão é definido como um objeto. Esse objeto em si possui uma propriedade chamada "construtor" que, por padrão, faz referência ao Construtor / Função .
Por exemplo, quando definimos uma função
o seguinte acontece automaticamente
Essa propriedade "prototype" está presente apenas nos objetos do tipo Function (e nunca nos objetos do tipo Non Function ).
Isso ocorre porque, quando um novo objeto é criado (usando o novo operador), ele herda todas as propriedades e métodos do objeto de protótipo atual da função Constructor, ou seja, uma referência interna é criada no objeto recém-criado que faz referência ao objeto referenciado pelo objeto de protótipo atual da função Constructor.
Essa "referência interna" criada no objeto para referenciar propriedades herdadas é conhecida como protótipo do objeto (que faz referência ao objeto referenciado pela propriedade "prototype" do Construtor, mas é diferente dele). Para qualquer objeto (Função ou Não Função), isso pode ser recuperado usando o método Object.getPrototypeOf () . Usando esse método, é possível rastrear a cadeia de protótipos de um objeto.
Além disso, todo objeto criado ( tipo de função ou tipo não funcional ) possui uma propriedade "construtora" que é herdada do objeto referenciado pela propriedade prototype da função construtora. Por padrão, essa propriedade "construtor" faz referência à função Construtor que a criou (se o "protótipo" padrão da Função Construtor não for alterado).
Para todos os objetos do tipo Function, a função construtora é sempre a função Function () {}
Para objetos do tipo Non Function (por exemplo, Javascript Built in Math object) a função construtora é a função que o criou. Para o objeto Math , é a função Object () {} .
Todo o conceito explicado acima pode ser um pouco assustador de entender sem nenhum código de suporte. Por favor, siga o seguinte código linha por linha para entender o conceito. Tente executá-lo para entender melhor.
A cadeia de protótipos de cada objeto é rastreada até Object.prototype (que por si só não possui nenhum objeto de protótipo). O código a seguir pode ser usado para rastrear a cadeia de protótipos de um objeto
A cadeia de protótipos para vários objetos funciona da seguinte maneira.
Para criar um objeto sem nenhum protótipo, use o seguinte:
Pode-se pensar que definir a propriedade prototype do Constructor como null deve criar um objeto com um protótipo null. No entanto, nesses casos, o protótipo do objeto recém-criado é definido como Object.prototype e seu construtor é definido para a função Object. Isso é demonstrado pelo seguinte código
A seguir no resumo deste artigo
Somente objetos do tipo Função podem criar um novo objeto usando o operador new . Os objetos assim criados são objetos do tipo Não Função . Os objetos do tipo Non Function não podem mais criar um objeto usando o operador new .
Todos os objetos do tipo Função, por padrão, têm uma propriedade "prototype" . Essa propriedade "prototype" faz referência a um objeto que possui uma propriedade "constructor" que, por padrão, faz referência ao próprio objeto do tipo Function .
Todos os objetos ( Tipo de função e Tipo não funcional ) têm uma propriedade "construtor" que, por padrão, faz referência ao objeto Tipo de função / Construtor que o criou.
Todo objeto criado internamente faz referência ao objeto referenciado pela propriedade "prototype" do Construtor que o criou. Este objeto é conhecido como protótipo do objeto criado (que é diferente da propriedade "prototype" dos objetos do tipo Função). Dessa maneira, o objeto criado pode acessar diretamente os métodos e propriedades definidos no objeto referenciado pela propriedade "prototype" do Constructor (no momento da criação do objeto).
O protótipo de um objeto (e, portanto, seus nomes de propriedades herdados) pode ser recuperado usando o método Object.getPrototypeOf () . De fato, esse método pode ser usado para navegar por toda a cadeia de protótipos do objeto.
A cadeia de protótipos de cada objeto é rastreada até Object.prototype (a menos que o objeto seja criado usando Object.create (null); nesse caso, o objeto não possui protótipo).
typeof (new Array ()) === 'objeto' é por design da linguagem e não é um erro, como apontado por Douglas Crockford
Definir a propriedade prototype do Constructor como null (ou indefinido, number, true, false, string) não deve criar um objeto com um protótipo nulo. Nesses casos, o protótipo do objeto recém-criado é definido como Object.prototype e seu construtor é definido para funcionar como Object.
Espero que isto ajude.
fonte
O conceito de
prototypal
herança é um dos mais complicados para muitos desenvolvedores. Vamos tentar entender a raiz do problema para entenderprototypal inheritance
melhor. Vamos começar com umaplain
função.Se usarmos um
new
operador noTree function
, chamamos isso deconstructor
função.Toda
JavaScript
função tem aprototype
. Quando você faz logonTree.prototype
, você obtém ...Se você olhar para a
console.log()
saída acima , poderá ver uma propriedade construtoraTree.prototype
e também uma__proto__
propriedade. O__proto__
representa o fato deprototype
que issofunction
se baseia e, como isso é apenas uma planícieJavaScript function
seminheritance
configuração ainda, refere-se aoObject prototype
que é algo que acabou de ser incorporado ao JavaScript ...https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
Isso tem coisas como
.toString, .toValue, .hasOwnProperty
etc ...__proto__
que foi trazido meu mozilla está obsoleto e é substituído peloObject.getPrototypeOf
método para obter oobject's prototype
.Vamos adicionar um método ao nosso
Tree
prototype
.Nós modificamos o
Root
e adicionamos umfunction
ramo a ele.Isso significa que quando você cria um
instance
deTree
, você pode chamá-lo debranch
método.Também podemos adicionar
primitives
ouobjects
ao nossoPrototype
.Vamos adicionar um
child-tree
ao nossoTree
.Aqui o
Child
herdaprototype
da árvore, o que estamos fazendo aqui é usar oObject.create()
método para criar um novo objeto com base no que você passa, aqui estáTree.prototype
. Nesse caso, o que estamos fazendo é definir o protótipo de Child para um novo objeto que pareça idêntico aoTree
protótipo. Em seguida, estamos definindo oChild's constructor to Child
, caso contrário, ele apontariaTree()
.Child
agora tem a sua própriaprototype
, seus__proto__
pontos paraTree
eTree's prototype
pontos para a baseObject
.Agora você cria uma chamada
instance
deChild
ebranch
que está originalmente disponível emTree
. Na verdade, não definimos o nossobranch
noChild prototype
. MAS, noRoot prototype
qual o Filho herda.No JS nem tudo é um objeto, tudo pode funcionar como um objeto.
Javascript
tem primitivos comostrings, number, booleans, undefined, null.
eles não sãoobject(i.e reference types)
, mas certamente podem agir como umobject
. Vamos ver um exemplo aqui.Na primeira linha desta listagem, um
primitive
valor de sequência é atribuído ao nome. A segunda linha trata o nome comoobject
e chamacharAt(0)
usando a notação de ponto.É o que acontece nos bastidores: // o que o
JavaScript
mecanismo fazO
String object
só existe para uma declaração antes de ser destruído (um processo chamadoautoboxing
). Vamos voltar novamente ao nossoprototypal
inheritance
.Javascript
suporta herança viadelegation
baseado emprototypes
.Function
um tem umaprototype
propriedade, que se refere a outro objeto.properties/functions
são olhados a partir deobject
si ou através daprototype
cadeia, se não existirA
prototype
em JS é um objeto queyields
você é o pai de outroobject
. [ie .. delegação]Delegation
significa que se você não conseguir fazer algo, dirá a alguém para fazer isso por você.https://jsfiddle.net/say0tzpL/1/
Se você procurar o violino acima, o cão tem acesso ao
toString
método, mas não está disponível nele, mas disponível através da cadeia de protótipos que delega paraObject.prototype
Se você olhar para o abaixo, estamos tentando acessar o
call
método que está disponível em todosfunction
.https://jsfiddle.net/rknffckc/
Se você procurar o violino acima, o
Profile
Function tem acesso aocall
método, mas não está disponível nele, mas disponível através da cadeia de protótipos que delega paraFunction.prototype
Nota:
prototype
é uma propriedade do construtor da função, enquanto__proto__
é uma propriedade dos objetos construídos a partir do construtor da função. Toda função vem com umaprototype
propriedade cujo valor é um vazioobject
. Quando criamos uma instância da função, obtemos uma propriedade interna[[Prototype]]
ou__proto__
cuja referência é o protótipo da funçãoconstructor
.O diagrama acima parece um pouco complicado, mas mostra toda a imagem de como
prototype chaining
funciona. Vamos passar por isso lentamente:Existem duas instâncias
b1
eb2
, cujo construtor éBar
e o pai é Foo e possui dois métodos da cadeia de protótiposidentify
espeak
viaBar
eFoo
https://jsfiddle.net/kbp7jr7n/
Se você procurar o código acima, temos o
Foo
construtor que possui o métodoidentify()
e oBar
construtor que possui ospeak
método Criamos duasBar
instânciasb1
eb2
cujo tipo pai éFoo
. Agora, enquanto chamamos ospeak
método deBar
, somos capazes de identificar quem está chamando o speak através daprototype
cadeia.Bar
agora tem todos os métodosFoo
definidos em seuprototype
. Vamos aprofundar na compreensão doObject.prototype
eFunction.prototype
e como eles estão relacionados. Se você olhar para cima o construtorFoo
,Bar
eObject
sãoFunction constructor
.O
prototype
deBar
éFoo
,prototype
deFoo
éObject
e se você observar de perto oprototype
deFoo
está relacionadoObject.prototype
.Antes de encerrarmos, vamos resumir com um pequeno pedaço de código aqui para resumir tudo acima . Estamos usando o
instanceof
operador aqui para verificar se umobject
possui em suaprototype
cadeia aprototype
propriedade de umconstructor
que, abaixo, resume todo o grande diagrama.Espero que este complemento tenha alguma informação, eu sei que isso pode ser muito difícil de entender ... em palavras simples , são apenas objetos vinculados a objetos !!!!
fonte
A interface para classes padrão se torna extensível. Por exemplo, você está usando a
Array
classe e também precisa adicionar um serializador personalizado para todos os seus objetos de matriz. Você gastaria tempo codificando uma subclasse, ou usaria composição ou ... A propriedade prototype resolve isso permitindo que os usuários controlem o conjunto exato de membros / métodos disponíveis para uma classe.Pense nos protótipos como um ponteiro vtable extra. Quando alguns membros estão ausentes da classe original, o protótipo é pesquisado em tempo de execução.
fonte
Pode ajudar a categorizar cadeias de protótipos em duas categorias.
Considere o construtor:
O valor de
Object.getPrototypeOf(Person)
é uma função. De fato éFunction.prototype
. ComoPerson
foi criado como uma função, ele compartilha o mesmo objeto de função de protótipo que todas as funções possuem. É o mesmo quePerson.__proto__
, mas essa propriedade não deve ser usada. De qualquer forma,Object.getPrototypeOf(Person)
você efetivamente sobe a escada do que é chamado de cadeia de protótipos.A corrente na direção ascendente é assim:
Person
→Function.prototype
→Object.prototype
(ponto final)Importante é que essa cadeia de protótipos tem pouco a ver com os objetos que
Person
podem ser construídos . Esses objetos construídos têm sua própria cadeia de protótipos, e essa cadeia pode não ter um ancestral próximo em comum com o mencionado acima.Tomemos, por exemplo, este objeto:
p não possui relação direta de protótipo-cadeia com Person . O relacionamento deles é diferente. O objeto p possui sua própria cadeia de protótipos. Usando
Object.getPrototypeOf
, você encontrará a cadeia da seguinte forma:p
→Person.prototype
→Object.prototype
(ponto final)Não há objeto de função nessa cadeia (embora possa ser).
Então
Person
parece relacionado a dois tipos de correntes, que vivem suas próprias vidas. Para "pular" de uma cadeia para outra, use:.prototype
: pula da cadeia do construtor para a cadeia do objeto criado. Essa propriedade é, portanto, definida apenas para objetos de função (comonew
só pode ser usada em funções)..constructor
: pula da cadeia do objeto criado para a cadeia do construtor.Aqui está uma apresentação visual das duas cadeias de protótipos envolvidas, representadas como colunas:
Para resumir:
Não é surpresa que o nome da propriedade
prototype
possa causar confusão. Talvez fosse mais claro se essa propriedade tivesse sido nomeadaprototypeOfConstructedInstances
ou algo assim.Você pode ir e voltar entre as duas cadeias de protótipos:
Essa simetria pode ser quebrada atribuindo explicitamente um objeto diferente à
prototype
propriedade (mais sobre isso posteriormente).Crie uma função, obtenha dois objetos
Person.prototype
é um objeto que foi criado ao mesmo tempo em que a funçãoPerson
foi criada. TemPerson
como construtor, mesmo que esse construtor ainda não tenha sido executado. Portanto, dois objetos são criados ao mesmo tempo:Person
própria funçãoAmbos são objetos, mas têm papéis diferentes: o objeto de função é construído , enquanto o outro objeto representa o protótipo de qualquer objeto que a função irá construir. O objeto protótipo se tornará o pai do objeto construído em sua cadeia de protótipos.
Como uma função também é um objeto, ela também tem seu próprio pai em sua própria cadeia de protótipos, mas lembre-se de que essas duas cadeias são sobre coisas diferentes.
Aqui estão algumas igualdades que podem ajudar a entender o problema - todas elas impressas
true
:Adicionando níveis à cadeia de protótipos
Embora um objeto protótipo seja criado quando você cria uma função de construtor, você pode ignorar esse objeto e atribuir outro objeto que deve ser usado como protótipo para quaisquer instâncias subseqüentes criadas por esse construtor.
Por exemplo:
Agora, a cadeia de protótipos de t é um passo mais longa que a de p :
t
→p
→Person.prototype
→Object.prototype
(ponto final)A outra cadeia de protótipos não é mais:
Thief
ePerson
os irmãos compartilham o mesmo pai em sua cadeia de protótipos:Person
}Thief
} →Function.prototype
→Object.prototype
(ponto final)O gráfico apresentado anteriormente pode ser estendido para isso (o original
Thief.prototype
é deixado de fora):As linhas azuis representam cadeias de protótipos, as outras linhas coloridas representam outros relacionamentos:
fonte
O Guia Definitivo para JavaScript Orientado a Objetos - uma explicação em vídeo muito concisa e clara de ~ 30min da pergunta feita (o tópico Herança prototípica começa em 5:45 , embora eu prefira ouvir o vídeo inteiro). O autor deste vídeo também criou o site visualizador de objetos JavaScript http://www.objectplayground.com/ .
fonte
Achei útil explicar a "cadeia de protótipos" como convenção recursiva quando
obj_n.prop_X
está sendo referenciada:se
obj_n.prop_X
não existir, verifiqueobj_n+1.prop_X
ondeobj_n+1 = obj_n.[[prototype]]
Se
prop_X
finalmente for encontrado no objeto protótipo k-ésimoobj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
Você pode encontrar um gráfico da relação dos objetos Javascript por suas propriedades aqui:
http://jsobjects.org
fonte
Quando um construtor cria um objeto, esse objeto faz referência implícita à propriedade "prototype" do construtor com a finalidade de resolver as referências de propriedade. A propriedade “prototype” do construtor pode ser referenciada pela expressão do programa constructor.prototype, e as propriedades adicionadas ao protótipo de um objeto são compartilhadas, por herança, por todos os objetos que compartilham o protótipo.
fonte
Há duas entidades distintas, mas relacionadas, que precisam ser explicadas:
.prototype
propriedade das funções.[[Prototype]]
[1] de todos os objetos [2] .Estas são duas coisas diferentes.
A
[[Prototype]]
propriedade:Esta é uma propriedade que existe em todos os [2] objetos.
O que é armazenado aqui é outro objeto que, como um objeto em si, possui um objeto
[[Prototype]]
próprio que aponta para outro objeto. Esse outro objeto tem um[[Prototype]]
próprio. Esta história continua até você alcançar o objeto prototípico que fornece métodos acessíveis em todos os objetos (como.toString
).A
[[Prototype]]
propriedade faz parte do que forma a[[Prototype]]
cadeia. Essa cadeia de[[Prototype]]
objetos é o que é examinado quando, por exemplo,[[Get]]
ou[[Set]]
operações são executadas em um objeto:A
.prototype
propriedade:Esta é uma propriedade encontrada apenas em funções. Usando uma função muito simples:
A
.prototype
propriedade contém um objeto que será atribuídob.[[Prototype]]
quando você o fizervar b = new Bar
. Você pode facilmente examinar isso:Um dos
.prototype
s mais importantes é o daObject
função . Este protótipo contém o objeto prototípico que todas as[[Prototype]]
cadeias contêm. Nele, todos os métodos disponíveis para novos objetos são definidos:Agora, como
.prototype
é um objeto, ele tem uma[[Prototype]]
propriedade. Quando você não fazer quaisquer atribuições paraFunction.prototype
o.prototype
's[[Prototype]]
pontos ao objeto protótipo (Object.prototype
). Isso é realizado automaticamente sempre que você cria uma nova função.Dessa forma, sempre que você fizer
new Bar;
a cadeia de protótipos, você terá tudo definidoBar.prototype
e definidoObject.prototype
:Quando você fazer atribuições fazer para
Function.prototype
tudo o que você está fazendo é estender a cadeia de protótipos para incluir outro objeto. É como uma inserção em uma lista vinculada individualmente.Isso basicamente altera a
[[Prototype]]
cadeia, permitindo que as propriedades definidas no objeto atribuídoFunction.prototype
sejam vistas por qualquer objeto criado pela função.[1: Isso não vai confundir ninguém; disponibilizado através da
__proto__
propriedade em muitas implementações.[2]: Todos, exceto
null
.fonte
Deixe-me contar minha compreensão dos protótipos. Não vou comparar a herança aqui com outras línguas. Eu gostaria que as pessoas parassem de comparar idiomas e apenas entendessem o idioma como ele mesmo. Entender os protótipos e a herança prototípica é tão simples, como mostrarei a seguir.
O protótipo é como um modelo, com base no qual você cria um produto. O ponto crucial a entender é que, quando você cria um objeto usando outro objeto como protótipo, o vínculo entre o protótipo e o produto é permanente. Por exemplo:
Todo objeto contém uma propriedade interna chamada [[protótipo]], que pode ser acessada pela
Object.getPrototypeOf()
função.Object.create(model)
cria um novo objeto e define sua propriedade [[prototype]] para o modelo de objeto . Portanto, quando o fizerObject.getPrototypeOf(product)
, você obterá o modelo de objeto .As propriedades do produto são tratadas da seguinte maneira:
Essa vinculação de objetos usando a propriedade prototype é chamada de herança prototípica. Lá, é tão simples, concorda?
fonte
Outra tentativa de explicar a herança baseada em protótipo JavaScript com melhores imagens
fonte
Considere o seguinte
keyValueStore
objeto:Eu posso criar uma nova instância deste objeto fazendo o seguinte:
Cada instância deste objeto teria as seguintes propriedades públicas:
data
get
set
delete
getLength
Agora, suponha que criamos 100 instâncias desse
keyValueStore
objeto. Mesmo queget
,set
,delete
,getLength
vai fazer exatamente a mesma coisa para cada um desses 100 casos, cada instância tem a sua própria cópia desta função.Agora, imagine se você pudesse ter apenas um único
get
,set
,delete
egetLength
cópia, e cada instância faria referência essa mesma função. Isso seria melhor para o desempenho e exigiria menos memória.É aí que entram os protótipos. Um protótipo é um "blueprint" de propriedades que são herdadas, mas não copiadas por instâncias. Portanto, isso significa que ele existe apenas uma vez na memória para todas as instâncias de um objeto e é compartilhado por todas essas instâncias.
Agora, considere o
keyValueStore
objeto novamente. Eu poderia reescrevê-lo assim:Isso é EXATAMENTE o mesmo que a versão anterior do
keyValueStore
objeto, exceto que todos os seus métodos agora estão em um protótipo. O que isso significa é que todas as 100 instâncias agora compartilham esses quatro métodos, em vez de cada um ter sua própria cópia.fonte
Resumo:
new
palavra-chave, o objeto recebe protótipo. Uma referência a este protótipo pode ser encontrada na__proto__
propriedade do objeto recém-criado.__proto__
propriedade refere-se àprototype
propriedade da função construtora.Exemplo:
Por que isso é útil:
Javascript tem um mecanismo ao procurar propriedades em objetos que é chamado de 'herança prototípica' , aqui está o que basicamente faz:
Por exemplo:
Atualizar:
A
__proto__
propriedade foi descontinuada, embora seja implementada na maioria dos navegadores modernos, uma maneira melhor de obter a referência do objeto prototype seria:Object.getPrototypeOf()
fonte
Eu sempre gosto de analogias quando se trata de entender esse tipo de coisa. 'Herança prototípica' é bastante confusa em comparação com a herança de graves de classe na minha opinião, mesmo que os protótipos sejam um paradigma muito mais simples. De fato, com os protótipos, realmente não há herança; portanto, o nome em si é enganoso, é mais um tipo de 'delegação'.
Imagina isto ....
Você está no ensino médio, está na sala de aula e tem um questionário que deve ser entregue hoje, mas não tem uma caneta para preencher suas respostas. Doh!
Você está sentado ao lado de seu amigo Finnius, que pode ter uma caneta. Você pergunta, e ele olha em volta da mesa, sem sucesso, mas em vez de dizer "eu não tenho uma caneta", ele é um bom amigo. Derp de fato tem uma caneta sobressalente e a devolve a Finnius, que a entrega a você para concluir seu teste. Derp confiou a caneta a Finnius, que delegou a caneta a você para uso.
O importante aqui é que Derp não lhe dá a caneta, pois você não tem um relacionamento direto com ele.
Este é um exemplo simplificado de como os protótipos funcionam, onde uma árvore de dados é pesquisada para o que você está procurando.
fonte
outro esquema mostrando __proto__ , relações de protótipo e construtor :
fonte
É que você já possui um objeto,
Object.new
mas ainda não o possui ao usar a sintaxe do construtor.fonte
Referência: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
fonte
O Prototype cria um novo objeto ao clonar o objeto existente . Então, quando pensamos em protótipo, podemos pensar em clonar ou fazer uma cópia de algo em vez de inventar.
fonte